网易新闻采用一镜到底(画中画)的动画形式,呈现了2017年的娱乐圈热门事件。
对于以内容展示为主的H5,此种形式新颖独特,浏览体验优秀,但对设计师的要求也较高。
此H5值得学习的的技术点如下:
- 逐帧动画代替GIF
- 动态部分和背景分离
- 画中画
一. 逐帧动画
![](http://www.mk2048.com/web_upload/blog_imgs/10/https___user-gold-cdn-xitu-io_2018_6_13_163f8a8c74e85fe0_w-521_h-474_f-jpeg_s-97407.gif)
通过css3的animation属性将@keyframes动画规则绑定的html元素。形如
<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}
}
这里需要注意animation的速度曲线属性使用了step()函数,而非常用的线性函数。
step()函数和线性函数的区别在于,前者是关键帧之间的直接跳跃,后者会在线性变化时加入补间动画来使动画更加连贯流畅。
关于step函数内关键字start和end的理解。start表示时间开始已执行一步,忽略第一步,end表示时间结束动画已结束,忽略最后一步。
二. 动态部分和背景分离
![](http://www.mk2048.com/web_upload/blog_imgs/10/https___user-gold-cdn-xitu-io_2018_6_13_163f8b28c9785866_w-780_h-649_f-webp_s-41190.gif)
三. 画中画的实现
![](http://www.mk2048.com/web_upload/blog_imgs/10/https___user-gold-cdn-xitu-io_2018_6_13_163f8b5908ed6d67_w-826_h-617_f-webp_s-46120.gif)
这里采用了html5中canvas的drawImage方法画图。drawImage的三种函数形式,
drawImage(image, dx, dy)
drawImage(image, dx, dy, dw, dh)
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
sx, sy, sw, sh针对的是图片,dx, dy, dw, dh针对的是canvas画布区域。
sx和sy是image所要绘制的起始位置,
sw和sh是image所要绘制区域(相对image的sx和sy坐标的偏移量)的宽度和高度值。
dx和dy是image在canvas中定位的坐标值
dw和dh是image在canvas中即将绘制区域(相对dx和dy坐标的偏移量)的宽度和高度值;
![](http://www.mk2048.com/web_upload/blog_imgs/10/https___user-gold-cdn-xitu-io_2018_6_13_163f8c253e083940_w-360_h-360_f-png_s-19674.gif)
H5将屏幕外部图片不断乘以一个系数逐渐缩小至手机屏幕,再将屏幕内图片同样基于系数缩小至外图的关键位置,两者保持重叠,达到一镜到底的效果。
![](http://www.mk2048.com/web_upload/blog_imgs/10/https___user-gold-cdn-xitu-io_2018_6_14_163fc32ebe4c20d2_w-776_h-581_f-webp_s-22224.gif)
![](http://www.mk2048.com/web_upload/blog_imgs/10/https___user-gold-cdn-xitu-io_2018_6_14_163fc3334a66ddda_w-662_h-486_f-webp_s-16574.gif)
H5两个关键绘图方法如下:
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)),
sw = imgNextAreaWidth / radio,
sh = imgNextAreaHeight / radio,
dx = 0,
dy = 0,
dw = 750,
dh = 1206;
var c = document.querySelector('#app')
var ctx = c.getContext('2d');
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
};
function drawImgMinisize(img, imgCurWidth, imgCurHeight, imgNextWidth, imgNextHeight, imgNextAreaWidthidth, imgNextAreaHeight, imgNextAreaL, imgNextAreaT, radio) {
var sx = 0,
sy = 0,
sw = imgCurWidth,
sh = imgCurHeight,
dx = (imgNextAreaWidth / radio - imgNextAreaWidth) * (imgNextAreaL / (imgNextWidth - imgNextAreaWidth)) * radio * 750 / imgNextAreaWidth,
dy = (imgNextAreaH / radio - imgNextAreaH) * (imgNextAreaT / (imgNextHeight - imgNextAreaH)) * radio * 1206 / imgNextAreaH,
dw = 750 * radio,
dh = 1206 * radio;
var c = document.querySelector('#app')
var ctx = c.getContext('2d');
ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
};
以上H5作者的两个方法,研究了许久,并不能清楚地理解其中的计算逻辑,故而自己重写了其中的计算方法。
function drawInsideImg(img, imgCurWidth, imgCurHeight, imgNextWidth, imgNextHeight, imgNextAreaW, imgNextAreaH, imgNextAreaL, imgNextAreaT, radio) {
var sx = 0,
sy = 0,
sw = imgCurWidth,
sh = imgCurHeight,
dx = (imgNextAreaL / (imgNextWidth - imgNextAreaW)) * (750 - 750 * radio),
dy = (imgNextAreaT / (imgNextHeight - imgNextAreaH)) * (1206 - 1206 * radio),
dw = 750 * radio,
dh = 1206 * radio;
this.ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)
}
function drawOutsideImg (img, imgNextWidth, imgNextHeight, imgNextAreaWidth, imgNextAreaHeight, imgNextAreaL, imgNextAreaT, radio) {
var sx = (imgNextAreaL / (imgNextWidth - imgNextAreaWidth)) * (imgNextWidth - imgNextAreaWidth / radio),
sy = (imgNextAreaT / (imgNextHeight - imgNextAreaHeight)) * (imgNextHeight - imgNextAreaHeight / radio),
sw = imgNextAreaWidth / radio,
sh = imgNextAreaHeight / radio,
dx = 0,
dy = 0,
dw = 750,
dh = 1206;
this.ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)
}
这里有两个恒等的量作为内外图重叠渲染的桥梁,即
(imgNextAreaL / (imgNextWidth - imgNextAreaWidth))
(imgNextAreaT / (imgNextHeight - imgNextAreaHeight))
部分内容参考自https://mp.weixin.qq.com/s/xScwM7Z3I7wXYmZg7y9ajg
更多专业前端知识,请上 【猿2048】www.mk2048.com