网易新闻采用一镜到底(画中画)的动画形式,呈现了2017年的娱乐圈热门事件。
网易新闻《娱乐圈画传》H5链接
对于以内容展示为主的H5,此种形式新颖独特,浏览体验优秀,但对设计师的要求也较高。
此H5值得学习的的技术点如下:
- 逐帧动画代替GIF
- 动态部分和背景分离
- 画中画
一. 逐帧动画
利用雪碧图将动画帧合并到一张图片,用css的背景定位来显示需要的图片部分。
通过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表示时间结束动画已结束,忽略最后一步。
二. 动态部分和背景分离
优点: 减少图片的大小。
三. 画中画的实现
画中画的实现,主要通过屏幕内外两张图片的重叠渲染,在关键的时候替换图片的方式。
这里采用了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坐标的偏移量)的宽度和高度值;
H5将屏幕外部图片不断乘以一个系数逐渐缩小至手机屏幕,再将屏幕内图片同样基于系数缩小至外图的关键位置,两者保持重叠,达到一镜到底的效果。
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