網易新聞《娛樂圈畫傳》H5技術簡析

張逸達發表於2019-03-04

網易新聞採用一鏡到底(畫中畫)的動畫形式,呈現了2017年的娛樂圈熱門事件。

網易新聞《娛樂圈畫傳》H5連結

對於以內容展示為主的H5,此種形式新穎獨特,瀏覽體驗優秀,但對設計師的要求也較高。
此H5值得學習的的技術點如下:

  • 逐幀動畫代替GIF
  • 動態部分和背景分離
  • 畫中畫

一. 逐幀動畫

網易新聞《娛樂圈畫傳》H5技術簡析

利用雪碧圖將動畫幀合併到一張圖片,用css的背景定位來顯示需要的圖片部分。

通過css3的animation屬性將@keyframes動畫規則繫結的html元素。形如

<div class="people"></div>

.people { 
    animation: people_ani 1s steps(1,end) infinite; 
    background: url(images/cover_people.png) no-repeat;
    position: absolute;
    left: 20px;
    bottom: -4px;
    width: 500px;
    height: 1000px;
}
@keyframes people_ani {
    0%{background-position:0 0}
    12.5%{background-position:-500px 0}
    25%{background-position:-1000px 0}
    37.5%{background-position:-1500px 0}
    50%{background-position:0 -1000px}
    62.5%{background-position:-500px -1000px}
    75%{background-position:-1000px -1000px}
    87.5%{background-position:-1500px -1000px}
    100%{background-position:-2000px -1000px}
}
複製程式碼

這裡需要注意animation的速度曲線屬性使用了step()函式,而非常用的線性函式。
step()函式和線性函式的區別在於,前者是關鍵幀之間的直接跳躍,後者會線上性變化時加入補間動畫來使動畫更加連貫流暢。
關於step函式內關鍵字start和end的理解。start表示時間開始已執行一步,忽略第一步,end表示時間結束動畫已結束,忽略最後一步。

二. 動態部分和背景分離

網易新聞《娛樂圈畫傳》H5技術簡析

優點: 減少圖片的大小。

三. 畫中畫的實現

網易新聞《娛樂圈畫傳》H5技術簡析

畫中畫的實現,主要通過螢幕內外兩張圖片的重疊渲染,在關鍵的時候替換圖片的方式。

這裡採用了html5中canvas的drawImage方法畫圖。drawImage的三種函式形式,
drawImage(image, dx, dy)
drawImage(image, dx, dy, dw, dh)
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

sx, sy, sw, sh針對的是圖片,dx, dy, dw, dh針對的是canvas畫布區域。
sx和sy是image所要繪製的起始位置,
sw和sh是image所要繪製區域(相對image的sx和sy座標的偏移量)的寬度和高度值。
dx和dy是image在canvas中定位的座標值
dw和dh是image在canvas中即將繪製區域(相對dx和dy座標的偏移量)的寬度和高度值;

網易新聞《娛樂圈畫傳》H5技術簡析

H5將螢幕外部圖片不斷乘以一個係數逐漸縮小至手機螢幕,再將螢幕內圖片同樣基於係數縮小至外圖的關鍵位置,兩者保持重疊,達到一鏡到底的效果。

網易新聞《娛樂圈畫傳》H5技術簡析
網易新聞《娛樂圈畫傳》H5技術簡析

H5兩個關鍵繪圖方法如下:

function drawImgOversize(img, imgNextWidth, imgNextHeight, imgNextAreaWidth, imgNextAreaHeight, imgNextAreaL, imgNextAreaT, radio) {
    var sx = imgNextAreaL - (imgNextAreaWidth / radio - imgNextAreaWidth) * (imgNextAreaL / (imgNextWidth - imgNextAreaWidth)),
        sy = imgNextAreaT - (imgNextAreaHeight / radio - imgNextAreaHeight) * (imgNextAreaT / (imgNextHeight - imgNextAreaHeight)),
        sw = imgNextAreaWidth / radio,
        sh = imgNextAreaHeight / radio,
        dx = 0,
        dy = 0,
        dw = 750,
        dh = 1206;
    var c = document.querySelector(`#app`)
    var ctx = c.getContext(`2d`);
    ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);

};

function drawImgMinisize(img, imgCurWidth, imgCurHeight, imgNextWidth, imgNextHeight, imgNextAreaWidthidth, imgNextAreaHeight, imgNextAreaL, imgNextAreaT, radio) {
    var sx = 0,
        sy = 0,
        sw = imgCurWidth,
        sh = imgCurHeight,
        dx = (imgNextAreaWidth / radio - imgNextAreaWidth) * (imgNextAreaL / (imgNextWidth - imgNextAreaWidth)) * radio * 750 / imgNextAreaWidth,
        dy = (imgNextAreaH / radio - imgNextAreaH) * (imgNextAreaT / (imgNextHeight - imgNextAreaH)) * radio * 1206 / imgNextAreaH,
        dw = 750 * radio,
        dh = 1206 * radio;
    var c = document.querySelector(`#app`)
    var ctx = c.getContext(`2d`);
    ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh);
};
複製程式碼

以上H5作者的兩個方法,研究了許久,並不能清楚地理解其中的計算邏輯,故而自己重寫了其中的計算方法。

function drawInsideImg(img, imgCurWidth, imgCurHeight, imgNextWidth, imgNextHeight, imgNextAreaW, imgNextAreaH, imgNextAreaL, imgNextAreaT, radio) {
    var sx = 0,
        sy = 0,
        sw = imgCurWidth,
        sh = imgCurHeight,
        dx = (imgNextAreaL / (imgNextWidth - imgNextAreaW)) * (750 - 750 * radio),
        dy = (imgNextAreaT / (imgNextHeight - imgNextAreaH)) * (1206 - 1206 * radio),
        dw = 750 * radio,
        dh = 1206 * radio;
    this.ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)
}

function drawOutsideImg (img, imgNextWidth, imgNextHeight, imgNextAreaWidth, imgNextAreaHeight, imgNextAreaL, imgNextAreaT, radio) {
    var sx = (imgNextAreaL / (imgNextWidth - imgNextAreaWidth)) * (imgNextWidth - imgNextAreaWidth / radio),
        sy = (imgNextAreaT / (imgNextHeight - imgNextAreaHeight)) * (imgNextHeight - imgNextAreaHeight / radio),
        sw = imgNextAreaWidth / radio,
        sh = imgNextAreaHeight / radio,
        dx = 0,
        dy = 0,
        dw = 750,
        dh = 1206;
    this.ctx.drawImage(img, sx, sy, sw, sh, dx, dy, dw, dh)
}
複製程式碼

這裡有兩個恆等的量作為內外圖重疊渲染的橋樑,即
(imgNextAreaL / (imgNextWidth – imgNextAreaWidth))
(imgNextAreaT / (imgNextHeight – imgNextAreaHeight))

部分內容參考自https://mp.weixin.qq.com/s/xScwM7Z3I7wXYmZg7y9ajg

相關文章