本文已参与「新人创作礼」活动,一起开启掘金创作之路。 点击查看活动详情
写在前面
这个是网易娱乐自2014年起的保留节目,每到年底便会推出一部,以一镜到底
画中画
的形式展开,每一幅图都是下一幅图的局部细节,层层嵌套,如俄罗斯套娃般层层解密讲述娱乐圈的大事记。
素材分析
从效果中看,前一幅图都是下一幅图的局部细节,因此在素材设计上就要处理好,将前图的内容以一种巧妙的方式放到下一图里,从实际内容上看,大部分都是以看书
,看报
,海报
等形式,至于位置反而没有要求了。不过要把前一幅图在后一幅图里的坐标位置提供出来用于程序计算。
这种形式,只要素材足够,保障加载,素材设计的足够巧妙,是完全能够循环展示足够多的画面的。
技术实现
从朴素的情感上分析,解决任何一个问题都跟把大象放进冰箱差不多
-
打开冰箱门
-
把大象放进去
-
关上冰箱门
所以这里的思路也是这样的
-
计算此时前图在画面中的大小位置,画上去
-
计算此时后图在画面中的大小位置,画上去
-
依次处理所有的图片
说起来简单,但是有几个点还是要处理的
图片的自适应问题
这里假设设计师提供的图片尺寸是750*1334
,那么在640*1008
的尺寸上该怎么计算应该绘制的图片大小和尺寸呢?
解决方案:以最短比例边为基准,裁剪掉多余的侧边。
比如图的比例与手机屏幕的比例是稍长的,那如果以图片长度为100%,图片的宽度就不能充满屏幕,所以以图片的宽度为100%基准,以图片的中心点裁剪掉图片的顶部和底部一部分,保证图片不失真,画面效果也影响不大。
// 这段代码的意思就是如果图片的边比例与屏幕的比例相比得到的位置
var bgx = scaleW > scaleH ? (imgInitialW - scaleH * w) / 2 : 0;
var bgy = scaleW > scaleH ? 0 : (imgInitialH - scaleW * h) / 2;
var bgw = scaleW > scaleH ? imgInitialW - bgx * 2 : imgInitialW;
var bgh = scaleW > scaleH ? imgInitialH : imgInitialH - bgy * 2;
两幅图大小和位置逐渐变化的算法
以其中一段为例,分为以下几步
- 图1初始(中间的黑色框,也是手机屏幕的大小)逐渐向图1终止(图中小红色框)变化的过程
- 图2初始(外面的大黑框)逐渐向图2终止(中间的红色框)变化的过程
- 两个变化的过程要始终保持同步
- 变换完之后切换到下一段从头开始
计算逻辑
这里我并不知道网易具体是怎么实现的,我的思路是这样的
- 由前面自适应逻辑可以得到每张图的初始状态(如果不自适应就是
0, 0, w, h
),自适应的话就是上面的(bgx, bgy, bgw, bgh
) - 每张图的最终位置是可以有设计稿提供的,甚至都可以不一样大,比如第一张图最终的位置可以是(
200, 200, 64, 100
) - 通过初始状态和最终状态计算出每次渐变后的2张图要在屏幕上的大小和位置,通过
canvas
的drawImage
绘制出来,前图要后绘制,以便自动叠加在上层。
最终就能完成效果了 在线体验
图片信息
// 每一条数据都是那张图最终要在屏幕上绘制的信息
var p = [[0,0,0,0],
[552, 42, 155, 245],
[316, 478, 63, 100],
[346, 166, 110, 182],
[157, 342, 102, 138],
[144, 491, 70, 119],
[63, 332, 289, 520],
[319, 313, 106, 178],
[189, 186, 344, 585],
[505, 267, 120, 185],
[410, 216, 278, 512],
[483, 924, 95, 151],
[368, 646, 247, 424],
[115, 19, 97, 154],
[115, 28, 357, 560],
[430, 75, 165, 280],
[429, 262, 299, 524],
[90, 886, 166, 259],
[163, 660, 270, 511],
[599, 210, 116, 185],
[423, 191, 288, 504],
[465, 924, 30, 66],
[bgx, bgy, bgw, bgh]];
绘制逻辑
drawImg(img1, img2){
bufctx.clearRect(0, 0, w, h);
bufctx.drawImage(img1.img,
img1.startX,
img1.startY,
img1.imgDrawWidth,
img1.imgDrawHeight,
img1.canvasStartX,
img1.canvasStartY,
img1.canvasDrawWidth,
img1.canvasDrawHeight);
if(img2 != null){
bufctx.drawImage(img2.img,
img2.startX,
img2.startY,
img2.imgDrawWidth,
img2.imgDrawHeight,
img2.canvasStartX,
img2.canvasStartY,
img2.canvasDrawWidth,
img2.canvasDrawHeight);
}
ctx.clearRect(0, 0, w, h);
ctx.drawImage(bufcanvas, 0, 0, canvas.width, canvas.height);
},