用alpha通道绘制重叠的圆圈

时间:2013-07-12 18:51:10

标签: html5 algorithm canvas geometry

这个问题在这里得到了回答:Combined area of overlapping circles

我的问题更具体。我在其他任意大小的圆圈内有任意数量的任意大小的圆圈,以制作类似目标的图像:

enter image description here

此图片必须具有一定的透明度。整个形状的透明度必须相同。然后,有任意数量的这些形状可以重叠,它需要看起来像这样:

enter image description here

透明度必须保持不变,无论它们重叠多少。

我唯一能想到的就是遍历画布上的每个像素并根据距离每个圆心的距离来计算像素应该是什么颜色,但这需要太长时间。我希望圆圈也可以拖动,所以这需要非常快。有一个更好的方法吗? (抱歉我的GIMP技能很差)

2 个答案:

答案 0 :(得分:2)

可以在不使用像素操作或任何库的情况下进行本地操作。

如果所有圆圈的透明度相同,则非常简单。

解决方案

snapshot
在一些随机背景之上的圈子

您需要做的是:

  • 分配一个离屏画布,其中圆圈绘制为实体(无透明度)
  • 分三步绘制圆圈。
  • 首先是所有圆圈的红色表面,然后是所有圆圈的蓝色表面,依此类推。
  • 为主(可见)画布
  • 设置透明度的全局Alpha
  • 清除两幅画布
  • 将离屏画布绘制到主画布

您的圈子功能可能如下所示:

function drawCircle(x, y, r, step) {

    ctx.beginPath();

    switch (step) {
        case 0:  // step 0, outer circle red
            ctx.fillStyle = '#f00';
            break;

        case 1:  // step 1, middle circle blue
            ctx.fillStyle = '#00f';
            r *= 0.67;
            break;

        case 2:  // step 2, inner circle green
            ctx.fillStyle = '#0f0';
            r *= 0.33;
            break;
    }
    ctx.arc(x, y, r, 0, 2 * Math.PI);
    ctx.fill();
}

该函数采用xy中心点以及radius。但另外它在0到2之间取step值,它决定了绘制哪个表面。这在接下来的步骤中非常重要。

首先,我们可以定义一个包含我们想要绘制的所有圆的数组:

var circs = [
    //x   y    r   dx dy (the last two for animation only)
    [100, 100, 50, 2, 1],
    [200, 200, 50, -2, -3],
    [150, 50, 50, 3, -1]
];

从这里你可以拖动它们,偏移x和y然后重绘它们,但为了演示,我会为它们设置动画。

在我们绘制之前,我们在主画布上设置全局alpha(屏幕外保持稳定):

mctx.globalAlpha = 0.7;  // main canvas

动画循环:

function start() {

    // clear off-screen canvas
    ctx.clearRect(0,0, w, h);

    // clear main canvas
    mctx.clearRect(0,0, w, h);

    var t = 0, i, c;

    // outer step loop
    for(; t < 3; t++) {

        // draw all circles at current step
        for(i = 0; c = circs[i]; i++) {
            drawCircle(c[0], c[1], c[2], t);
        }
    }

    // re-position circles for animation
    for(i = 0;c = circs[i]; i++) {
        c[0] += c[3];  /// add delta to x
        c[1] += c[4];  /// add delta to y

        // reverse deltas if at boundaries
        if (c[0] < 0 || c[0] > w) c[3] = -c[3];
        if (c[1] < 0 || c[1] > h) c[4] = -c[4];
    }

    // draw off-screen to main canvas        
    mctx.drawImage(ocanvas, 0, 0);

    // loop animation
    requestAnimationFrame(start);
}

如果要将其他元素绘制到画布上,可以为每个操作重置全局alpha - 或者使用第二个屏幕画布来保存静态内容。

演示

var demo = document.getElementById("demo");
var w = demo.width, h = demo.height;

var ocanvas = document.createElement('canvas');
ocanvas.width = w;
ocanvas.height = h;

var ctx = ocanvas.getContext('2d');
var mctx = demo.getContext('2d');
var img = document.createElement('img')
img.onload = start;
img.src = 'http://i.imgur.com/CHPdL2y.png';

/// key to it all
mctx.globalAlpha = 0.7;

var circs = [
    //x   y    r   dx   dy
    [100, 100, 50,  2  ,  1.5],
    [200, 200, 70, -2  , -3],
    [150,  50, 50,  3  , -1],
    [150,  50, 30,  4  ,  4],
    [150,  50, 20, -3  , -2],
    [100, 100, 55,  2.5,  2.5],
    [200, 200, 75, -1  , -2.5],
    [150,  50, 45,  3.5, -2],
    [150,  50, 35,  5  ,  2],
    [150,  50, 25, -1.2, -5]
];

function drawCircle(x, y, r, step) {

    ctx.beginPath();

    switch (step) {
        case 0:
            ctx.fillStyle = '#f00';
            break;
        case 1:
            ctx.fillStyle = '#00f';
            r *= 0.67;
            break;
        case 2:
            ctx.fillStyle = '#0f0';
            r *= 0.33;
            break;
    }
    ctx.arc(x, y, r, 0, 2 * Math.PI);
    ctx.fill();
}

function start() {
    
    ctx.clearRect(0, 0, w, h);
    mctx.clearRect(0, 0, w, h);

    var i = 0, t, c;
    for(t = 0; t < 3; t++) {
        for(i = 0; c = circs[i]; i++) {
            drawCircle(c[0], c[1], c[2], t);
        }
    }

    for(i = 0;c = circs[i]; i++) {
        c[0] += c[3];
        c[1] += c[4];
        if (c[0] < 0 || c[0] > w) c[3] = -c[3];
        if (c[1] < 0 || c[1] > h) c[4] = -c[4];
    }
    
    mctx.drawImage(ocanvas, 0, 0);
    requestAnimationFrame(start);
}
body {
    margin:0;
    background:url(//i.stack.imgur.com/b8eCZ.jpg) no-repeat;
}
<canvas id="demo" width="500" height="333"></canvas>

答案 1 :(得分:1)

您必须使用像RaphaelJS这样的库。它的功能超出了你的要求。

编辑:您可以通过编程方式确定中心之间的距离(D)并将其与半径之和(S)进行比较,从而通过js应用Alpha透明度。如果D < S,然后它们重叠,以便处理红色圆圈。

http://raphaeljs.com/