最近突然对canvas动画感兴趣,而后就心血来潮的看了一些文章,事先声明,部分原创,我只是代码的搬运工。我先上一下截图,而后再说下个人想法。css
【个人想法】:其实我想作网上特别多的那种,当输入密码的时候用手捂住眼睛那种,可是原谅我戏精本色,我想作成那种当输入密码的时候,用木槌敲击小黄人或者闪电劈晕小黄人的那种特效,最好是配上音效。。。可是实现效果都不太好,马马虎虎能用不过不完美就对了,因为达到了学习目的以及时间缘由,就不继续往下作了,之后有时间可能会作,或者各位CSS大牛帮我完成,完成优秀的特效后记得把连接甩我一脸~万分感谢。代码在下方---html
仍是那句话,我只是代码的搬运工,首先是雪花飘落的场景。参考文章点击这里 —— canvas实现雪花飘落,文章上来就贴了代码,直接就能用,可是没有任何注释,既然是初探canvas和动画,讲道理一点注释都没有,真心看不太懂,因此我在这里把代码来解析一下。效果以下:前端
不吹不黑,我的以为个人审美仍是挺不错的,这种下雪的特效须要配一个高对比度的背景图,不然你背景色就是浅色冬天,而后飘白色雪花,其实感受看不太清楚,因此找了好久,发现这张动漫圣诞背景图,时间点在晚上,配合灯光飘着雪花,仍是很清晰的~css3
// 存储全部的雪花
const snows = [];
// 下落的加速度
const G = 0.015;
// 60是人眼所能见到流畅动画的最小阈值
const fps = 60;
// 速度上限,避免速度过快
const SPEED_LIMIT_X = 1;
const SPEED_LIMIT_Y = 1;
复制代码
/**
* 雪花对象
* @param {x} x
* @param {y} y
* @param {半径或长宽} radius
*/
function Snow(x, y, radius) {
this.x = x;
this.y = y;
// 若是是圆形就是半径,不然就是长宽相同的正方形
this.radius = radius;
// x方向的移动速度,能够向左也能够向右,范围在[-1, 1]
this.speed_x = 0;
// y方向的移动速度,只能向下,最快为1
this.speed_y = 0;
// 雪花自身旋转的角度
this.deg = 0;
// x方向,下落速度参数,飘落效果 > 0向左飘; < 0 向右飘
this.ax = Math.random() < 0.5 ? 0.005 : -0.005;
}
复制代码
// 绘制新雪花,x位置为随机数,y为顶部0,半径为随机数随机生成大小不一的雪花
new Snow(Math.random() * W, 0, Math.random() * 15 + 5);
复制代码
// 绘制雪花
Snow.prototype.draw = function () {
// 获取半径宽高
const radius = this.radius;
// 保存画布的当前状态,由于下面用到了变换坐标和旋转画布
ctx.save();
/**
* 下面这两句变化也挺重要的,由于旋转是按照画布原点进行的
* 所以,若是想让雪花旋转明显,就须要将画布坐标移动到雪花的坐标点
* 若是不加上坐标转换,那么全部雪花都在左上角也就是坐标原点旋转,x, y也不会变,没有飘落效果
*/
// 将画布的坐标原点移动到(x, y)的位置,canvas默认是(0, 0)
ctx.translate(this.x, this.y);
// 将画布顺时针旋转的角度
ctx.rotate(this.deg * Math.PI / 180);
// 绘制雪花图像,由于画布坐标移动到了(x, y),因此从0,0开始就是(-radius, radius)
ctx.drawImage(snowImage, -radius, -radius, radius * 2 , radius * 2);
// 恢复canvas旋转、translate等操做的状态,通常与save配合使用就是恢复到上一个save的状态
// 若是不恢复上一个状态的话,话不旋转角度坐标都没变化,也就不会出现动画效果,必须恢复
ctx.restore();
}
复制代码
// 更新雪花位置
Snow.prototype.update = function () {
// 雪花自身旋转的角度增值
const deltaDeg = Math.random() * 0.6 + 0.2;
// 不断变化x方向的移动速度
this.speed_x += this.ax;
// x向左或者向右速度过大的时候改变方向
if (this.speed_x >= SPEED_LIMIT_X || this.speed_x <= -SPEED_LIMIT_X) {
this.ax *= -1;
}
// 雪花下落速度,最高是1
if (this.speed_y < SPEED_LIMIT_Y) {
// 雪花下落速度不断增长
this.speed_y += G;
}
// 角度不断变化
this.deg += deltaDeg;
// x坐标不断变化
this.x += this.speed_x;
// y坐标不断变化
this.y += this.speed_y;
}
复制代码
/**
* 主循环函数, 生成雪花以及绘制更新雪花位置
*/
function loop() {
// 擦除当前画布内容,不然原有的雪花不会消失,新绘制的雪花不断覆盖看起来会像一条雪花白色实线在降低
ctx.clearRect(0, 0, W, H);
// 两个雪花之间的时间差,不能生成的太快,要否则就成了鹅毛大雪了^_^
const now = Date.now();
// 距离上一次绘制的时间差
deltaTime = now - lastTime;
// 重置结束时间
lastTime = now;
// 时间控制器,当timer > snowLevelTime的时候,才增长雪花,不然不增长雪花
timer += deltaTime;
/**
* 不加控制的话雪花会特别多, 150~300之间都合适
*/
if (timer > snowLevelTime) {
snows.push(
// 绘制新雪花,x位置为随机数,y为顶部0,半径为随机数随机生成大小不一的雪花
new Snow(Math.random() * W, 0, Math.random() * 15 + 5)
);
timer %= snowLevelTime;
}
const length = snows.length;
snows.map(function (s, i) {
s.update();
s.draw();
if (s.y >= H) {
snows.splice(i, 1);
}
});
// 避免失真,浏览器页面每次重绘以前调用
requestAnimationFrame(loop);
}
复制代码
我只是代码的搬运工,这里有另外一个大神写了一篇很是详细的文章,CSS3手绘小黄人,我只是将代码进行了适配的扩展,让代码在大部分屏幕分辨率下均可以使用。这里就不写过程了,文章里写的真的挺清楚的~git
眩晕效果其实我也想找现成的代码,我喜欢站在巨人的肩膀上,可是奈何找不到,连图片都找不到,最后没办法了,本身写吧,就简单的实现了下眩晕效果,至于眩晕的旋转动画,下面会简单介绍:github
/* CSS */
.circle-container {
position: relative;
width: 45px;
height: 45px;
transform: rotate(180deg);
}
.one-circle {
position: absolute;
width: 45px;
height: 45px;
background-color: #fff;
border-left: 3px solid #333;
border-top: 3px solid #333;
border-radius: 50%;
}
.two-circle {
position: absolute;
top: 9px;
width: 34px;
height: 34px;
background-color: #fff;
border-right: 3px solid #333;
border-bottom: 3px solid #333;
border-radius: 50%;
}
.three-circle {
position: absolute;
bottom: 10px;
left: 8px;
width: 23px;
height: 23px;
background-color: #fff;
border-top: 3px solid #333;
border-left: 3px solid #333;
border-radius: 50%;
}
.four-circle {
position: absolute;
bottom: 0px;
right: 6px;
width: 13px;
height: 13px;
background-color: #fff;
border-bottom: 3px solid #333;
border-right: 3px solid #333;
border-radius: 50%;
}
/* Html */
<div class='circle-container'>
<div class="one-circle">
<div class="two-circle">
<div class="three-circle">
<div class="four-circle"></div>
</div>
</div>
</div>
</div>
复制代码
不管是代码仍是实现都挺简单的,可是奈何是原创,仍是写出来吧,O(∩_∩)O哈哈~web
隔行如隔山啊,才发现即便是前端,也有如此多的分支,确实像张鑫旭老师自我介绍那样,前段偏前工程师,我却是以为偏前偏后偏业务每一个方向都值得深刻研究啊,不过我这个阶段仍是大杂烩一下吧。既然是对动画感兴趣,用完canvas了,也就想了解了解其余几种实现方法了,顺便简单学习一下,下面是学体会。canvas
最原始的方法就是使用window.setTimout()或者window.setInterval()经过不断更新元素的状态位置等来实现动画,前提是画面的更新频率要达到每秒60次才能让肉眼看到流畅的动画效果。浏览器
这个就不贴代码了,说实话,应该没有人在用这种方式实现动画了,若是有,为你的网页性能所担心~bash
transtion能够理解为过渡,两点之间,连续性的变化。transform理解为变换,平移、旋转等是经常使用的,transform + transition结合起来,也就是 过渡 + 变换就会造成简单的动画。 transtion实现动画的规则是须要定义对应的属性,好比我想让宽度发生变化,那么就定义属性为width,变化持续时间,也就是多久完成变换,最后在指定事件中改变定义的属性值便可。
// 实现鼠标放到篮球上篮球放大
.ball {
width: 128px;
height: 128px;
transition-property: width,height;
transition-duration: 2s;
-moz-transition-property: width,height; /* Firefox 4 */
-moz-transition-duration: 2s; /* Firefox 4 */
-webkit-transition-property: width,height; /* Safari and Chrome */
-webkit-transition-duration: 2s; /* Safari and Chrome */
-o-transition-property: width,height; /* Opera */
-o-transition-duration: 2s; /* Opera */
}
.ball:hover {
width: 256px;
height: 256px;
}
复制代码
与transition和transform不同,animation就能够直接理解为动画,我的以为功能也要更强大一些。 如上面说的那样,animation就是CSS为动画而出的,基本能够完成大部分简易的动画效果,他须要定义一个动画特效名,而后经过keyframe定义特效效果。下面我简单实现了一个篮球自由落体的效果,很粗糙,只是一个简单的demo:
.ball-animation {
position: absolute;
width: 128px;
height: 128px;
transform: rotate(0);
animation: ball-drop 6s linear;
animation-fill-mode: forwards;
-webkit-animation: ball-drop 6s linear;
-webkit-transform: rotate(0);
-webkit-animation-fill-mode: forwards;
}
@keyframes ball-drop {
0% {
transform: rotate(0);
top: 0
}
10% {
transform: rotate(60);
top: calc(var(--height) * 1px );
}
20% {
transform: rotate(120);
top: 40px;
}
30% {
transform: rotate(240);
top: calc(var(--height) * 1px );
}
40% {
transform: rotate(300);
top: 80px;
}
50% {
transform: rotate(360);
top: calc(var(--height) * 1px );
}
60% {
transform: rotate(60);
top: 160px;
}
70% {
transform: rotate(120);
top: calc(var(--height) * 1px );
}
80% {
transform: rotate(180);
top: 380px;
}
90% {
transform: rotate(240);
top: calc((var(--height) - 20) * 1px );
}
100% {
transform: rotate(260);
top: calc(var(--height) * 1px );
}
}
复制代码
虽然实现的很粗糙,可是我仍是写了影子之类的东西,因此也算是走心,哈哈。详细的介绍我就很少说了,我以为去看张鑫旭老师的文章就能够了,浅显易懂,传送门
由于使用canvas实现动画最重要的就是这个API了,因此放到最后来讲。MDN里关于它有这么个高亮注意:若您想要在下次重绘时产生另外一个动画画面,您的回调例程必须调用 requestAnimationFrame()
。因此也就是说,若是想要不断地重绘产生动画效果必须调用这个API。
window.requestAnimationFrame() 方法告诉浏览器您但愿执行动画并请求浏览器在下一次重绘以前调用指定的函数来更新动画。该方法使用一个回调函数做为参数,这个回调函数会在浏览器重绘以前调用。 通常来讲,人眼若是想看到流畅的动画,至少每秒更新60次,固然你也能够控制想要刷新的次数,同时,requestAnimationFrame()会返回一个整形ID,经过这个ID你能够调用cancleAnimationFrame(ID)来中止动画经过下面的代码:
let timeoutId;
let animateId;
const fps = 20; // 好比我每秒就想刷新20次
window.requestAnimationFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
timeoutId = setTimeout(callback, 1000 / fps);
}
})();
}
function loop() {
// 执行动画代码
animateId = requestAnimaitonFrame(loop);
}
// 中止动画,先中止timeout再中止requestAnimationFrame
clearTimeout(timeoutId);
cancleAnimationFrame(animateId);
复制代码
一样是js实现动画效果,因为这个API是专门用来在浏览器实现动画的,因此浏览器对其也进行了优化,相比setTimeout和setInterval的形式来讲,requestAnimationFrame()性能更好,在大多数浏览器里,当运行在后台标签页或者隐藏的 里时,requestAnimationFrame() 会暂停调用以提高性能和电池寿命。
其实不用比也知道,同一个动画效果,用纯CSS3实现和用requestAnimationFrame这种js实现手段,确定CSS3的性能要高一些的。下面是我作的同一个动效,两者渲染时间对比: animation:
requestAnimationFrame
好吧,我实际上是个假测试,我以为首先我没有控制速度一致性,其次,我在annimation实现的时候使用了calc函数计算位置,可能对性能也会有影响,不过在重绘时间上,animation的方法仍是要优秀一点的。
强烈声明:我只是为告终果而做比较,没有谁高谁低的意思,我以为根据场景来使用吧~
最后实现的代码有不少能够改进的地方,在这里记录下来,万一哪位大神心情好给我改了咋办,O(∩_∩)O哈哈~。
这两天简单看了看动画,感受一入前端深似海啊,目前计划仍是全部感兴趣的都涉猎一下,而后慢慢选择一个方向吧~代码地址:luffyZhou的动画Demo欢迎你们多提意见,多给STAR
只clone不star,就不够意思了哦,我会在内心诅咒你的^_^