简体   繁体   English

如何在画布的fillText方法中解析html标记

[英]how to parse html markup in fillText method of canvas

I have a canvas element which has labels. 我有一个带有标签的canvas元素。 And each label is created using the fillText method. 每个标签都是使用fillText方法创建的。

I want to be able to send text like this: "CH 2 ", but I don't get this as the final result. 我希望能够发送这样的文本:“ CH 2 ”,但我没有得到最终结果。 The <sub> element doesn't get parsed properly. <sub>元素未正确解析。 How can I solve this issue? 我该如何解决这个问题?

Here is some example code: 这是一些示例代码:

  var ctx = document.getElementById('canvas').getContext('2d'); ctx.font = "48px serif"; ctx.fillText("Hello <sub>world</sub>", 10, 50); 
 <canvas id="canvas" width=500 height=500 ><canvas> 

You can get around this using as @lipp mentions in comments Simon's solution and insert already sub-scripted characters into the string. 您可以使用@lipp在Simon的解决方案中的注释中解决此问题 ,并将已经下标的字符插入字符串中。

You can also make a simple parser that detects some code and renders next segment differently (see below). 您还可以创建一个简单的解析器,该解析器检测一些代码并以不同的方式呈现下一个片段(请参见下文)。

There is also the possibility to use SVG to use HTML for drawing on canvas , but it has its backdraws such as async behavior, limited content (before security mechanisms prevent it from being drawn to a canvas) and lacking cross-browser support in some cases. 也有可能使用SVG在画布上使用HTML进行绘制 ,但是它具有诸如异步行为,内容有限(在安全机制阻止其被绘制到画布上之前)之类的缺点,并且在某些情况下缺乏跨浏览器支持。

An example parser 解析器示例

This is just a start example. 这只是一个开始的例子。 You can chose any code as well as adding new codes etc. This is just one way, there are many others... 您可以选择任何代码以及添加新代码等。这只是一种方式,还有许多其他方式...

If you have a HTML source string simply replace those tags with a code, or extend the parser to handle those as well. 如果您有HTML源字符串,只需用代码替换这些标记,或扩展解析器以同样处理这些标记。

 var ctx = c.getContext("2d"), fontSize = 28, str = "This string has codes to enable |subscripted| text."; setFontSize(fontSize); // parse string for(var i = 0, x = 10, tx = 0, isSub = false; i < str.length; i++) { // iterate over chars if (str[i] === "|") { // special code? ctx.fillText(str.substring(tx, i), x, 50 + (isSub ? 7 : 0)); // draw current text seg x += ctx.measureText(str.substring(tx, i)).width; // add width to x tx = ++i; // update start pointer isSub = !isSub; // toggle subscript mode setFontSize(isSub ? fontSize * 0.5 : fontSize); // set font size } } ctx.fillText(str.substring(tx, i), x, 50); // draw last text part function setFontSize(sz) {ctx.font = sz + "px sans-serif"} 
 <canvas id=c width=600></canvas> 

Here is a function that converts an html string to a series of fillText statements. 这是一个将html字符串转换为一系列fillText语句的函数。 It handles multiline strings and lets you specify alignment (left, right, center). 它处理多行字符串,并允许您指定对齐方式(左,右,中心)。

<canvas id="textCanvas" width="700" height="150" style="border:1px solid #d3d3d3;">

<script>

function parse_html(ctx, s, x0, y0, align, font, fontsize, col) {
    // 2d canvas context, string, pos.x, pos.y, left/right/center, font, font height, color
    // Convert html code to a series of individual strings, each displayable by fillText().
    font = 'px '+font
    var lines = []
    var line = [0]
    var part = '' // the text element preceding a '<'
    var cmd = ''
    var bold = false
    var italic = false
    var sup = false
    var sub = false
    var x = 0, y = 0
    var dx, start
    var legal = ['b', 'strong', 'i', 'em', 'sup', 'sub']

    function add_part() {
        var style = ''
        var fs = fontsize
        if (bold) style += 'bold '
        if (italic) style += 'italic '
        if (sup || sub) {
            fs = 0.8*fontsize
            if (sup) y -= 0.3*fontsize // y increases downward in 2D canvas
            else y += 0.3*fontsize
        }
        ctx.font = style+fs+font
        dx = ctx.measureText(part).width
        line.push([x, y, ctx.font, part])
        part = ''
        x += dx
    }

    function end_line() {
        if (part !== '') add_part()
        line[0] = x
        lines.push(line)
        line = [0]
        x = y = 0
    }

    for (var i=0; i<s.length; i++) {
        var c = s[i]
        if (c == '\n') {
            end_line()
        } else if (c != '<') {
            part += c // a part of the text
        } else { // encountered '<'
            //if (part !== '') add_part()
            start = i+1
            i++
            cmd = s[i]
            var end = false
            if (cmd == '/') {
                cmd = ''
                end = true
            }
            var ok = true
            for (i=i+1; i<s.length; i++) {
                if (s[i] == '<') { // This means that the intial '<' did not start a command
                    i = i-1 // back up
                    part += '<'+cmd
                    add_part()
                    ok = false // signal that we encountered '<'
                    break
                }
                if (s[i] == '>') break
                cmd += s[i]
            }
            if (!ok) continue
            if (cmd == 'br' || cmd == 'br/') {
                end_line()
            } else {
                if (legal.indexOf(cmd) >= 0 && part !== '') add_part()
                switch (cmd) {
                    case 'b':
                    case 'strong':
                        bold = !end
                        break
                    case 'i':
                    case 'em':
                        italic = !end
                        break
                    case 'sup':
                        sup = !end
                        if (end) y = 0
                        break
                    case 'sub':
                        sub = !end
                        if (end) y = 0
                        break
                    default:
                        part += '<'+cmd+'>'
                }
            }
        }
    }
    if (part.length > 0) line.push([x, y, fontsize+font, part])
    ctx.font = fontsize+font
    line[0] = x + ctx.measureText(part).width
    lines.push(line)

    function rgb_to_html(rgb) { // convert RGB 0-1 to html 0-255
        var r = Math.floor(255 * rgb[0])
        var g = Math.floor(255 * rgb[1])
        var b = Math.floor(255 * rgb[2])
        return 'rgb(' + r + ',' + g + ',' + b + ')'
    }

    var width, L
    var nline = 0
    // Each line in lines starts with the total width of the line, followed by
    // elements of the form {x, y, font, text}, where x and y start at zero.
    var maxwidth = -1
    for (L in lines) {
        if (lines[L][0] > maxwidth) maxwidth = lines[L][0]
    }
    for (L in lines) {
        y0 += nline*1.2*fontsize
        nline++
        for (var p in lines[L]) {
            var k = lines[L][p]
            if (k[1] === undefined) {
                width = k
                continue
            }
            ctx.font = k[2]
            ctx.fillStyle = rgb_to_html(col)
            switch (align) {
                case 'left':
                    x = x0 + k[0]
                    y = y0 + k[1]
                    break
                case 'center':
                    x = x0 + k[0] - width/2
                    y = y0 + k[1]
                    break
                case 'right':
                    x = x0 + k[0] - maxwidth
                    y = y0 + k[1]
                    break
                default:
                    throw new Error(align+' is not a possible alignment option.')
            }
            ctx.fillText(k[3], x, y)
        }
    }
}

var c = document.getElementById("textCanvas")
var ctx = c.getContext("2d")
var s = 'The <b><i>quick</i> fox</b> <i>jumps.</i><br><i>M</i><sub>sys</sub> >= 10<sup>-3</sup> kg'
parse_html(ctx, s, 350, 50, 'center', 'Verdana', 30, [0,0,1])
</script>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM