首页 > 解决方案 > JavaScript - 画布下雪动画

问题描述

我想在 JavaScript 中创建下雪动画效果。我对这种编码语言的知识真的很差。但是,我设法用一片雪花和一个循环来做到这一点。

我现在想生成不止一个雪花。有人可以帮我弄清楚如何产生更多的雪花吗?

let canvas = document.getElementById("wip");
let ctx = canvas.getContext("2d");
canvas.width = window.innerWidth-30;
canvas.height = window.innerHeight-30;

let xloc = Math.floor(Math.random() * canvas.width);
let yloc = 0;
ctx.strokeStyle="#"+((1<<24)*Math.random()|0).toString(16);

function drawflake(){
    ctx.beginPath();

    //Top Right Brunch
    ctx.moveTo(xloc,yloc);
    ctx.lineTo(xloc-7,yloc-7);
    ctx.lineTo(xloc-11,yloc-7);

    ctx.moveTo(xloc-7,yloc-7);
    ctx.lineTo(xloc-7,yloc-11);

    ctx.moveTo(xloc-7,yloc-7);
    ctx.lineTo(xloc-14,yloc-14);
    ctx.lineTo(xloc-17,yloc-14);

    ctx.moveTo(xloc-14,yloc-14);
    ctx.lineTo(xloc-14,yloc-17);

    ctx.moveTo(xloc-14,yloc-14);
    ctx.lineTo(xloc-18,yloc-18);

    //Top Left Brunch
    ctx.moveTo(xloc,yloc);
    ctx.lineTo(xloc+7,yloc-7);
    ctx.lineTo(xloc+11,yloc-7);

    ctx.moveTo(xloc+7,yloc-7);
    ctx.lineTo(xloc+7,yloc-11);

    ctx.moveTo(xloc+7,yloc-7);
    ctx.lineTo(xloc+14,yloc-14);
    ctx.lineTo(xloc+17,yloc-14);

    ctx.moveTo(xloc+14,yloc-14);
    ctx.lineTo(xloc+14,yloc-17);

    ctx.moveTo(xloc+14,yloc-14);
    ctx.lineTo(xloc+18,yloc-18);

    //Bottom Left Brunch
    ctx.moveTo(xloc,yloc);
    ctx.lineTo(xloc+7,yloc+7);
    ctx.lineTo(xloc+7,yloc+11);

    ctx.moveTo(xloc+7,yloc+7);
    ctx.lineTo(xloc+11,yloc+7);

    ctx.moveTo(xloc+7,yloc+7);
    ctx.lineTo(xloc+14,yloc+14);
    ctx.lineTo(xloc+14,yloc+17);

    ctx.moveTo(xloc+14,yloc+14);
    ctx.lineTo(xloc+17,yloc+14);

    ctx.moveTo(xloc+14,yloc+14);
    ctx.lineTo(xloc+18,yloc+18);

    //Bottom Right Brunch
    ctx.moveTo(xloc,yloc);
    ctx.lineTo(xloc-7,yloc+7);
    ctx.lineTo(xloc-11,yloc+7);

    ctx.moveTo(xloc-7,yloc+7);
    ctx.lineTo(xloc-7,yloc+11);

    ctx.moveTo(xloc-7,yloc+7);
    ctx.lineTo(xloc-14,yloc+14);
    ctx.lineTo(xloc-17,yloc+14);

    ctx.moveTo(xloc-14,yloc+14);
    ctx.lineTo(xloc-14,yloc+17);

    ctx.moveTo(xloc-14,yloc+14);
    ctx.lineTo(xloc-18,yloc+18);

    //Left Brunch
    ctx.moveTo(xloc,yloc);
    ctx.lineTo(xloc-9.899,yloc);
    ctx.lineTo(xloc-12.899,yloc-3);

    ctx.moveTo(xloc-9.899,yloc);
    ctx.lineTo(xloc-12.899,yloc+3);

    ctx.moveTo(xloc-9.899,yloc);
    ctx.lineTo(xloc-19.799,yloc);
    ctx.lineTo(xloc-22.799,yloc-3);

    ctx.moveTo(xloc-19.799,yloc);
    ctx.lineTo(xloc-22.799,yloc+3);

    ctx.moveTo(xloc-19.799,yloc);
    ctx.lineTo(xloc-25.456,yloc);

    //Right Brunch
    ctx.moveTo(xloc,yloc);
    ctx.lineTo(xloc+9.899,yloc);
    ctx.lineTo(xloc+12.899,yloc-3);

    ctx.moveTo(xloc+9.899,yloc);
    ctx.lineTo(xloc+12.899,yloc+3);

    ctx.moveTo(xloc+9.899,yloc);
    ctx.lineTo(xloc+19.799,yloc);
    ctx.lineTo(xloc+22.799,yloc-3);

    ctx.moveTo(xloc+19.799,yloc);
    ctx.lineTo(xloc+22.799,yloc+3);

    ctx.moveTo(xloc+19.799,yloc);
    ctx.lineTo(xloc+25.456,yloc);

    ctx.lineWidth = 1;
    ctx.lineCap = 'round';
    ctx.stroke();

    yloc=yloc+1;
    if (yloc>canvas.height) {
        yloc=0;
        xloc = Math.floor(Math.random() * canvas.width);
        ctx.strokeStyle="#"+((1<<24)*Math.random()|0).toString(16);
    }
}

function clearCanvas() {
    ctx.clearRect(0,0, canvas.width, canvas.height);
}

function repeat() {
    clearCanvas();
    drawflake();
    setTimeout(repeat, 24);
}

repeat();
<canvas id="wip"></canvas>

标签: javascript

解决方案


您可以将所有特定于 flake 的代码移动到 aclass中,以便您可以使用new. 定义薄片属性的变量应定义为实例属性。在这种情况下,您需要对变量xlocyloc进行更改strokeStyle。尽管后者是 的一个属性ctx,但每次显示另一个薄片时,该属性都需要更改,因此薄片必须知道自己的颜色是什么。所以这需要是一个属性。

然后我建议删除将薄片移回顶部的代码,因为这确实应该被视为不同的薄片。所以销毁前一个实例并创建一个新实例更有意义。

另一个异步循环(也有setTimeout,但会有更大的延迟)可以负责创建新的薄片。

以下是您的代码如何适应这种情况。评论澄清了我所做的所有更改:

let canvas = document.getElementById("wip");
let ctx = canvas.getContext("2d");
canvas.width = window.innerWidth-30;
canvas.height = window.innerHeight-30;

class SnowFlake { // each snowflake will be an instance of this class
    constructor() { // this is called when you do `new SnowFlake()`
        // the location is now saved in variables that belong to the instance:
        // Note the use of `this` in all three assignments:
        this.xloc = Math.floor(Math.random() * canvas.width);
        this.yloc = 0;
        this.strokeStyle="#"+((1<<24)*Math.random()|0).toString(16);
    }
    drawflake(){ // This function becomes a method
        // Get the stroke style that was saved during construction:
        ctx.strokeStyle=this.strokeStyle;
        // Get the location that was saved during construction
        let xloc = this.xloc;
        let yloc = this.yloc;
        
        // No change needed in the drawing part of this function...
        ctx.beginPath();

        //Top Right Brunch
        ctx.moveTo(xloc,yloc);
        ctx.lineTo(xloc-7,yloc-7);
        ctx.lineTo(xloc-11,yloc-7);

        ctx.moveTo(xloc-7,yloc-7);
        ctx.lineTo(xloc-7,yloc-11);

        ctx.moveTo(xloc-7,yloc-7);
        ctx.lineTo(xloc-14,yloc-14);
        ctx.lineTo(xloc-17,yloc-14);

        ctx.moveTo(xloc-14,yloc-14);
        ctx.lineTo(xloc-14,yloc-17);

        ctx.moveTo(xloc-14,yloc-14);
        ctx.lineTo(xloc-18,yloc-18);

        //Top Left Brunch
        ctx.moveTo(xloc,yloc);
        ctx.lineTo(xloc+7,yloc-7);
        ctx.lineTo(xloc+11,yloc-7);

        ctx.moveTo(xloc+7,yloc-7);
        ctx.lineTo(xloc+7,yloc-11);

        ctx.moveTo(xloc+7,yloc-7);
        ctx.lineTo(xloc+14,yloc-14);
        ctx.lineTo(xloc+17,yloc-14);

        ctx.moveTo(xloc+14,yloc-14);
        ctx.lineTo(xloc+14,yloc-17);

        ctx.moveTo(xloc+14,yloc-14);
        ctx.lineTo(xloc+18,yloc-18);

        //Bottom Left Brunch
        ctx.moveTo(xloc,yloc);
        ctx.lineTo(xloc+7,yloc+7);
        ctx.lineTo(xloc+7,yloc+11);

        ctx.moveTo(xloc+7,yloc+7);
        ctx.lineTo(xloc+11,yloc+7);

        ctx.moveTo(xloc+7,yloc+7);
        ctx.lineTo(xloc+14,yloc+14);
        ctx.lineTo(xloc+14,yloc+17);

        ctx.moveTo(xloc+14,yloc+14);
        ctx.lineTo(xloc+17,yloc+14);

        ctx.moveTo(xloc+14,yloc+14);
        ctx.lineTo(xloc+18,yloc+18);

        //Bottom Right Brunch
        ctx.moveTo(xloc,yloc);
        ctx.lineTo(xloc-7,yloc+7);
        ctx.lineTo(xloc-11,yloc+7);

        ctx.moveTo(xloc-7,yloc+7);
        ctx.lineTo(xloc-7,yloc+11);

        ctx.moveTo(xloc-7,yloc+7);
        ctx.lineTo(xloc-14,yloc+14);
        ctx.lineTo(xloc-17,yloc+14);

        ctx.moveTo(xloc-14,yloc+14);
        ctx.lineTo(xloc-14,yloc+17);

        ctx.moveTo(xloc-14,yloc+14);
        ctx.lineTo(xloc-18,yloc+18);

        //Left Brunch
        ctx.moveTo(xloc,yloc);
        ctx.lineTo(xloc-9.899,yloc);
        ctx.lineTo(xloc-12.899,yloc-3);

        ctx.moveTo(xloc-9.899,yloc);
        ctx.lineTo(xloc-12.899,yloc+3);

        ctx.moveTo(xloc-9.899,yloc);
        ctx.lineTo(xloc-19.799,yloc);
        ctx.lineTo(xloc-22.799,yloc-3);

        ctx.moveTo(xloc-19.799,yloc);
        ctx.lineTo(xloc-22.799,yloc+3);

        ctx.moveTo(xloc-19.799,yloc);
        ctx.lineTo(xloc-25.456,yloc);

        //Right Brunch
        ctx.moveTo(xloc,yloc);
        ctx.lineTo(xloc+9.899,yloc);
        ctx.lineTo(xloc+12.899,yloc-3);

        ctx.moveTo(xloc+9.899,yloc);
        ctx.lineTo(xloc+12.899,yloc+3);

        ctx.moveTo(xloc+9.899,yloc);
        ctx.lineTo(xloc+19.799,yloc);
        ctx.lineTo(xloc+22.799,yloc-3);

        ctx.moveTo(xloc+19.799,yloc);
        ctx.lineTo(xloc+22.799,yloc+3);

        ctx.moveTo(xloc+19.799,yloc);
        ctx.lineTo(xloc+25.456,yloc);

        ctx.lineWidth = 1;
        ctx.lineCap = 'round';
        ctx.stroke();

        // Save the new location to the instance:
        this.yloc = yloc+1;
        // Don't deal here with the range check. Do that in `repeat`
    }
}

function clearCanvas() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
}

// Collection that holds all of the active flakes
let flakes = new Set;

function repeat() {
    clearCanvas();
    // Draw all flakes
    for (let flake of flakes) {
        flake.drawflake();
        if (flake.yloc > canvas.height) { // flake reached the bottom?
            flakes.delete(flake); // remove the flake from the collection
        }
    }
    setTimeout(repeat, 24);
}

// new function to deal with the generation of new flakes
function generate() {
    flakes.add(new SnowFlake()); // construct a new flake and add it to the collection
    // Make the time between two generations of a new flakes a bit random
    setTimeout(generate, 1000 + Math.random() * 2000);
}

generate(); // also start the generations
repeat();
<canvas id="wip"></canvas>

您可以通过更改表达式中使用的两个常数来影响薄片生成的速率及其变化1000 + Math.random() * 2000


推荐阅读