【问题标题】:shadowBlur (html canvas) not working in js loopshadowBlur(html画布)在js循环中不起作用
【发布时间】:2020-10-03 11:46:01
【问题描述】:

我正在尝试改进我使用 JS 创建的天空动画中的星星。这是我发现的时候,我可以使用 shadowBlur 属性来改变围绕我的星星创建的阴影的大小,让它看起来像是在闪烁。现在的问题是 shadowBlur 上升但不会变回黑色。这是我使用的代码。对此的任何帮助将不胜感激:)。

祝你有美好的一天!

// ---- Vars for star animation
let randomStars     = [];
let starCollection  = [];
let numberofStars   = 50;
let flickeringStars = 50;

class Star{
    constructor(x,y,color,radius,shadowBlur){
        this._canvas        = document.querySelector('canvas');
        this._canvas.width  = window.innerWidth;
        this._canvas.height = window.innerHeight; 
        this._c             = this._canvas.getContext('2d');
        this._radius        = radius;

        this._x             = x;
        this._y             = y;
        this._color         = color;  

        this._shadowBlur    = 10;
        this._shadowColor   = 'white';
    }
    //drawing individual stars
    draw(){
        this._c.beginPath();
        this._c.arc(this._x,this._y,this._radius,0,Math.PI * 2,false);
        this._c.fillStyle   = this._color;
        this._c.strokeStyle = 'black';
        this._c.shadowColor = this._shadowColor;
        this._c.shadowBlur  = this._shadowBlur;
        this._c.stroke();
        this._c.fill();
        this._c.closePath();
    }

 
    //Fade in and out for stars
    flicker(){
        setTimeout(()=>{this._shadowBlur=10;},200);
        setTimeout(()=>{this._shadowBlur=8;},400);
        setTimeout(()=>{this._shadowBlur=6;},600);
        setTimeout(()=>{this._shadowBlur=4;},800);
        setTimeout(()=>{this._shadowBlur=2;},1000);

        setTimeout(()=>{this._shadowBlur=0;},1200);
        setTimeout(()=>{this._shadowBlur=2;},1400);
        setTimeout(()=>{this._shadowBlur=4;},1600);
        setTimeout(()=>{this._shadowBlur=6;},1800);
        setTimeout(()=>{this._shadowBlur=8;},2000);
        setTimeout(()=>{this._shadowBlur=10;},2200);

        setTimeout(()=>{this.draw();},200);
        setTimeout(()=>{this.draw();},400);
        setTimeout(()=>{this.draw();},600);
        setTimeout(()=>{this.draw();},800);
        setTimeout(()=>{this.draw();},1000);
        setTimeout(()=>{this.draw();},1200);
        setTimeout(()=>{this.draw();},1400);

        setTimeout(()=>{this.draw();},1600);
        setTimeout(()=>{this.draw();},1800);
        setTimeout(()=>{this.draw();},2000);
        setTimeout(()=>{this.draw();},2200);
    }

    //Clears the canvas
    clearstars(){
        this._c.clearRect(0,0,window.innerWidth, window.innerHeight);
    }

}


// ---- Functions ----

//Typing animation
const typingAnimation       = ()=>{
        if(textProgress < text.length){
            setTimeout(()=>{requestAnimationFrame(typingAnimation)}, speed);
            if(text.charAt(textProgress) === '\n')document.getElementById('animation-text').innerHTML += '</br>';
            document.getElementById('animation-text').innerHTML += text.charAt(textProgress);
            textProgress ++;
        }else{
            let event      = new CustomEvent('showStars');
            dispatchEvent(event);
        }
}
//Store stars
const storeStars            = ()=>{
    starCollection = [];
    
    for(let i=0;i<numberofStars;i++){
        let x           = Math.floor(Math.random()*window.innerWidth);
        let y           = Math.floor(Math.random()*window.innerHeight);
        starCollection.push(new Star(x,y,"white",(Math.random()+1)-0.7));
    }
}
//Show stars to the screen
const showStars             = ()=>{
    for(let i=0;i<starCollection.length;i++){
        starCollection[i].draw();
    }
}
//Store random stars
const generateRandomStars   = ()=>{
    randomStars = [];

    for(let i=0;i<flickeringStars;i++){
        let x           = Math.floor(Math.random()*window.innerWidth);
        let y           = Math.floor(Math.random()*window.innerHeight);
        randomStars.push(new Star(x,y,"white",(Math.random()+1)-0.7));
    }
}
//Show randoms stars after clearing previous set of flickering stars
const showRandomStars       = ()=>{
    let id = window.setTimeout(function () { }, 0);
    while (id--) {
        window.clearTimeout(id); 
    }

    let starHandler = new Star(0,0,"white",0);
    starHandler.clearstars();
                    
    showStars();
    flickerStars();
}
//Flickers stars and changes set of stars randomly
const flickerStars = ()=>{
    for(let i=0;i<flickeringStars;i++){
        setInterval(()=>{
            randomStars[i].flicker();
        },2200);
        setInterval(()=>{
            console.log("changing stars pattern");
            generateRandomStars();
            showRandomStars();
        },12200);
    }
}


// ---- Event Listeners ----

//Typing animation on load
window.addEventListener("load", ()=>{
    storeStars();
    generateRandomStars();
    showStars();
    flickerStars();  
});

//Handles star animation scaling on window resize
window.addEventListener("resize", ()=>{
    let id = window.setTimeout(function () { }, 0);
    while (id--) {
        window.clearTimeout(id); 
    }

    let starHandler = new Star(0,0,"white",0);
    starHandler.clearstars();

    generateRandomStars();
    storeStars();                
    showStars();
    flickerStars();
});
body{
background-color:black;
}
<html>
<body><canvas></canvas></body>
</html>

【问题讨论】:

    标签: html loops canvas shadow blur


    【解决方案1】:

    我无法确定您想要获得的 FX。下面是一个简单的闪烁星形动画,它使用改变大小的小矩形来模拟闪烁。

    降低帧率,使闪烁更加明显。

    它非常高效,不需要复杂的状态更改,或缓慢的渲染方法(如模糊)

    var W = 0, H = 0; // hold canvas size. Set to zero so first frame sizes canvas
    var count = 500;         // number of stars
    var frame = 0;           // current frame number
    const frameRate = 5;     // render stars ever 5 frames
    
    const sizeRange = 1.5;   // max size of star us minSize + sizeRange + flickerSize
    const minSize = 1;       // minimum size of star
    const flickerSize = 1;   // amount of change random change to make a star brighter
    const flickerRate = 0.1; // odds per rendered frame that a star flickers
    const stars = [];
    
    // Create a random set of numbers for star positions.
    // Values must be larger than largest canvas you are going to use.
    // This will work up to 8K display
    while (count--) { stars.push(Math.random() * 1024 * 8) } 
    
    const ctx = canvas.getContext("2d");
    requestAnimationFrame(mainLoop);
    
    function mainLoop() {
      var len = stars.length, i = 0, x = stars[i++], starSize;
    
      // if the window has resized change the canvas to fit
      if (W !== innerWidth || H !== innerHeight) {
        W = canvas.width = innerWidth;
        H = canvas.height = innerHeight;
        ctx.fillStyle = "#FFF";
        frame = 0;
      }
      if (frame++ % frameRate === 0) {  // only render every frameRate frames
        ctx.clearRect(0, 0, W, H);            
        ctx.beginPath();  // draw all stars with one path aas it is several orders
                          // of magnitude quicker than creating a path for each
                                
        while (i < len) {
          // draws small stars to large
          const starScale = (i / len);               // set scale from 0 to 1
          starSize = sizeRange;                      // set the range of sizes
          if (Math.random() < flickerRate) {         // on random odds of flicker
            starSize += flickerSize * Math.random(); // add random flicker size
          } 
          starSize *= starScale;   // scale the star
          starSize += minSize;     // add min size of star
          halfSize = starSize / 2; // offset to top left of star
    
          const y = stars[i++]; // get next random number as star y pos
    
          // add rect to path fitted to canvas width and height (W, H)
          ctx.rect((x % W) - halfSize , (y % H) - halfSize , starSize , starSize );
          x = y; // Use y for the next x star coordinate
    
        }
        ctx.fill(); // fill in all the stars
      }
      requestAnimationFrame(mainLoop);
    }
    canvas {
      position: absolute;
      top: 0px;
      left: 0px;
      background: #000;
    }
    &lt;canvas id="canvas"&gt;&lt;/canvas&gt;

    【讨论】:

    • 感谢您的帮助。但是我已经在我以前的一个代码版本中实现了这种动画效果。出于某种原因,尽管在星星周围添加阴影并使其减少和增加使它看起来更加逼真,这就是为什么我想测试我发布的当前代码有什么问题。再次非常感谢您的建议。
    • @JohanGodinho 计时器事件很混乱,最终会导致页面崩溃。计时器远非准确,当你有这么多计时器时,情况会变得更糟。在函数showRandomStars 中,while 循环可能会迭代数万次,并且每 12.2 秒您启动 100 个间隔计时器,它们都设置为两次触发。在我给出的示例中,我使用了一个动画循环并使用一个(或多个)帧计数器来控制时间,这更容易管理,时间也会更准确。
    • 我明白你的意思了。对不起,我对 js 有点陌生。我注意到您的代码如何为我提高了很多性能,并且还需要更少的计算机资源。不过,我发现很难理解 while 循环中发生的数学问题。能稍微解释一下吗?
    • @JohanGodinho 我已经通过将 cmets 添加到 sn-p 来更新答案,并更改了变量名称以帮助您弄清楚它在做什么,
    • 非常感谢您的帮助。非常感谢:)
    猜你喜欢
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    • 2015-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-16
    • 1970-01-01
    相关资源
    最近更新 更多