從滅霸的無限手套說起

vczhan發表於2019-04-30
本文不是技術文章,只是單純記錄下

最近婦聯4在熱映,先劇透兩個精彩片段。
圖片描述圖片描述

前兩天看到Google搜尋有個彩蛋,搜尋滅霸或者thanos,點選右邊的無限手套觸發彩蛋,打個響指,消滅一半的搜尋結果條目,消失特效類似電影裡的。
圖片描述

首先分析下這個彩蛋主要包括

  1. 點選手套動畫效果
  2. 消失的搜尋條目的粒子效果

接下來是從以下方面著手

  1. html頁面
  2. DOMcanvas
  3. 粒子效果
  4. 其他包括音效、頁面平滑滾動等

html頁面(扒網頁)

首先排除扒Google搜尋頁面,因為伺服器用的是國內阿里雲訪問不了。

然後就打算扒百度的搜尋頁,用的是PHP程式,我知道的能夠獲取頁面程式碼的有file_get_contentcURL函式,雖然拿到了頁面程式碼,但是隻要搜尋結果那些DOM的話用正則比較麻煩,搜了下找到phpQuery庫,它能像jQuery操作那樣拿到指定DOM,和Node.js的cheerio包類似。但是百度的這個頁面樣式類是動態的,還要把整個style內容也輸出,而且很多圖片大概是經過了什麼處理,沒許可權顯示不了,遂放棄。

接著扒鬥魚的直播列表頁,返回一堆亂碼,實力告退了。最後選擇了相似的企鵝電競直播列表頁,頁面算是搞定了。

DOM轉canvas

前端有html2canvasdom-to-image兩個庫可以把頁面指定元素轉化為畫布或圖片,html2canvas比較有名些,早期我也是用這個庫做前端截圖功能(https://imusic.github.io/clip/),但是它對CSS3的處理並不好,後來我發現了dom-to-image這個庫,它對CSS3的處理就比較好了,而且體積更小,所以又用這個庫替換了(https://demo.vczhan.com/clip/)。
圖片描述

不過因為要轉化的內容裡有跨域的圖片,canvas對此做了限制,我們需要對圖片做代理處理。dom-to-image這個庫並沒有提供相關的代理外掛,最後還是用html2canvas這個庫。頁面沒有複雜的元素,並且這個庫近來做了更新,對CSS3支援好了些,作者還提供了兩種語言的代理,分別是Python版本的和Node.js版本的,不過我選擇了其他人寫的PHP版本。前端只要配置相關引數就可以。伺服器端則會在檔案目錄下新建cache目錄存放圖片並返回給前端渲染到畫布上。(不知能否改成不儲存圖片檔案而是改成輸出base64或者blob資料)

html2canvas(node, {
  proxy: 'html2canvasproxy.php'
}).then(canvas => {
  // do stuff
})

粒子效果

粒子效果比較難的部分是怎麼調整各個引數到合適的值還要保證動畫不卡。其實js計算過程並不會讓動畫卡頓,主要瓶頸在渲染階段。

渲染部分原來用遍歷粒子直接繪製,但因為粒子較多,動畫看起來有點卡。

render() {
  context.clearRect(0, 0, sw, sh)

  let particles = this.particles

  for (let i = 0, particle; particle = particles[i++];) {
    if (particle.state === 'dead') continue

    context.save()
    context.translate(particle.x, particle.y)

    context.fillStyle = particle.color
    context.globalAlpha = particle.alpha
    context.beginPath()
    context.fillRect(0, 0, 1, 1)
    context.restore()
  }
}

後來改成每次渲染時,先得到空白畫布的影像資料,然後遍歷粒子,給影像資料對應的位置加上rgba,最後將影像資料放回畫布。

render() {
  // context.clearRect(0, 0, sw, sh)
  let particles = this.particles

  const imageData = context.createImageData(sw, sh)
  const buffer32 = new Uint32Array(imageData.data.buffer)

  for (let i = 0, particle; particle = particles[i++];) {
    if (particle.state === 'dead') continue

    const {x, y, color: {r, g, b}, alpha: a} = particle
    const pos = y * sw + x

    buffer32[pos] = r | (g << 8) | (b << 16) | (a << 24)
  }

  context.putImageData(imageData, 0, 0)
}

Google那個頁面是用了多個canvas,可以參考下面的粒子
https://codepen.io/birjolaxew...

其他

其他就是些細節調整,比如點選手套的過渡動畫並加上音效,過渡時間和延遲要慢慢調到合適的使動畫與音效對應。當某個DOM要消失也要加上音效,並且頁面平滑滾動,使其位於螢幕中心,可以直接用scrollIntoView這個方法。

node.scrollIntoView({behavior: 'smooth', block: 'center'})

素材都可以從Google彩蛋頁裡提取,還有其他一些細節就不逐一贅述了。

最後放上本次的demo

相關文章