前言:花时间学习了网易的年度娱乐圈画转h5的技术实现。有些点比较难懂,故此,作个笔记。若是刚好帮助到你,棒呆
首先咱们能够浏览一下这个h5,视觉上它是由一幅画慢慢变小,而后再出现另外一幅画,特别之处就是当前画,是下一画中的一个小图,一部分。因此叫他画中画。html
思路历程:第一眼看到这个效果,个人思路就是,把全部的画起始放大n倍,而后当小图的大小恰好是屏幕宽高的时候,就是咱们的起始放大倍数,而后倍数慢慢缩小为1。可是这个面临各类问题,1.没法准确计算放大倍数。2.很难计算图片缩小时该在的位置,3.图片很模糊,后来我想,此时再用小图的大图去覆盖它,就有这种效果。这个方法感受很怪,扯犊子呢。而后...Stupid,too young too simple 我开始找他的源码
而后咱们发现它控制台的源码,发现以下:git
//主要方法 window.WeixinJSBridge && e.play()//处理微信浏览器下的音乐播放 initCanvas //初始化canvas画布 preload //加载图片 init //初始化场景 showend //整个动画结束的回调函数 touchEvent //touch事件,控制动画执行 draw //关键方法,就叫他画画吧 drawImgOversize //关键方法,就叫他画局部吧,这个后面会解释 drawImgMinisize //关键方法,就叫他画所有吧,这个后面会解释 drawImage //关键方法,canvas原生方法
先抛开整个过程当中的gif动画实现不说。而后主要来画画。看这个drawImage
方法,666。因此理解了这个方法以后,咱们的思路要转变一下,用这个实现效果的方法并非放大缩小,而是对图片的canvas处理,选取图片的局部,再放到canvas上。
github这个圈起来,考试要考,呸。。。
也就是说,作这个还须要ui爸爸妈妈们的帮助,须要知道,每张图的尺寸大小,图中的小图的位置,大小。
而后,他们已经帮咱们准备好啦 canvas
说了一堆废话,正式开始实现分析。
咱们能够看到这个图,并非自适应屏幕的,而是设定好了既定的尺寸,750x1206,因此咱们设计稿就是2倍,iPhone6的。而除了封面是750x1206,全部的原图都是1875x3015。因此,下方长按按钮距离底部还有点距离。这个目前来讲网易也没有适配iphonex的情形。api
他画图就画图,那他边画边缩是怎么作到的?
这时候要看源码中有这些个东西浏览器
this.radio this.scale = .985, this.scaleSlow = .995
起初我甚至以为这些参数是无关紧要的...
首先touchstart的时候触发的方法中,有这个requestAnimationFrame
,就是传说中一秒执行60次的猛男api。在requestAnimationFrame中不断执行draw就会不断地画,而后咱们用一个变量radio,不断减少,而后影响到drawImage的参数,说不定能够实现呢!(这里面的关系待会再说)
那为何scale是0.985呢,那0.211行不行,985给多少钱,我211给双倍啊.....微信
有这么一个计算公式,咱们须要radio从1减少,那就乘一个小数iphone
this.radio=this.radio*this.scale //若是这个计算每秒钟执行60次,那么this.radio就会变得更小 this.radio=this.radio*this.scale^60
因此这个scale
就是一个减少频度,频率是每秒60次,因此0.985的话,大概就是执行3,4s。this.radio
就会变成0.0几。尽可能知足areaW/imgw),这个是最小缩放值了
,再小就应该换图缩了。this.scaleSlow = .995就会更慢
,由于咱们注意到,当图片缩得差很少的时候,就会慢一点,由于图边的文字已经露出来了,让用户看清楚些。因此limitMax,limitMin
就是拿来干这个的,啥时候该慢一点缩了。固然,这里的值网易估计是计算器算了,而我是大概算,薛薇有点捞。函数
因此在源码的中有这些方法
(省略判断... )? i.radio = i.scaleSlow * i.radio : i.radio = i.scale * i.radio, if (省略判断){ //若是this.radio<= areaW/imgW 那就该换图了 this.index++, this.radio = 1 }
而后,对于每个场景咱们都须要画两张图,为何要两张,由于局部放大以后
太模糊了,就再画一张完整的图盖着这个区域,就ok没问题。学习
mmp?清晰图与模糊图很差理解的本身想一想,看这的描述 https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage
两脚离地了,聪明的智商又从新占领高地了
//例如,拿前两张图来分析,咱们先来画两张,其余剩下的再说是吧。。走两步,没毛病,skr [{ link: "http://static.ws.126.net/f2e/ent/ent_painting2016/images/1.jpg?1520",//① imgW: "750", imgH: "1206" }, { link: "http://static.ws.126.net/f2e/ent/ent_painting2016/images/2.jpg?1520",//② imgW: "1875", imgH: "3015", areaW: "375", areaH: "603", areaL: "1379", areaT: "103", limitMax: .3, limitMin: .2 }]
而后画两张图,再次聚焦到这个方法
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) //drawImage(①,...) //drawImage(②,...)
画封面图,封面图,长按以后,是从全屏到变小的那张,他一直很清晰,因此叫他清晰图吧。
他应该完整的显示在屏幕中。那sx, sy, sWidth, sHeight简单。
drawImage(①,0,0,1875,3015,dx, dy, dWidth, dHeight)
他画在屏幕上的位置和大小应该是怎么样的。一开始,固然是整个设定区域啦。也就是
drawImage(①,0,0,1875,3015,0, 0, 750, 1260)
他最小是多小?这个是设计稿说了算,咱们能够看到②中的四个属性,areaW,areaH,areaL,areaT,就是描述画中的小画的大小和位置,因此最小就是这样
drawImage(①,0,0,1875,3015,areaL, areaT, areaW, areaH)
可是,在缩小的过程当中呢,变化的是后面四个参数,咱们须要计算的就是后面四个参数
drawImage(①,0,0,1875,3015,距离屏幕左侧距离, 距离屏幕顶部距离, 当前图宽, 当前图高)
一样的,另外一张图呢。
画下一张图,长按以后,是从模糊到清晰的那张,因此叫他模糊图。
他应该选一部分区域显示在屏幕中。那部分区域是啥?就是清晰图的小小小版,为啥模糊?选取那么小的区域,填充在整个设定的屏幕区域,不模糊见鬼了。模糊没关系,拿上面那张清晰的完完整整盖住,就完美了
因此,一开始它怎么画?就拿一部分,完整的填充设定的屏幕区域就行
drawImage(②,图片开始选择的位置x,图片开始选择的位置y,图片选择的宽,图片选择的高,0, 0, 750, 1260)
最后呢,怎么画?整张图,完整的填充设定的屏幕区域就行
drawImage(②,0,0,1875,3015,0, 0, 750, 1260)
因此咱们须要计算的就是前面四个参数
drawImage(②,距离屏幕左侧距离,距离屏幕顶部距离,当前图宽,当前图高,0, 0, 720, 1260)
因此,如今有个问题是,我不知道我说的意思同窗们get到没,由于即便没有,我也要继续讲了。
剩下的计算问题,涉及到,几何数学,物理,生物,法学,离散,线性规划,高斯模糊...
首先咱们来计算,drawImgOversize也就是
drawImage(②,距离屏幕左侧距离,距离屏幕顶部距离,当前图宽,当前图高,0, 0, 720, 1260)
距离屏幕左侧距离咱们记为Sx,也就是图片中的那个?号。对于某一时刻,HG(③)的宽度=areaW/this.radio
咱们能够得出一个公式:
//①=areaL,AB=imgW,LK=areaW,②=①-? > ①/(AB-LK)=②/(HG-LK) > ①/(AB-LK)=②/(HG-LK)===》①/(AB-LK)=(①-?)/(HG-LK)
最后得出
//距离屏幕左侧距离: areaL-areaL/(imgW-areaW)*(areaW/this.radio-areaW) //同理距离屏幕顶部距离: areaT-areaT/(imgH-areaH)*(areaH/this.radio-areaH) //当前图宽: areaW/this.radio //当前图高: areaH/this.radio
而后同理:对于drawImgMinisize,某一时刻HG=750*this.radio
最后完整的计算值
this._drawImgOverSize( this.containerImage, imgNext.imgW, imgNext.imgH, imgNext.areaW, imgNext.areaH, imgNext.areaL, imgNext.areaT, this.radio, ) this._drawImgMinSize( this.innerImage, imgCur.imgW, imgCur.imgH, imgNext.imgW, imgNext.imgH, imgNext.areaW, imgNext.areaH, imgNext.areaL, imgNext.areaT, this.radio, ) _drawImgOverSize (i, iw, ih, aw, ah, al, at, r) { this.ctx.drawImage( i, al - (aw / r - aw) * (al / (iw - aw)), at - (ah / r - ah) * (at / (ih - ah)), aw / r, ah / r, 0, 0, 750, 1206, ); } _drawImgMinSize (i, ciw, cih, iw, ih, aw, ah, al, at, r) { this.ctx.drawImage( i, 0, 0, ciw, cih, 750 * (1 - r) * (al / (iw - aw)),//与下面是同样的值 // ((ah / r - ah) * (at / (ih - ah)) * r * 1206) / ah,//网易的以为太过算式复杂 1206 * (1 - r) * (at / (ih - ah)), 750 * r, 1206 * r, ); }
ok,就这样吧....