看到过许多的下雪动画,总感觉不够形象生动下面的这个下雪动画,是基于HTML5 Canvas实现的,理解起来也挺简单。
demo地址
先来一览是多生动的效果吧:http://www.xuanfengge.com/funny/html5/snowFall/
小tip:支持的浏览器有Chrome、IE9+、360极速
需求点分析
- 雪花形状:统一在canvas上绘制
- 随机绘画出雪花形状:运用canvas的绘制圆形渐变
- 雪花飘动画:利用requestAnimationFrame绘制动画帧
- 雪花数量:控制在一定范围,如200片
javascript代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
window.onload = function(){ /* * 自由下雪 snowFall * author:xuanfeng * time: 2014-01-11 */ // 控制下雪 function snowFall(snow) { // 可配置属性 snow = snow || {}; this.maxFlake = snow.maxFlake || 200; //最多片数 this.flakeSize = snow.flakeSize || 10; //雪花形状 this.fallSpeed = snow.fallSpeed || 2; //坠落速度 this.status = 0; //0-初始化、1-开始下雪、2-停止下雪、3-暂停下雪、4-继续下雪 } // 兼容写法 requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.oRequestAnimationFrame || function(callback) { setTimeout(callback, 1000 / 60); }; cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame || window.oCancelAnimationFrame; // 开始下雪 snowFall.prototype.start = function(){ if(this.status == 1 || this.status == 4){ // 已经在下雪则不作处理 return false; } this.status = 1; // 创建画布 snowCanvas.apply(this); // 创建雪花形状 createFlakes.apply(this); // 画雪 drawSnow.apply(this) } // 停止下雪 snowFall.prototype.stop = function(){ if(this.status == 2 || this.status == 0 || !this.canvas){ return false; } // 停止动画循环 this.pause(); this.status = 2; // 删除画布 this.canvas.parentNode.removeChild(this.canvas); this.canvas = null; } // 暂停下雪 snowFall.prototype.pause = function(){ if(this.status == 3){ return false; } this.status = 3; cancelAnimationFrame(this.loop) }; // 继续下雪 snowFall.prototype.resume = function(){ if(this.status == 3 && this.canvas){ this.status = 4; // 动画的计时控制 this.loop = requestAnimationFrame(function() { drawSnow.apply(that) }); } }; // 创建画布 function snowCanvas() { // 添加Dom结点 var snowcanvas = document.createElement("canvas"); snowcanvas.id = "snowfall"; snowcanvas.width = window.innerWidth; snowcanvas.height = window.innerHeight; snowcanvas.setAttribute("style", "position: fixed; top: 0; left: 0; z-index: 2999; pointer-events: none;"); document.getElementsByTagName("body")[0].appendChild(snowcanvas); this.canvas = snowcanvas; this.ctx = snowcanvas.getContext("2d"); // 窗口大小改变的处理 window.onresize = function() { snowcanvas.width = window.innerWidth; snowcanvas.height = window.innerHeight } } // 雪运动对象 function flakeMove(canvasWidth, canvasHeight, flakeSize, fallSpeed) { this.x = Math.floor(Math.random() * canvasWidth); //x坐标 this.y = Math.floor(Math.random() * canvasHeight); //y坐标 this.size = Math.random() * flakeSize + 2; //形状 this.maxSize = flakeSize; //最大形状 this.speed = Math.random() * 1 + fallSpeed; //坠落速度 this.fallSpeed = fallSpeed; //坠落速度 this.velY = this.speed; //Y方向速度 this.velX = 0; //X方向速度 this.stepSize = Math.random() / 30; //步长 this.step = 0 //步数 } flakeMove.prototype.update = function() { var x = this.x, y = this.y; // 左右摆动(余弦) this.velX *= 0.98; if (this.velY <= this.speed) { this.velY = this.speed } this.velX += Math.cos(this.step += .05) * this.stepSize; this.y += this.velY; this.x += this.velX; // 飞出边界的处理 if (this.x >= canvas.width || this.x <= 0 || this.y >= canvas.height || this.y <= 0) { this.reset(canvas.width, canvas.height) } }; // 飞出边界-放置最顶端继续坠落 flakeMove.prototype.reset = function(width, height) { this.x = Math.floor(Math.random() * width); this.y = 0; this.size = Math.random() * this.maxSize + 2; this.speed = Math.random() * 1 + this.fallSpeed; this.velY = this.speed; this.velX = 0; }; // 渲染雪花-随机形状 flakeMove.prototype.render = function(ctx) { var snowFlake = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size); snowFlake.addColorStop(0, "rgba(255, 255, 255, 0.9)"); snowFlake.addColorStop(.5, "rgba(255, 255, 255, 0.5)"); snowFlake.addColorStop(1, "rgba(255, 255, 255, 0)"); ctx.save(); ctx.fillStyle = snowFlake; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); ctx.restore(); }; // 创建雪花-定义形状 function createFlakes() { var maxFlake = this.maxFlake, flakes = this.flakes = [], canvas = this.canvas; for (var i = 0; i < maxFlake; i++) { flakes.push(new flakeMove(canvas.width, canvas.height, this.flakeSize, this.fallSpeed)) } } // 画雪 function drawSnow() { var maxFlake = this.maxFlake, flakes = this.flakes; ctx = this.ctx, canvas = this.canvas, that = this; // 清空雪花 ctx.clearRect(0, 0, canvas.width, canvas.height); for (var e = 0; e < maxFlake; e++) { flakes[e].update(); flakes[e].render(ctx); } // 一帧一帧的画 this.loop = requestAnimationFrame(function() { drawSnow.apply(that); }); } // 调用及控制方法 var snow = new snowFall({maxFlake:200}); var start = document.getElementById("start-snowFall"), stop = document.getElementById("stop-snowFall"), pause = document.getElementById("pause-snowFall"), resume = document.getElementById("resume-snowFall"); // 开始 start.onclick = function(){ snow.start(); } // 停止 stop.onclick = function(){ snow.stop(); } // 暂停 pause.onclick = function(){ snow.pause(); } // 恢复 resume.onclick = function(){ snow.resume(); } } |
点解
关于requestAnimationFrame
教程:http://technet.microsoft.com/zh-cn/library/hh920765.aspx
整个过程
- 创建画布,获得2D上下文对象
- 根据雪花片数,定义雪花随机形状、出现位置、X随机速度、Y随机速度
- 定义动画帧,让每个帧的雪花位置都改变并渲染
- 雪花的下路路径是以左右摇摆(即余弦函数图)形式下落
- 超出边界时,回到顶部随机位置继续下落,保持雪花最大片数
左右摇摆
主要代码:
1 2 3 4 |
this.step = 0; this.stepSize = Math.random() / 30; this.velX += Math.cos(this.step += .05) * this.stepSize; this.x += this.velX; |
step从0开始,每一帧增加0.05,相当于上图的横轴(X坐标)随着时间向右走
Math.cos(this.step += .05)则为上图对应的数轴(Y坐标),会随着step的增加,呈现1到-1的抛物线变换,正负值使得velX有左右方向
stepSize为步长,因为一帧有几十毫秒,很快,所以这个值为小于1/30的一个随机数
x为雪花的X坐标位置,就会在界面左右摆动
最后附上本demo的其他代码
HTML代码
1 2 3 4 |
<a id="start-snowFall" class="button button-border-action button-pill">开始</a> <a id="stop-snowFall" class="button button-border-action button-pill">停止</a> <a id="pause-snowFall" class="button button-border-action button-pill">暂停</a> <a id="resume-snowFall" class="button button-border-action button-pill">恢复</a> |
CSS代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
<style> *{margin:0; padding: 0;} body{background: #333;} .button { -webkit-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.15); -moz-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.15); box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.5), 0px 1px 2px rgba(0, 0, 0, 0.15); background-color: #eeeeee; background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #fbfbfb), color-stop(100%, #e1e1e1)); background: -webkit-linear-gradient(top, #fbfbfb, #e1e1e1); background: -moz-linear-gradient(top, #fbfbfb, #e1e1e1); background: -o-linear-gradient(top, #fbfbfb, #e1e1e1); background: linear-gradient(top, #fbfbfb, #e1e1e1); display: -moz-inline-stack; display: inline-block; vertical-align: middle; zoom: 1; border: 1px solid #d4d4d4; height: 32px; line-height: 32px; padding: 0px 25.6px; font-weight: 300; font-size: 14px; font-family: "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; color: #666666; text-shadow: 0 1px 1px white; margin: 0; text-decoration: none; text-align: center; } a{ cursor: pointer; color: #00A1CB; } .button-pill { -webkit-border-radius: 50px; -moz-border-radius: 50px; -ms-border-radius: 50px; -o-border-radius: 50px; border-radius: 50px; } .button-border-action { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; -webkit-transition-property: all; -moz-transition-property: all; -o-transition-property: all; transition-property: all; -webkit-transition-duration: 0.3s; -moz-transition-duration: 0.3s; -o-transition-duration: 0.3s; transition-duration: 0.3s; color: #00a1cb; border: 2px solid #00a1cb; background: none; text-shadow: none; } .button-border-action:hover { background: none; color: #00c9fe; border: 2px solid #00c9fe; } </style> |
如有有什么更好的方案,欢迎交流
请问网页显示程序用的什么插件?
什么网页显示程序?
网站的平滑滚动不是很爽啊。
那还是换回普通的滚动咯?现在的滚动条是div,那我用CSS3模仿个吧
就是滚动的时候还有一点缓冲的这个有点不适应。