从灭霸的无限手套说起

lanseguhui 2019-07-01

本文不是技术文章,只是单纯记录下

最近妇联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

相关推荐