1

这个问题已经在这里得到了回答:重叠圆圈的组合区域

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

在此处输入图像描述

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

在此处输入图像描述

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

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

4

2 回答 2

2

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

只要所有圆圈的透明度都相同,它就非常简单。

解决方案

快照
一些随机背景上的圆圈

您需要做的是:

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

您的 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中心点以及radius。但除此之外,它需要一个step介于 0 和 2 之间的值来确定正在绘制的表面。这在接下来的步骤中很重要。

首先我们可以定义一个数组来保存我们想要绘制的所有圆圈:

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>

于 2013-07-12T20:12:28.920 回答
1

您必须使用像 RaphaelJS 这样的库。它的能力超出了您在这里的要求。

编辑:您可以通过 js 通过编程确定中心之间的距离 (D) 并将其与半径 (S) 的总和进行比较来应用 alpha 透明度。如果 D < S,则它们重叠,因此请注意红色圆圈。

( http://raphaeljs.com/ )

于 2013-07-12T19:05:42.767 回答