绘制相同颜色的公共区域的碰撞透明圆圈

时间:2016-09-24 11:59:24

标签: javascript canvas

当我使用每个圆圈的fill()逐个绘制相同颜色的圆圈时。当它们重叠时,常见的是颜色较暗(饱和)。 参见示例1(左)in JS Bin

如果我创建长路径,然后对这个公共路径使用fill(),它有奇怪的工件(是的,当然它被复杂的路径弄糊涂,不知道我想要绘制的是什么) 参见示例2(右)in JS Bin

如何实现具有相同颜色的圆形公共区域未饱和,它应该不会比具有相同颜色的其他圆圈更暗。 (正是我有in JS Bin右侧,但没有疯狂的文物)

如果具有不同颜色的圆圈具有公共区域,则颜色应该是饱和的。

2 个答案:

答案 0 :(得分:1)

enter image description here

关于您的.getImageData解决方案......

使用合成而不是.getImageData来混合颜色会更快。

这是一个组合重叠的半透明圆圈而不会使重叠变暗的功能。

  • 在第二个画布上以不透明的颜色绘制所有相同颜色的圆圈。
  • 设置context.globalCompositeOperation='source-in'会导致新图形替换现有像素。
  • 使用所需的半透明颜色填充第二个画布,用于这组不同颜色的圆圈。

结果是一组没有变暗效果的重叠圆圈。

function uniformColorCircles(circles){
    var PI2=Math.PI*2;
    tempctx.globalCompositeOperation='source-over';
    tempctx.clearRect(0,0,cw,ch);
    tempctx.beginPath();
    for(var i=0;i<circles.length;i++){
        var c=circles[i];
        tempctx.arc(c.x,c.y,c.radius,0,PI2);
    }
    tempctx.fillStyle='black';
    tempctx.fill();
    tempctx.globalCompositeOperation='source-in';
    tempctx.fillStyle=circles[0].rgba;
    tempctx.fill();
}

以下是示例代码和涉及多组半透明圆圈的演示:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

ctx.fillRect(0,0,120,220);

var tempCanvas=canvas.cloneNode();
var tempctx=tempCanvas.getContext('2d');

var c1={x:100,y:200,radius:50,rgba:'rgba(255,0,0,0.5)'};
var c2={x:100,y:240,radius:35,rgba:'rgba(255,0,0,0.5)'};
var c3={x:140,y:200,radius:50,rgba:'rgba(0,255,255,0.5)'};
var c4={x:140,y:240,radius:35,rgba:'rgba(0,255,255,0.5)'};
var c5={x:120,y:140,radius:50,rgba:'rgba(255,255,0,0.5)'};
uniformColorCircles([c1,c2]);
ctx.drawImage(tempCanvas,0,0);
uniformColorCircles([c3,c4]);
ctx.drawImage(tempCanvas,0,0);
uniformColorCircles([c5]);
ctx.drawImage(tempCanvas,0,0);


function uniformColorCircles(circles){
    var PI2=Math.PI*2;
    tempctx.globalCompositeOperation='source-over';
    tempctx.clearRect(0,0,cw,ch);
    tempctx.beginPath();
    for(var i=0;i<circles.length;i++){
        var c=circles[i];
        tempctx.arc(c.x,c.y,c.radius,0,PI2);
    }
    tempctx.fillStyle='black';
    tempctx.fill();
    tempctx.globalCompositeOperation='source-in';
    tempctx.fillStyle=circles[0].rgba;
    tempctx.fill();
}
body{ background-color:white; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=512 height=512></canvas>

答案 1 :(得分:0)

我使用辅助画布和画布图像数据处理解决了这个问题。

有数据数组,其中包含坐标和值,用于确定我们需要使用哪种颜色。

我在其自己的图层中绘制每个圆圈颜色,然后使用preventSaturation函数处理每个图层。然后将所有图层添加到原始画布中。

如果有人知道更好的方式让我知道。

如果有人不理解我需要做的事情,那就是: 1)我有this 2)我试图this

var canvas = document.getElementById('circles');
var ctx = canvas.getContext('2d');

var radius = 30;
var opacity = .7;

var data = [
    {
        x: 200,
        y: 200,
        v: 10
    },
    {
        x: 230,
        y: 230,
        v: 20
    },
    {
        x: 250,
        y: 210,
        v: 30
    },
    {
        x: 270,
        y: 190,
        v: 40
    },
    {
        x: 300,
        y: 220,
        v: 100
    },
    {
        x: 300,
        y: 260,
        v: 200
    },
    {
        x: 320,
        y: 210,
        v: 300
    },
    {
        x: 300,
        y: 160,
        v: 200
    },
    {
        x: 380,
        y: 160,
        v: 3000
    },
    {
        x: 380,
        y: 110,
        v: 3000
    },
    {
        x: 320,
        y: 190,
        v: 3000
    }
];

var styles = {
    blue: {
        edgeValue: 0,
        color: [0, 0, 255, 0.7]
    },
    green: {
        edgeValue: 100,
        color: [0, 255, 0, 0.7]
    },
    red: {
        edgeValue: 1000,
        color: [255, 0, 0, 0.7]
    }
};

var layers = {};

for (var prop in styles) {
    if(styles.hasOwnProperty(prop)) {

        var c = document.createElement('canvas');
        c.width = canvas.width;
        c.height = canvas.height;

        var cx = c.getContext('2d');

        var cc = document.createElement('canvas');
        cc.width = radius * 2;
        cc.height = radius * 2;

        var ccx = cc.getContext('2d');

        var cColor = styles[prop].color;

        ccx.fillStyle = 'rgba(' + cColor[0] + ',' + cColor[1] + ',' + cColor[2] + ',' + cColor[3] + ')';

        ccx.beginPath();
        ccx.arc(radius, radius, radius, 0, Math.PI * 2, true);
        ccx.closePath();
        ccx.fill();

        layers[prop] = {
            color: styles[prop].color,
            edgeValue: styles[prop].edgeValue,
            canvas: c,
            ctx: cx,
            canvasC: cc,
            ctxC: ccx,
            objects: []
        };

    }
}

data.forEach(function(o) {
    var layer = o.v < styles.green.edgeValue ? layers.blue : o.v < styles.red.edgeValue ? layers.green : layers.red;
    layer.ctx.drawImage(layer.canvasC, o.x, o.y);
    layer.objects.push(o);
});

for(prop in layers) {
    if(layers.hasOwnProperty(prop)) {
        var image = layers[prop].ctx
                .getImageData(0, 0, layers[prop].canvas.width, layers[prop].canvas.height);
        preventColorSaturation(image.data, layers[prop].color);
        layers[prop].ctx.putImageData(image, 0, 0);

        ctx.drawImage(layers[prop].canvas, 0, 0);
    }
}

function preventSaturation (d, s) {
    var rgb255 = raRGBA255(s);

    for (var i = 0; i < d.length; i += 4) {
        d[i] = Math.min(d[i], rgb255[0]);
        d[i + 1] = Math.min(d[i + 1], rgb255[1]);
        d[i + 2] = Math.min(d[i + 2], rgb255[2]);
        d[i + 3] = Math.min(d[i + 3], rgb255[3]);
    }
}

function raRGBA255 (s) {

    return [
        s[0],
        s[1],
        s[2],
        255 * s[3]
    ];
}

function raHexToRGB (s) {
    var hexREGEXP = /^#([0-9A-Za-z]{3,6})$/;
    var parsedHEX = s.match(hexREGEXP);

    if (!parsedHEX) {
        return [0, 0, 0];
    }

    return [
        parseInt(parsedHEX[1].slice(0, 2), 16),
        parseInt(parsedHEX[1].slice(2, 4), 16),
        parseInt(parsedHEX[1].slice(4), 16)
    ];
}