本文不是技术文章,只是单纯记录下
最近妇联 4 在热映,先剧透两个精彩片段。
前两天看到 Google 搜索有个彩蛋,搜索 灭霸
或者thanos
,点击右边的无限手套触发彩蛋,打个响指,消灭一半的搜索结果条目,消失特效类似电影里的。
首先分析下这个彩蛋主要包括
- 点击手套动画效果
- 消失的搜索条目的粒子效果
接下来是从以下方面着手
- html 页面
-
DOM
转canvas
- 粒子效果
- 其他包括音效、页面平滑滚动等
html 页面(扒网页)
首先排除扒 Google 搜索页面,因为服务器用的是国内阿里云访问不了。
然后就打算扒百度的搜索页,用的是 PHP
程序,我知道的能够获取页面代码的有 file_get_content
和cURL
函数,虽然拿到了页面代码,但是只要搜索结果那些 DOM 的话用正则比较麻烦,搜了下找到 phpQuery 库,它能像 jQuery
操作那样拿到指定 DOM,和 Node.js 的 cheerio
包类似。但是百度的这个页面样式类是动态的,还要把整个 style 内容也输出,而且很多图片大概是经过了什么处理,没权限显示不了,遂放弃。
接着扒斗鱼的直播列表页,返回一堆乱码,实力告退了。最后选择了相似的企鹅电竞直播列表页,页面算是搞定了。
DOM 转 canvas
前端有 html2canvas 和 dom-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