可以在不使用像素操作或任何库的情况下进行本机操作。
只要所有圆圈的透明度都相同,它就非常简单。
解决方案
一些随机背景上的圆圈
您需要做的是:
- 分配一个屏幕外画布,其中圆圈绘制为实体(无透明度)
- 分三步画圆圈。
- 首先是所有圆圈的红色表面,然后是所有圆圈的蓝色表面,依此类推。
- 为主(可见)画布设置透明度的全局 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();
}
该函数采用x
和y
中心点以及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>