乐趣区

关于动画:实现网易新闻的图中图PIP二零一七年娱乐圈画传

netease-news

实现网易新闻的图中图(PIP)《二零一七年娱乐圈画传》查看源码

点击或者扫码预览:

绘画过程剖析

下面的动图,如何实现?直观上感觉像是一个大图,但认真想想必定不对,那得多大的图~ 看了下网页构造,用了一个 canvas 来显示,应该就是把图像一帧一帧的画到 canvas 下面,比方 1 秒画 60 帧,就能达到顺滑的膨胀成果。

绘制的时候,同时存在两张图,最后的时候图 1 和屏幕重合,图 2 在屏幕外边。随着工夫的推移,图 2 和图 1 一起缓缓放大,最终图 2 和屏幕重合,图 1 膨胀到了图 2 的 PIP 地位。这时候去掉图 1,增加图 3,图 3 和图 2 一起膨胀,而后再增加图 4,始终到最初一张图。

想要实现整个过程,须要保障:

  1. 所有图、屏幕显示区域(宽度最大,长度可能有空白或者滚动条)、以及 PIP 的长宽比是统一的,这样能力在放大后完满重合
  2. 以后的图片,须要晓得下一张图的 PIP 的地位和大小,这样能力放大挪动到指定地位
  3. 个别这种画中画都是有事件程序的,所以要求图片的程序是排列好的,依照程序膨胀

绘画须要用到这个 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 在指标画布上绘制的高度

第一个参数,没啥说的,就是想要绘制的指标元素,咱们这里是图片

sxsy,想要剪裁的图片的左上角坐标,如果是0,0 就是从原图的左上角开始

sWidthsHeight,想要剪裁的图片的长宽

以上 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 函数的参数,那么 sxsysWidthsHeight这 4 个参数就是0,0, 以后图片的宽度, 以后图片的高度,也就是把整个图片都剪裁下来了,所以只须要计算以后图片在画布中的地位和长宽

察看上图,咱们假如 ABCD 是手机屏幕,也就是以后图片最后的大小和地位

EFGH 以后图片的两头态

HIJK 以后图片的最终大小和地位,也就是下一张图片的 PIP 的大小和地位

做几条辅助线,ES 平行于 HU 平行于 IB,MH 是程度的,RE、TH 是垂直的

所以通过一段时间后,有 EF=AB*reduceEH = 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-positionbackground-size:

background-position: 管制背景图片在 容器元素 中的地位,也就是图片的左上角在元素中的地位

background-size: 调整图片到指定大小,百分比绝对于 容器元素 的尺寸

以封面的 ” 长按 ” 按钮为例:

  1. 要保障在不同宽度的设施上都显示一样的比例的按钮,应用了 vw 作为图片容器的长宽单位
  2. 要保障容器中只显示出按钮,要应用 vw 去设置background-position,这样能够让这个 ” 开始 ” 两字的左上角和容器的左上角重合
  3. 雪碧图的原始大小是固定的,当初想把他适配到不同宽度的设施,须要利用 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%;

感激

感激这篇文章的指引

退出移动版