想用 js 来写一个下雪效果并不难, 但是如果要兼顾到效果的逼真度, 流畅度, 用户体验那就需要好好思考一下了, 至少有几个问题需要想清楚:
1, 绘制是用 dom 来做还是 canvas 等别的技术?
如果使用 dom 来绘制, 那么会有性能上的瓶颈, 在动态控制的节点较少时, 看不太明显, 但是满屏的雪花肯定是会有一些能感知的卡顿的, 但反观 canvas, 其运行会被硬件加速, 流畅度会好很多.
2, 雪花素材是使用图片还是用 canvas 亦或是 svg?
如果选择用 dom 来做, 那么最简单的做法就是使用图片, 当然也可以使用 canvas 或者 svg, 这种情况这里不做详细说明, 重点说一下选用 canvas 我们应该选用图片素材还是直接绘制, 利用 canvas 我们也是可以轻松绘制出雪花的, 可参考我之前的一片文章 js 分形艺术 - koch 雪花曲线, 但是这种绘制雪花的方法是通过迭代来实现的, 其性能肯定不好, 想一想满屏的雪花呀, cpu 不得被疯吃, 所以这里建议采用图片素材.
想明白了这些, 其实现就相对简单了, 下雪无非就是坐标的更新罢了, 当然为了尽可能逼真中间还是有一些小细节需要注意, 比如雪花的飘落方向, 有可能忽左忽右吧, 比如雪花有可能有大有小吧等等, 下面贴出我的实现代码:
javascript 代码
- (function(window, document, undefined) {
- // 存储所有的雪花
- const snows = [];
- // 下落的加速度
- const G = 0.01;
- const fps = 60;
- // 速度上限, 避免速度过快
- const SPEED_LIMIT_X = 1;
- const SPEED_LIMIT_Y = 1;
- const W = window.innerWidth;
- const H = window.innerHeight;
- let tickCount = 150;
- let ticker = 0;
- let lastTime = Date.now();
- let deltaTime = 0;
- let canvas = null;
- let ctx = null;
- let snowImage = null;
- window.requestAnimationFrame = (function() {
- return window.requestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame ||
- window.oRequestAnimationFrame ||
- window.msRequestAnimationFrame ||
- function (callback) {
- setTimeout(callback, 1000/ fps);
- }
- })();
- init();
- function init() {
- createCanvas();
- canvas.width = W;
- canvas.height = H;
- canvas.style.CSSText = 'position: fixed; top: 0; left: 0; pointer-events: none;';
- document.body.appendChild(canvas);
- // 小屏幕时延长添加雪花时间, 避免屏幕上出现太多的雪花
- if (W <768) {
- tickCount = 350;
- }
- snowImage = new Image();
- snowImage.src = 'snow.png';
- loop();
- }
- function loop() {
- requestAnimationFrame(loop);
ctx.clearRect(0, 0, W, H);
- const now = Date.now();
- deltaTime = now - lastTime;
- lastTime = now;
- ticker += deltaTime;
- if (ticker> tickCount) {
- snows.push(
- new Snow(Math.random() * W, 0, Math.random() * 5 + 5)
- );
- ticker %= tickCount;
- }
- const length = snows.length;
- snows.map(function(s, i) {
- s.update();
- s.draw();
- if (s.y>= H) {
- snows.splice(i, 1);
- }
- });
- }
- function Snow(x, y, radius) {
- this.x = x;
- this.y = y;
- this.sx = 0;
- this.sy = 0;
- this.deg = 0;
- this.radius = radius;
- this.ax = Math.random() <0.5 ? 0.005 : -0.005;
- }
- Snow.prototype.update = function() {
- const deltaDeg = Math.random() * 0.6 + 0.2;
- this.sx += this.ax;
- if (this.sx>= SPEED_LIMIT_X || this.sx <= -SPEED_LIMIT_X) {
- this.ax *= -1;
- }
- if (this.sy < SPEED_LIMIT_Y) {
- this.sy += G;
- }
- this.deg += deltaDeg;
- this.x += this.sx;
- this.y += this.sy;
- }
- Snow.prototype.draw = function() {
- const radius = this.radius;
- ctx.save();
- ctx.translate(this.x, this.y);
- ctx.rotate(this.deg * Math.PI / 180);
ctx.drawImage(snowImage, -radius, -radius, radius * 2, radius * 2);
- ctx.restore();
- }
- function createCanvas() {
- canvas = document.createElement('canvas');
- ctx = canvas.getContext('2d');
- }
- })(window, document);
使用方式只需要将上面代码保存为 js 文件, 然后在任意一个 html 文件中引用就可以了.
效果和雪花素材图可以到 http://www.deanhan.cn/canvas-snow.html http://www.deanhan.cn/canvas-snow.html 查看.
来源: http://www.qdfuns.com/article/17507/0d42d5997e75b94a56f0ffa5b55517f2.html