这个问题在这里得到了某种程度的回答:Combined area of overlapping circles
但我的问题更具体。我在其他任意大小的圆圈内有任意数量的任意大小的圆圈,以制作类似目标的图像:
这张图片必须有一定的透明度。整个形状的透明度必须相同。然后,有任意数量的这些形状可以重叠,它需要看起来像这样:
无论重叠多少,透明度都必须保持不变。
我唯一能想到的方法是遍历 Canvas 上的每个像素,并根据像素与每个圆的中心的距离计算像素应该是什么颜色,但这需要太长时间。我希望圆圈也可以拖动,所以这需要非常快。有一个更好的方法吗? (抱歉我的 GIMP 技术不好)
最佳答案
可以在不使用像素操作或任何库的情况下以本地方式执行。
如果所有圆圈的透明度都相同,那就非常简单了。
解决方案
一些随机背景上的圆圈
你需要做的是:
- 分配一个屏幕外的 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();
}
该函数采用x
和y
中心点以及半径
。但除此之外,它还需要一个介于 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/