问题
I have a canvas with multiple circles in different colours and I want add a fade out effect only to some circles. The effect is only applicable to the ones in red and green.
The code is as follows
function drawPiece(pieceX, pieceY, color) {
if (color === "rgba(0,0,0,1)" || color === "rgba(255,255,255,1)"){
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(pieceX, pieceY, 50, 0, 2 * Math.PI, false);
ctx.fill();
ctx.lineWidth = "4";
ctx.strokeStyle = "rgba(0,0,0,1)";
ctx.stroke();
ctx.closePath();
}
else {
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(pieceX, pieceY, 10, 0, 2 * Math.PI, false);
ctx.fill();
ctx.lineWidth = "4";
ctx.strokeStyle = "rgba(0,0,0,1)";
ctx.stroke();
ctx.closePath();
setTimeout(function(){
var fadeTarget = document.getElementById("canvasGame");
var fadeEffect = setInterval(function () {
if (!fadeTarget.style.opacity) {
fadeTarget.style.opacity = 1;
}
if (fadeTarget.style.opacity > 0) {
fadeTarget.style.opacity -= 0.02;
} else {
clearInterval(fadeEffect);
}
}, 20);
},0.5);
}
}
The fade effect works but it fades out the whole canvas and not the individual circles.
How can I achieve this, that only some elements are faded out.
Thanks in advance
回答1:
A great canvas 2d resource is MDN's CanvasRenderingContext2D
Animations using canvas.
You will need a render loop if you want to animate canvas content.
The render loop is called 60 times a second, if possible, drawing too much and the rate will drop below 60fps.
The main loop clears the canvas, then draws the animated content, then requests the next frame.
requestAnimationFrame(mainLoop); // request the first frame to start the animation
function mainLoop() {
ctx.globalAlpha = 1; // default to 1 in case there is other content drawn
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // clear the canvas
drawContent(); // render the content.
requestAnimationFrame(mainLoop); // request the next frame (in 1/60th second)
}
A function to draw the circle. You can remove the alpha from the color and use globalAlpha to set the transparency.
Math.TAU = Math.PI * 2; // set up 2 PI
function drawCircle(x, y, radius, color, alpha = 1) {
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
ctx.strokeStyle = "#000";
ctx.lineWidth = 4;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.TAU);
ctx.fill();
ctx.stroke();
}
Create an object to hold a circle's description and an array to put them in
const circles = [];
function circle(x,y,r = 10, col = "#FFF", alpha = 1) {
return {x, y, r, col, alpha, alphaTarget: alpha};
}
Then in the drawContent
function draw the circles one at a time
function drawContent() {
for (const circle of circles) {
if(circle.alpha !== circle.alphaTarget) {
const aStep = circle.alphaTarget - circle.alpha;
const dir = Math.sign(aStep);
circle.alpha += Math.min(Math.abs(aStep), dir * 0.02)) * dir;
}
drawCircle(circle.x, circle.y, circle.r, circle.col, circle.alpha);
}
}
Demo
The demo draws 100 circles each with their own color and alpha. The alpha is randomly selected to fade out and then back in.
You will need a render loop if you want to animate canvas content.
I move the circle so that if a device is to slow to render the content then it will be easier to see the low frame rate.
Math.TAU = Math.PI * 2; // set up 2 PI
Math.rand = (val) => Math.random() * val;
Math.randI = (val) => Math.random() * val | 0;
requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d");
const W = canvas.width = innerWidth; // size canvas to page
const H = canvas.height = innerHeight; // size canvas to page
const circleCount = 100;
const circleFadeRate = 0.01; // per 60th second
const circles = [];
const circle = (x,y,r = 10, col = "#FFF", alpha = 1) => ({x, y, r, col, alpha, alphaTarget: alpha});
createCircles();
function createCircles() {
var i = circleCount;
while (i--) {
circles.push(circle(Math.rand(W), Math.rand(H), Math.rand(10) + 10, "#" + Math.randI(0xFFF).toString(16).padStart(3,"0"), 1));
}
circles.sort((a,b) => a.r - b.r); // big drawn last
}
function mainLoop() {
ctx.globalAlpha = 1;
ctx.clearRect(0, 0, W, H);
drawContent();
requestAnimationFrame(mainLoop);
}
function drawCircle(x, y, radius, color, alpha = 1) {
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
ctx.strokeStyle = "#000";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.TAU);
ctx.fill();
ctx.stroke();
}
function drawContent() {
for (const circle of circles) {
if(circle.alpha !== circle.alphaTarget) {
const aStep = circle.alphaTarget - circle.alpha;
const dir = Math.sign(aStep);
circle.alpha += Math.min(Math.abs(aStep), 0.02) * dir;
} else if(Math.random() < 0.01) {
circle.alphaTarget = circle.alpha < 0.7 ? 1 : Math.random() * 0.4;
}
circle.y += (circle.r - 10) / 5;
circle.y = circle.y > H + circle.r + 2 ? -(circle.r + 2) : circle.y;
drawCircle(circle.x, circle.y, circle.r, circle.col, circle.alpha);
}
}
body {
padding: 0px;
}
canvas {
position: absolute;
top: 0px;
left: 0px;
}
<canvas id="canvas"></canvas>
For more information on the 2D canvas API see the link at top of this answer.
回答2:
Canvas is a painting surface. Meaning you can't change it after you paint it. You can only clear it, or paint over it. Just like a real painting, you can't change the color of a stroke you've already painted.
So you must clear the canvas and then redraw it all, except this time draw some circles with a different opacity. Just change the last number on those rgba
values to be between 0 and 1 to change the opacity.
Store opacity in a variable somewhere:
var circleOpacity = 1;
Change the opacity and then redraw in your interval function:
circleOpactiy -= 0.2;
drawMyCanvas();
Now draw the some pieces with a fillStyle
something like:
ctx.fillStyle = shouldBeFaded ? `rgba(0,0,0,${circleOpacity})` : 'rgba(0,0,0,1)'
Alternatively, you could position two canvases absolutely so they are on top of each other and you could fade the top one as you are already doing. That way you won't have to re-render the canvas constantly. If the only thing you want to do is fade some circles, this might be easier. But if you want to anything more complex on that canvas (like render a game of some sort) you'll want to redraw the canvas every frame of animation anyway.
来源:https://stackoverflow.com/questions/59547679/how-to-add-a-fade-effect-to-only-certain-elements-on-a-html-canvas