前言:花時間學習了網易的年度娛樂圈畫轉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,就這樣吧....