Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

canvas实现下雪效果 #21

Open
ChenJiaH opened this issue Aug 2, 2019 · 0 comments
Open

canvas实现下雪效果 #21

ChenJiaH opened this issue Aug 2, 2019 · 0 comments
Labels

Comments

@ChenJiaH
Copy link
Owner

ChenJiaH commented Aug 2, 2019

首发于微信公众号《前端成长记》,写于 2017.01.20

介绍

该Demo主要是requestAnimationFrame的一个简单应用,实现了粒子效果,网上也有一些其他的粒子效果的实现的根本原理也大致相同,只是运动路径算法差异。

在线示例

在线示例

实现

requestAnimationFrame

** 首先,你需要明白requestAnimationFrame是个什么东西 **

在浏览器动画程序中,我们通常使用一个定时器来循环每隔几毫秒移动目标物体一次,来让它动起来。如今有一个好消息,浏览器开发商们决定:“嗨,为什么我们不在浏览器里提供这样一个API呢,这样一来我们可以为用户优化他们的动画。”所以,这个requestAnimationFrame()函数就是针对动画效果的API,你可以把它用在DOM上的风格变化或画布动画或WebGL中。

** 那么使用它有什么优势呢? **

浏览器可以优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果。如果在一个浏览器标签页里运行一个动画,当这个标签页不可见时,浏览器会暂停它,这会减少CPU,内存的压力,节省电池电量。

所以,我们第一步现在代码中增加一个rAF方法

// 使用最优频率requestAnimationFrame代替定时器,当然如果浏览器如果不支持的话,还是使用定时器完成
window.rAF = (function(){    
  return window.requestAnimationFrame ||
             window.webkitRequestAnimationFrame ||
             window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function (callback) {                
              setTimeout(callback, 1000 / 60);            
            };
})();

初始化基本配置

  1. 首先我们可以设置一些默认的值,便于后续做修改调整或者适配等。
// 默认配置
var defaultSetting = {
  width: 900, // canvas默认宽度,后面修改为频幕宽度
  height: 300, // canvas默认高度
  canvas: null, // canvas对象
  ctx: null, // 画布content
  snowArr: [], // 雪花数组
  total: 80  // 雪花数量
}
  1. 接下来往雪花数组中推入指定数量的雪花对象,当然,每一个雪花我们可以给它设定自己的属性,长宽,渐变等。
defaultSetting.width = window.innerWidth || document.documentElement.clientWidth ||  document.body.clientWidth;
defaultSetting.canvas = document.getElementById("canvas");
defaultSetting.canvas.setAttribute("width",defaultSetting.width);
defaultSetting.ctx = defaultSetting.canvas.getContext("2d");
for(var i = 0; i < defaultSetting.total;i++){
    defaultSetting.snowArr.push({
        "left":Tools.createRandom(0, defaultSetting.width), // 工具函数createRandom是创建一个m上下n的随机数
        "top":Tools.createRandom(0, defaultSetting.height),
        "deg":Tools.createRandom(-6, 6),
        "scale":Tools.createRandom(3, 6)
    });
}

这个时候我们就得到了开始时刻雪花的数组和相关属性值。

核心,不断渲染雪花位置的函数

function updateSnow(){
    // 清除画布,重新绘制下一帧雪花的位置
    defaultSetting.ctx.clearRect(0, 0, defaultSetting.width, defaultSetting.height);
    defaultSetting.ctx.save();
    // 循环雪花数组,分别绘制每一个雪花的位置
    for(var i=0; i<defaultSetting.snowArr.length; i++){
        var h = 0.5 * defaultSetting.snowArr[i].scale;
        defaultSetting.snowArr[i].left = defaultSetting.snowArr[i].left + Math.tan(Tools.radian(defaultSetting.snowArr[i].deg))*h/2;
        defaultSetting.snowArr[i].top = defaultSetting.snowArr[i].top + h;
        // 删除画面外的雪花
        if(defaultSetting.snowArr[i].left < 0 || defaultSetting.snowArr[i].left > defaultSetting.width || defaultSetting.snowArr[i].top > defaultSetting.height){
            defaultSetting.snowArr.splice(i--, 1);
            continue;
        }
        var width_i = defaultSetting.snowArr[i].scale;
        // 雪花边界投影
        var ra = defaultSetting.ctx.createRadialGradient(defaultSetting.snowArr[i].left, defaultSetting.snowArr[i].top, width_i/4, defaultSetting.snowArr[i].left, defaultSetting.snowArr[i].top, width_i);
        ra.addColorStop(0, "rgba(255,255,255,0.8)");
        ra.addColorStop(1, "rgba(255,255,255,0.1)");
        defaultSetting.ctx.fillStyle = ra;
        defaultSetting.ctx.beginPath();
        defaultSetting.ctx.arc(defaultSetting.snowArr[i].left, defaultSetting.snowArr[i].top, width_i, 0, 2*Math.PI);
        defaultSetting.ctx.fill();
    }
    defaultSetting.ctx.restore();
    // 持续刷新雪花的位置
    rAF(updateSnow);
}

这样我们就得到了一组80个不断运动的雪花,但是这样的话不会有源源不断的新雪花加入,所以,最后一步,我们还需要不断补充新鲜雪花。

补充新鲜雪花

这个时候就会发现开始把雪花数组给单独提出来是多么明智,只需要不断push到该数组就行了

// 增加新的雪花
function createNewSnow(){
    setTimeout(function(){
        if(defaultSetting.snowArr.length < 200){
            for(var i=0; i<20; i++){
                defaultSetting.snowArr.push({
                    "left":Tools.createRandom(0, defaultSetting.width),
                    "top":0,
                    "deg":Tools.createRandom(-6, 6),
                    "scale":Tools.createRandom(3, 6)
                });
            }
        }
        createNewSnow();
    }, Math.random()*200+500);
}
createNewSnow();

总结

一个简单的粒子下雪效果就是这么简单,进一步可以去试试alloy团队的粒子效果。

(完)


本文为原创文章,可能会更新知识点及修正错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验
如果能给您带去些许帮助,欢迎 ⭐️star 或 ✏️ fork
(转载请注明出处:https://chenjiahao.xyz)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant