如何使用HTML5 Canvas在同一行中为不同的单词着色?

时间:2017-05-10 23:31:11

标签: javascript canvas html5-canvas

考虑这个循环遍历对象中的键值对:

Object.keys(categories).forEach(categoryKey => {
  ctx.fillText(`${categoryKey}: ${categories[categoryKey]}`, 40, (30 * i) + 160);
  ctx.fillStyle = '#fff';
  i++;
});

所有文字都是白色的,但我想要的是:${categoryKey}的文字为红色。

因为它在同一个字符串中运行,所以我不确定如何在不破坏的情况下执行此操作,因为我希望它在同一个ctx.fillText()调用中。

2 个答案:

答案 0 :(得分:2)

我需要多次为画布渲染设置文本格式(特别是对于数学示例),所以下面是我使用嵌套格式指南修改渲染文本的函数的修改。

演示

来自我自己的库的修改后的副本,它根据嵌套的样式规则格式化文本。

simpleTextStyler首先获取字体中每个字符的大小(使用simpleTextStyler.setFont()设置当前上下文字体或不起作用),然后查看字符串中的每个字符输出具有相同样式的字符组,并一次渲染一个。

它使用一个堆栈来保存当前样式,如果找到一个新样式,将一个样式压入堆栈" {"并在每次结束时将样式弹出堆栈"}"

如果" \ n"它将添加一个新行。找到了。如果" \ t"请转到下一个制表位。并在"{""{"中使用variouse嵌套样式,其中"{"后面的第一个字符表示函数。

  • s 子脚本
  • S 超级脚本
  • + 更大的文字
  • - 较小的文字
  • 颜色必须后跟6个字符十六进制颜色,例如红色{#FF0000这个文字是红色}

文本样式可以嵌套,例如"text{Ssuper{ssub{Ssuper}}{Ssuper super}}"是文本超级 Sub 超级 超级超级 < / p>

该演示呈现字符串"Testing\nnewline\n\tTab\n\t\tTab\n\t\t\tTab\nSub{sScript} Super{SScript} Size {+Big {+Bigger}} Normal {-Small {-Smaller}}\nAnd now colours \n{#FF0000Red} {#00FF00Green} {#0000FFBlue}"

根据您的需要

simpleTextStyler.setFont(); // only needs to be set for the font family
                            // sizing text is in the last argument of the next call

simpleTextStyler.drawText(ctx,
    `{#FF0000${categoryKey}}: ${categories[categoryKey]}`, 40, (30 * i) + 160,fontSize);

&#13;
&#13;
var ctx = canvas.getContext("2d");
ctx.font = "18px arial";
setTimeout(drawExamples,0);
function drawExamples(){
  simpleTextStyler.setFont(); // set the current font
  simpleTextStyler.drawText(ctx,
  "Testing\nnewline\n\tTab\n\t\tTab\n\t\t\tTab\nSub{sScript} Super{SScript} Size {+Big {+Bigger}} Normal {-Small {-Smaller}}\nAnd now colours \n{#FF0000Red} {#00FF00Green} {#0000FFBlue}",
      10,20,18)
}
      

const simpleTextStyler = (function(){
    var simpleTextStyler = {
        sizes : [],
        baseSize : undefined,
        font : undefined,
        controlChars : "{}\n\t",
        spaceSize : 0,
        tabSize : 8, // in spaceSize units
        tabs : (function(){var t = [];for(var i=0; i < 100; i += 8){t.push(i);}; return t;})(),
        getNextTab : function(x){
            i = 0;
            while(i < this.tabs.length){
                if(x < this.tabs[i] * this.tabSize * this.spaceSize){
                    return this.tabs[i] * this.tabSize * this.spaceSize;
                }
                i++;
            }
            return this.tabs[i-1] * this.tabSize * this.spaceSize;
        },
            
        getFontSize : function(font){
            var numFind = /[0-9]+/;
            var number = numFind.exec(font)[0];
            if(isNaN(number)){
                throw Error("SimpleTextStyler Cant find font size");
            }
            return Number(number);
            
        },
        setFont : function(font = ctx.font){
            this.font = ctx.font = font;
            this.baseSize = this.getFontSize(font);
            for(var i = 32; i < 256; i ++){
                this.sizes[i-32] = ctx.measureText(String.fromCharCode(i),0,0).width/this.baseSize;
            }
            this.spaceSize = this.sizes[0];

        },
        drawText : function(context,text,x,y,size){
            var i,len,subText;
            var w,scale;
            var xx,yy,ctx;
            var state = [];
            if(text === undefined){ return }
            xx = x;
            yy = y;
            if(!context.setTransform){ // simple test if this is a 2D context
                if(context.ctx){ ctx = context.ctx } // may be a image with attached ctx?
                else{ return }
            }else { ctx = context }

            function renderText(text){
            
                ctx.save();
                ctx.fillStyle = colour;
                ctx.translate(x,y)
                ctx.scale(scale,scale)
                ctx.fillText(text,0,0);
                ctx.restore();
            }
            var colour = ctx.fillStyle;
            ctx.font = this.font;
            len = text.length;
            subText = "";
            w = 0;
            i = 0;
            scale = size / this.baseSize;
            while(i < len){
                var c = text[i];
                var cc = text.charCodeAt(i);
                if(cc < 256){ // only ascii
                    if(this.controlChars.indexOf(c) > -1){
                        if(subText !== ""){
                            scale = size / this.baseSize;
                            renderText(subText);
                            x += w;
                            w = 0;
                            subText = "";                        
                        }
                        if(c === "\n"){  // return move to new line
                            x = xx;
                            y += size;
                        }else
                        if(c === "\t"){ // tab move to next tab
                            x = this.getNextTab(x-xx)+xx;
                        }else
                        if(c === "{"){   // Text format delimiter                       
                            state.push({
                                size : size,
                                colour : colour,
                                x:x,
                                y:y,
                            })
                            i += 1;
                            var t = text[i];
                            if(t === "+"){  // Increase size
                                size *= 1/(3/4);
                            }else if(t === "-"){  // decrease size
                                size *= 3/4;
                            }else if(t === "s"){ // sub script
                                y += size * (1/3);
                                size  *= (2/3);
                            }else if(t === "S"){ // super script
                                y -= size * (1/3);
                                size  *= (2/3);
                            }else if(t === "#"){
                                colour = text.substr(i,7);
                                i+= 6;
                            }
                        }else if(c  === "}"){
                            var s = state.pop();
                            y = s.y;
                            size = s.size;
                            colour = s.colour;
                            scale = size / this.baseSize;
                        }
                    }else{
                        subText += c;
                        w += this.sizes[cc-32] * (size ) ;
                    }
                 }
                 i += 1;
            }
            if(subText !== ""){
                renderText(subText);
            }
           
        },
    }
    return simpleTextStyler;
})();
&#13;
canvas {
    border : 2px solid black;
}
&#13;
<canvas id="canvas" width=512 height=200></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

感谢@ Blindman67建议使用measureText()

这是我最终如何做到的(虽然这个上下文中的一些代码对你来说没有意义,因为它可能是更重要的一部分)。希望你能够看到关键部分:

let i = 0;

Object.keys(categories).forEach(categoryKey => {
  const category = paramKeyToCategoryName(categoryKey);
  const offsetTop = 190;
  const offsetLeft = 40;
  const spacing = 30;

  if (category.length) {
    // Category text.
    ctx.fillStyle = '#f13f3d';
    ctx.fillText(`${category.toUpperCase()}:`, offsetLeft, (spacing * i) + offsetTop);

    // Measure category text length.
    const measure = ctx.measureText(category.toUpperCase());
    const measureWidth = measure.width + 12;

    // Selected artist text.
    ctx.fillStyle = '#fff';
    ctx.fillText(`${categories[categoryKey].toUpperCase()}`, (offsetLeft + measureWidth), (spacing * i) + offsetTop);

    i++;
  }
});

希望这有助于某人。