今天看到一个非常喜欢的 H5 ,又是网易出品的!于是,我忍不住去研究了他的实现方式,有 3 个值得我们学习的地方,分别是逐帧动画,多种变换叠加的 css 动画,还有最亮的:画中画动画的实现方式,下文将分享技术实现方式。
实验环境,采用 chrome 开发者工具:
一、逐帧动画
这个 h5 ,几乎没有采用 gif 图片,大部分采用 c ss 的方式实现的逐帧动画。
比如上图,每一帧的尺寸是 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= [{ link: "./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 的实现方式,比如:
可以点击查阅。
码字不易,开启新的打赏方式:
本公众号定期更新关于
设计师、程序员发挥创意
互相融合的指南、作品。
主要技术栈:
nodejs、react native、electron
Elasticsearch
Solidity
Keras
欢迎关注,转发~
欢迎长按二维码
关注本号
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。