html - 使用 alpha channel 绘制重叠的圆圈

标签 html algorithm canvas geometry

这个问题在这里得到了某种程度的回答:Combined area of overlapping circles

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

enter image description here

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

enter image description here

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

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

最佳答案

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

如果所有圆圈的透明度都相同,那就非常简单了。

解决方案

snapshot
一些随机背景上的圆圈

你需要做的是:

  • 分配一个屏幕外的 Canvas ,圆圈被绘制为实心(无透明度)
  • 分三步画出圆圈。
  • 首先是所有圆圈的红色表面,然后是所有圆圈的蓝色表面等等。
  • 为主(可见) Canvas 设置透明度的全局 alpha
  • 清除两个 Canvas
  • 将屏幕外 Canvas 绘制到主 Canvas

你的 circle 函数看起来像这样:

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 中心点以及半径。但除此之外,它还需要一个介于 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,然后重新绘制它们,但为了演示的缘故,我将为它们制作动画。

在绘制之前,我们在主 Canvas 上设置了全局 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);
}

如果您想在 Canvas 上绘制其他元素或使用第二个屏幕 Canvas 来保存静态内容,则可以为每个操作重置全局 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>

关于html - 使用 alpha channel 绘制重叠的圆圈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17622037/

相关文章:

检查有向图是否强连通的算法

algorithm - 哪种排序方式最适合并行处理?

javascript - 同时监听多个按键事件

javascript - 在 IOS 设备上暂停 (html5) youtube 视频

java - 插入加号时查找数字的所有组合

android - 如何在android中将编辑后的 Canvas 保存为原始图像大小的图像

javascript - 用 Canvas 绘图时如何在两点之间进行插值?

javascript - RobinHerbots 的 jQuery 输入掩码无法在 IE8 (WinXP) 上运行

javascript - 如何在 html 页面加载结束时执行任何特定操作或事件

javascript - 使用javascript删除表中的行