在HTML5 Canvas中实现符号绘图库的正确方法是什么?

时间:2017-02-18 00:47:45

标签: javascript html5 canvas

我正在将符号绘图库从Java移植到Javascript。符号将被绘制到HTML5 Canvas对象中。符号具有坐标和形状(正方形,圆形,十字形等),大小和颜色的属性。

我已经在Java中使用了这样一个工作符号绘图库多年。在Javascript中,我很难用颜色。符号通常以正确的形状和大小绘制,但并不总是使用正确的颜色。一个符号的颜色也可以“渗透”到其他东西,如图表轴,我正在绘制(通常为黑色)。那么如何将绘制符号作为绘图的“原子”单位,这样就不会对我正在绘制的其他东西产生副作用?我觉得我不理解路径和子路径的概念以及上下文存储的状态。我已经广泛寻找解决这个问题的方法,但还没有发现任何可行的方法。

以下是我的符号库中的一些典型代码:

function plotSymbol(ctx, symbol, h, v, width, symcolor) {
    var i;
    if (width == 0) return;

    if (symbol == Resources.PlotSymbolsEnum.SYMBOL_SQUARE) {
        ctx.strokeStyle = symcolor;
        ctx.strokeRect(h - width / 2.0, v - width / 2.0, width, width);
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_SQUAREFILLED) {
        ctx.fillStyle = symcolor;
        ctx.fillRect(h - width / 2.0, v - width / 2.0, width, width);
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_CIRCLE) {
        var x = h - width / 2;
        var y = v - width / 2;
        drawCircle(ctx, x, y, width/2, 1, symcolor, false);
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_CIRCLEFILLED) {
        var x = h - width / 2;
        var y = v - width / 2;
        drawCircle(ctx, x, y, width/2, 1, symcolor, true);
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_DIAMOND) {
        var xpoints1 = [ h, h + width / 2, h, h - width / 2 ];
        var ypoints1 = [ v - width / 2, v, v + width / 2, v ];

        ctx.strokeStyle = symcolor;
        ctx.beginPath();
        ctx.moveTo(xpoints1[0], ypoints1[0]);
        for (i = 1; i < xpoints1.length; i++) {
            ctx.lineTo(xpoints1[i], ypoints1[i]);
        }
//        ctx.closePath();
        ctx.stroke();
        ctx.strokeStyle = 'black';
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_DIAMONDFILLED) {
        var xpoints2 = [ h, h + width / 2, h, h - width / 2 ];
        var ypoints2 = [ v - width / 2, v, v + width / 2, v ];

        ctx.fillStyle = symcolor;
        ctx.beginPath();
        ctx.moveTo(xpoints2[0], ypoints2[0]);
        for (i = 1; i < xpoints2.length; i++) {
            ctx.lineTo(xpoints2[i], ypoints2[i]);
        }

//        ctx.closePath();
        ctx.fill();
        ctx.stroke();
        ctx.fillStyle = 'white';
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_TRIANGLE) {
        var xpoints3 = [ h - width / 2, h + width / 2, h ];
        var ypoints3 = [ v + width / 2, v + width / 2, v - width / 2 ];

        ctx.strokeStyle = symcolor;
        ctx.beginPath();
        ctx.moveTo(xpoints3[0], ypoints3[0]);
        for (i = 1; i < xpoints3.length; i++) {
            ctx.lineTo(xpoints3[i], ypoints3[i]);
        }

//        ctx.closePath();
        ctx.stroke();
        ctx.strokeStyle = 'black';
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_TRIANGLEFILLED) {
        var xpoints3 = [ h - width / 2, h + width / 2, h ];
        var ypoints3 = [ v + width / 2, v + width / 2, v - width / 2 ];

        ctx.strokeStyle = symcolor;
        ctx.beginPath();
        ctx.moveTo(xpoints3[0], ypoints3[0]);
        for (i = 1; i < xpoints3.length; i++) {
            ctx.lineTo(xpoints3[i], ypoints3[i]);
        }

//        ctx.closePath();
        ctx.fill();
        ctx.stroke();
        ctx.strokeStyle = 'black';
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_CROSS1) {
        ctx.strokeStyle = symcolor;
        width += 1;
        ctx.beginPath();
        ctx.moveTo(h - width / 2, v);
        ctx.lineTo(h + width / 2, v);
        ctx.moveTo(h, v - width / 2);
        ctx.lineTo(h, v + width / 2);
//        ctx.closePath();
        ctx.stroke();
        ctx.strokeStyle = 'black';
    }
    else if (symbol == Resources.PlotSymbolsEnum.SYMBOL_CROSS2) {
        ctx.strokeStyle = symcolor;
        ctx.beginPath();
        ctx.moveTo(h - width / 2, v - width / 2);
        ctx.lineTo(h + width / 2, v + width / 2);
        ctx.moveTo(h - width / 2, v + width / 2);
        ctx.lineTo(h + width / 2, v - width / 2);
//        ctx.closePath();
        ctx.stroke();
        ctx.strokeStyle = 'black';
    }
}

我已经注释掉了闭路径,因为我并不总是希望形状返回原点。我想我最初把它们放进去是因为我误解了closePath()的作用,并认为它们会绘制一个独立于其他符号的符号。

以下是它的调用方式:

ctx.save();
ctx.rect(left, y1, width, height);
ctx.clip();
.
.
.
y = this.mMonthlyMeans[i];
y = (y - yOrigin) * yScale;
y = Math.floor(y) + 0.5;
plotSymbol(ctx, theMeanSym, this.getPlotLeft() + xCtr, y, theMeanSymSize, theMeanSymbolColor);
ctx.strokeStyle = 'black';  // this call is perhaps unnecessary in properly written code but seems to head off some problems
.
.
.
ctx.restore() // to restore the clip

我非常感谢在HTML5 Canvas中正确执行此操作的任何建议和指示。它似乎并不像将像素绘制到像Java这样的Graphics2D对象那么简单,

2 个答案:

答案 0 :(得分:0)

您应该在致电strokeStylectx.stroke()之前设置ctx.strokeXXX(),并在fillStylectx.fill()之前设置ctx.fillXXX(),在每种情况下块。

您错过了strokeStyle阻止中的SYMBOL_DIAMONDFILLEDfillStyle阻止中的SYMBOL_TRIANGLEFILLED

答案 1 :(得分:0)

我相信我找到了解决方案。谷歌搜索今天我发现了一个关于绘制到Canvas对象的教程(http://eloquentjavascript.net/16_canvas.html)。作者说:

  

&#34;路径可以包含多个形状 - 每个移动动画开始一个新形状。&#34;

这是寻找的线索。我有使用moveTo和lineTo绘制十字和x符号的方法。这就是我的十字符号代码(从Java移植)看起来很喜欢:

    ctx.strokeStyle = symcolor;
    ctx.beginPath();
    ctx.moveTo(h - width / 2, v);
    ctx.lineTo(h + width / 2, v);
    ctx.moveTo(h, v - width / 2);
    ctx.lineTo(h, v + width / 2);
    ctx.stroke();

错误是内部moveTo启动一个新的子路径,并且stroke命令似乎只渲染strokeStyle颜色中的第一个子路径或使用默认颜色。净效果是大多数符号都以正确的颜色绘制,但(我认为)最后一个符号是以默认颜色绘制的,显示为黑色。

更改代码以便我分别划分各个部分会导致所有符号以正确的颜色绘制:

    ctx.strokeStyle = symcolor;
    ctx.beginPath();
    ctx.moveTo(h - width / 2, v);
    ctx.lineTo(h + width / 2, v);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(h, v - width / 2);
    ctx.lineTo(h, v + width / 2);
    ctx.stroke();