网易新闻《娱乐圈画传》H5的动画技巧
今天看到一个非常喜欢的H5,又是网易出品的!于是,我忍不住去研究了他的实现方式,有3个值得我们学习的地方,分别是逐帧动画,多种变换叠加的css动画,还有最亮的:画中画动画的实现方式,下文将分享技术实现方式。
实验环境,采用chrome开发者工具:
一、逐帧动画
这个h5,几乎没有采用gif图片,大部分采用css的方式实现的逐帧动画。
比如上图,每一帧的尺寸是500px 1000px,共有8帧,存成雪碧图的形式。
CSS雪碧 即CSS Sprite,也有人叫它CSS精灵,是一种CSS图像合并技术,该方法是将小图标和背景图像合并到一张图片上,然后利用css的背景定位来显示需要显示的图片部分。好处是减少你的网站的HTTP请求数量。
重点在于这句代码 animation: people_ani 1s steps(1,end) infinite;
其他如下:
<div class="people"></div>
.people {
animation: people_ani 1s steps(1,end) infinite;
background: url(images/cover_people.png) no-repeat;
position: absolute;
left: 20px;
bottom: -4px;
width: 500px;
height: 1000px;
}
@keyframes people_ani {
0%{background-position:0 0}
12.5%{background-position:-500px 0}
25%{background-position:-1000px 0}
37.5%{background-position:-1500px 0}
50%{background-position:0 -1000px}
62.5%{background-position:-500px -1000px}
75%{background-position:-1000px -1000px}
87.5%{background-position:-1500px -1000px}
100%{background-position:-2000px -1000px}
}
二、多种变换叠加的动画
首页的海浪波动动画,在x轴跟y轴都同时运动,采用了伪元素的这种方法。
把x轴的变化写在元素本身,y轴的变化写在before里。
代码如下:
<div class="water1"></div>
.water1 {
position: absolute;
left: 470px;
top: 760px;
width: 350px;
height: 400px;
}
.water1 {
animation: water1 3.0s ease 1s infinite;
}
@keyframes water1{
0%{transform:translate(0,0)}
50%{transform:translate(-60px,0)}
100%{transform:translate(0,0)}
}
.water1:before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: url(images/sprite_v2.png) no-repeat 0 -690px;
}
.water1:before {
animation: water2 3.0s ease 2s infinite;
}
@keyframes water2{
0{transform:translate(0,0)}
50%{transform:translate(0,-50px)}
00%{transform:translate(0,0)}
}
三、动态部分与背景分离:
这种就不多说了,这样可以减少图片文件大小。
四、画中画的实现:
整个h5最吸引人的就是画中画的动画形式,下图是前后2帧的图片:
前后2帧的变化关系如下图所示:
这里都是采用canvas逐帧绘制而成的,比css方案有个好处,就是同一时间绘制显示的内容,避免了采用css分别变换带来的不确定性。
我们尝试下,这里只选取了其中2帧作为演示。
html代码:
<canvas id="app" width="750" height="1206"></canvas>
<div class="collection">
<img src="./cover_v2.jpg">
<img src="./p1.jpg"">
<img src="./p2.jpg">
</div>
把canvas撑满屏幕,div class='collection'隐藏。
设一个初始化参数:
js代码:
domList=document.querySelector('.collection').children;
imgList= [{
li
47 31377 47 14986 0 0 1280 0 0:00:24 0:00:11 0:00:13 3143nk: "./cover_v2.jpg",
imgW: "750",
imgH: "1206"
}, {
link: "./p1.jpg",
imgW: "1875",
imgH: "3015",
areaW: "152",
areaH: "244",
areaL: "370",
areaT: "1068",
gif: "p1"
}, {
link: "./p2.jpg",
imgW: "1875",
imgH: "3015",
areaW: "556",
areaH: "894",
areaL: "1251",
areaT: "1050"
}];
index=1;
radio=1;
imgNext ={
link: "./p2.jpg",
imgW: "1875",
imgH: "3015",
areaW: "556",
areaH: "894",
areaL: "1251",
areaT: "1050"
};
imgCur ={
link: "./p1.jpg",
imgW: "1875",
imgH: "3015",
areaW: "152",
areaH: "244",
areaL: "370",
areaT: "1068",
gif: "p1"
};
//p2.jpg
img_oversize = domList[index + 1];
//p1.jpg
img_minisize = domList[index];
当长按start按钮时,开始绘制逐帧动画,是通过radio不断乘于一个缩放系数达到目的的。
radio=0.99*radio
重复这一过程的代码如下:
radio=0.99*radio
drawImgOversize(img_oversize, imgNext.imgW, imgNext.imgH, imgNext.areaW, imgNext.areaH, imgNext.areaL, imgNext.areaT, radio)
上面这个代码表示,把后面一帧逐渐缩小至手机屏幕,原理如下图:
再把前面一帧,逐渐缩小,成为画中画,原理如下图:
代码:
radio=0.99*radio
drawImgMinisize(img_minisize, imgCur.imgW, imgCur.imgH, imgNext.imgW, imgNext.imgH, imgNext.areaW, imgNext.areaH, imgNext.areaL, imgNext.areaT, radio)
为了实验方便,我把radio跟两个方法都拆开来了,实际使用的时候,只需调用一次radio=0.99*radio即可。
drawImgOversize与drawImgMinisize有兴趣再深入理解下哦,代码如下:
function drawImgOversize(img, imgNextWidth,imgNextHeight,imgNextAreaWidth, imgNextAreaHeight, imgNextAreaL, imgNextAreaT, radio) {
var sx = imgNextAreaL - (imgNextAreaWidth / radio - imgNextAreaWidth) * (imgNextAreaL / (imgNextWidth - imgNextAreaWidth)),
sy = imgNextAreaT - (imgNextAreaHeight / radio - imgNextAreaHeight) * (imgNextAreaT / (imgNextHeight - imgNextAreaHeight)),
swidth = imgNextAreaWidth / radio,
sheight = imgNextAreaHeight / radio,
x = 0,
y = 0,
width = 750,
height = 1206;
var c=document.querySelector('#app')
var ctx=c.getContext('2d');
ctx.drawImage(img, sx, sy, swidth, sheight, x, y, width, height);
};
function drawImgMinisize(img, imgCurWidth, imgCurHeight, imgNextWidth, imgNextHeight, imgNextAreaW, imgNextAreaH, imgNextAreaL, imgNextAreaT, radio) {
var c=document.querySelector('#app')
var ctx=c.getContext('2d');
var sx = 0,
sy = 0,
swidth = imgCurWidth,
sheight = imgCurHeight,
x = (imgNextAreaW / radio - imgNextAreaW) * (imgNextAreaL / (imgNextWidth - imgNextAreaW)) * radio * 750 / imgNextAreaW,
y = (imgNextAreaH / radio - imgNextAreaH) * (imgNextAreaT / (imgNextHeight - imgNextAreaH)) * radio * 1206 / imgNextAreaH,
width = 750 * radio,
height = 1206 * radio;
ctx.drawImage(img, sx, sy, swidth, sheight, x, y, width, height);
};
以上就是动画技术的核心内容,以供学习研究之用。
附上h5的地址:
http://ent.163.com/special/entphotos2017/
ps:
之前我也研究另外2个喜欢的h5的实现方式,比如:
全民刷军装背后的AI技术及简单实现
从网易《初心》H5里学到的一些
可以点击查阅。
码字不易,开启新的打赏方式:
本公众号定期更新关于
设计师、程序员发挥创意
互相融合的指南、作品。
主要技术栈:
nodejs、react native、electron
Elasticsearch
Solidity
Keras
欢迎关注,转发~
欢迎长按二维码
关注本号