How to add a fade effect to only certain elements on a html canvas

老子叫甜甜 提交于 2020-01-16 13:06:29

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!