共计 3055 个字符,预计需要花费 8 分钟才能阅读完成。
netease-news
实现网易新闻的图中图(PIP)《二零一七年娱乐圈画传》查看源码
点击或者扫码预览:
绘画过程剖析
下面的动图,如何实现?直观上感觉像是一个大图,但认真想想必定不对,那得多大的图~ 看了下网页构造,用了一个 canvas 来显示,应该就是把图像一帧一帧的画到 canvas 下面,比方 1 秒画 60 帧,就能达到顺滑的膨胀成果。
绘制的时候,同时存在两张图,最后的时候图 1 和屏幕重合,图 2 在屏幕外边。随着工夫的推移,图 2 和图 1 一起缓缓放大,最终图 2 和屏幕重合,图 1 膨胀到了图 2 的 PIP 地位。这时候去掉图 1,增加图 3,图 3 和图 2 一起膨胀,而后再增加图 4,始终到最初一张图。
想要实现整个过程,须要保障:
- 所有图、屏幕显示区域(宽度最大,长度可能有空白或者滚动条)、以及 PIP 的长宽比是统一的,这样能力在放大后完满重合
- 以后的图片,须要晓得下一张图的 PIP 的地位和大小,这样能力放大挪动到指定地位
- 个别这种画中画都是有事件程序的,所以要求图片的程序是排列好的,依照程序膨胀
绘画须要用到这个 CanvasRenderingContext2D.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
函数,函数的参数比拟多,这里列出来:
参数 | Description |
---|---|
image | 绘制到 canvas 的元素,能够是图片、视频和 canvas |
sx | image 的矩形(裁剪)抉择框的左上角 X 轴坐标 |
sy | image 的矩形(裁剪)抉择框的左上角 Y 轴坐标 |
sWidth | image 的矩形(裁剪)抉择框的宽度 |
sHeight | image 的矩形(裁剪)抉择框的高度 |
dx | image 的左上角在指标画布上 X 轴坐标 |
dy | image 的左上角在指标画布上 Y 轴坐标 |
dWidth | image 在指标画布上绘制的宽度 |
dHeight | image 在指标画布上绘制的高度 |
第一个参数,没啥说的,就是想要绘制的指标元素,咱们这里是图片
sx
和 sy
,想要剪裁的图片的左上角坐标,如果是0,0
就是从原图的左上角开始
sWidth
和sHeight
,想要剪裁的图片的长宽
以上 4 个参数,互相配合,就能够从 image 上剪出原图的一部分
接下来 dx dy dWidth dHeight
是针对画布的参数,也就是说在画布上选一块中央将下面裁剪下来的图像放进去。这时候问题来了,如果裁剪的和画布上筹备放图像的宽高不一样咋办?好办,拉伸或者膨胀
计算公式
下面说到,图片是依照程序一张一张替换和膨胀的,那么怎么晓得我下一张图的 PIP 的地位和大小呢,这须要咱们提前规定好,也就是说我晓得每张图片的大小和 PIP 的大小地位(第一张封面没有)
{
src: 'cover.jpg',
width: '750',
height: '1206',
},
{
src: 'p1.jpg',
width: '1875',
height: '3015',
pipImg: {
width: '152',
height: '244',
left: '370',
top: '1068',
},
},
{
src: 'p2.jpg',
width: '1875',
height: '3015',
pipImg: {
width: '556',
height: '894',
left: '1251',
top: '1050',
},
},
咱们假如,通过一段时间后,长或者宽放大到原来的 reduce(小于 1),随着 reduce 的缓缓变小,变动也越来越大
以后图片
从封面开始,以后图片此时撑满整个屏幕,随着工夫的推移,逐步放大和挪动,最终放大挪动到下一张图片的 PIP 的大小和地位
这一过程中,整个图片都在画布中,而且是从图片的左上角开始剪裁的,联合下面的 drawImage
函数的参数,那么 sx
、sy
、sWidth
和sHeight
这 4 个参数就是0,0, 以后图片的宽度, 以后图片的高度
,也就是把整个图片都剪裁下来了,所以只须要计算以后图片在画布中的地位和长宽
察看上图,咱们假如 ABCD 是手机屏幕,也就是以后图片最后的大小和地位
EFGH 以后图片的两头态
HIJK 以后图片的最终大小和地位,也就是下一张图片的 PIP 的大小和地位
做几条辅助线,ES 平行于 HU 平行于 IB,MH 是程度的,RE、TH 是垂直的
所以通过一段时间后,有 EF=AB*reduce
和 EH = AD*reduce
先计算画布中的横坐标dx
,最后是 0,随着 reduce 变小,越来越大,最终就是 MH 的长度,也就是 PIP 的 left
ML = AR;
AR/AT=AE/AH=AS/AU;
所以 AR=AT*(AS/AU)
而 AS=AB-SB
AU = AB-HI
AB:以后图片的宽度
SB: 两头态宽度,即 AB*reduce
HI: 下一张图片的 PIP 的宽度
AT: 下一张图片的 PIP 的 left
下面是伪代码,具体的能够看看源码
下一张图片
察看下面的动图,最后 nextImage(下一张图片)的 PIP 占满了画布,而后画布缓缓扩充,最初是整个 nextImage 占满了画布
还是一样的图这里假如:ABCD 是 nextImage
EFGH 看做画布的两头态(扩充过程中),最后和 nextImage(下一张图片)的 PIP 重合,缓缓变大
HIJK 是画布的最后状态,大小就是 nextImage 的 PIP
剖析这个过程,整个过程图像都是撑满了画布的,所以 dx,dy,dWidth,dHeight
应用固定的值(0,0, 屏幕的宽, 屏幕的高)
,只计算剪裁图片的局部即可
先计算剪裁图片的横坐标 sx
,最后在 PIP 的左上角,也就是nextImage.pip.left
,最终在整张图的左上角(也就是 0),也就是 ML 为剪裁的横坐标
因为 EF 从 HI 缓缓扩充的,变化率是 reduce,所以有 reduce = HI/EF
,所以有EF = HI/reduce
,这个值随着 reduce 的放大,变得越来越大,最终达到了 AB,就完结了
接下来求 ML
ML = AR;
AR/AT=AE/AH=AS/AU
AS=AB-EF
AU=AB-HI
所以 ML=AR=AT*AS/AU=AT*(AB-EF)/(AB-HI)
AT: nextImage.PIP.left
AB: nextImage.width
EF: nextImage.PIP.width / reduce
HI: nextImage.PIP.width
MH: 和 AT 相等
同理可求 sy
,sWidth
也就是 EF
其余动画
原作的动画很多都是应用背景图片雪碧图 + 简略动画
这里介绍下 background-position
和background-size
:
background-position
: 管制背景图片在 容器元素 中的地位,也就是图片的左上角在元素中的地位
background-size
: 调整图片到指定大小,百分比绝对于 容器元素 的尺寸
以封面的 ” 长按 ” 按钮为例:
- 要保障在不同宽度的设施上都显示一样的比例的按钮,应用了
vw
作为图片容器的长宽单位 - 要保障容器中只显示出按钮,要应用
vw
去设置background-position
,这样能够让这个 ” 开始 ” 两字的左上角和容器的左上角重合 - 雪碧图的原始大小是固定的,当初想把他适配到不同宽度的设施,须要利用
background-size
将图片大小调整到绝对于容器的大小,此时须要配合background-position
一起调整
width: 15vw;
height: 14vw;
background: url(./assets/images/sprite_v2.png) no-repeat;
background-position: -41.4vw -79.45vw;
background-size: 770%;
感激
感激这篇文章的指引