看过复仇者联盟的都知道,灭霸做为计划生育政策的坚决支持者和执行者,一个响指清除了宇宙中二分之一的生命。电影中被清除的生命灰飞烟灭的镜头非常酷炫,因此在复联4上映后,那个不存在的网站google,推出了一个彩蛋,若是在搜索框搜索灭霸,会出现一个手套的按钮,点击后会让网页搜索结果消失一半。 javascript
咱们这里只用一个图片元素,HTML结构以下html
<body>
<div class="content">
<div id="image">
<!-- 图片为网络地址才可在本地经过直接打开html调试 -->
<img src="https://i.loli.net/2019/05/06/5ccffa469ec52.jpg" width="400" />
</div>
</div>
</body>
复制代码
const imageBox = document.querySelector('#image')
html2canvas(imageBox, {
backgroundColor: 'transparent' //背景设置为透明
}).then(canvas=>{
//处理canvas的代码(注意.then这种写法只有在新版本的html2canvas可用)
});
复制代码
//处理canvas的代码
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(
0,
0,
canvas.width,
canvas.height
);
const pixelArr = imageData.data; //像素信息
复制代码
图像信息被存储在一个Uint8ClampedArray(8位无符号整型固定数组)中,这个数组中的值为0到255的整数,按图片中像素从左到右从上到下的顺序,每4个数字表示一个像素信息,这4个数字分别表示rgba(r-红色,g-绿,b-蓝色,a-透明度)的四个值。 好比这样一个图片java
[0,0,0,255,255,255,255,255,255,0,0,255,0,255,0,255]
复制代码
//建立一个和图像信息数组长度相同的数组并填充0(至关于一个和原图像尺寸相同的透明图像)
const data = pixelArr.slice(0).fill(0);
//建立透明图像数组的个数,不能过小也不能太大。
const canvasCount = 30;
//将透明图像数组复制多个
const imageDataArray = Array.from({ length: canvasCount }, () =>
data.slice(0)
);
//将原图像上的像素信息随机分配进不一样的透明图象上,位置保持不变
for (let i = 0; i < pixelArr.length; i += 4) {
const p = Math.floor((i / pixelArr.length) * canvasCount);
//a为随机选出要放入像素信息的数组
const a = imageDataArray[Math.floor(Math.random() * canvasCount];
//将像素信息放入随机到的透明图像数组中覆盖
a[i] = pixelArr[i];
a[i + 1] = pixelArr[i + 1];
a[i + 2] = pixelArr[i + 2];
a[i + 3] = pixelArr[i + 3];
}
复制代码
当canvasCount为3时效果以下:
原始图片canvas
生成的包含原图部分像素的3个canvas
canvasCount越大,生成的canvas越多,分配到每一个canvas上面的像素就越少,飘的就越散。
4. 接下来就很是简单了,隐藏掉原始图像,为生成的canvas添加飘散动画就能够了,飘散动画主要组成就是高斯模糊,位移,旋转,透明度变化,具体代码这里就不写了,能够在demo源代码中看到,最终效果以下。canvas
灰飞烟灭的动画已经完成,接下来是如何触发这段动画,文章开始就说过谷歌搜索上的原始效果是经过点击按钮触发,而咱们经过麦克风实时检测输入音量,当打响指时(音量达到必定大小)触发动画。数组
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
// 获取用户的 media 信息
navigator.mediaDevices
.getUserMedia({ audio: true })
.then(stream => {
//音频处理代码
})
.catch(error => {
mystatus.innerHTML = '获取音频时好像出了点问题。' + error;
});
} else {
mystatus.innerHTML = '不支持获取媒体接口';
}
复制代码
// 当输入音量超过此值时,表示检测大音量输入(响指声)
const TRIGGER_VALUE = 0.9;
// 将麦克风的声音输入这个对象
mediaStreamSource = audioContext.createMediaStreamSource(
stream
);
// 建立一个音频分析对象,采样的缓冲区大小为4096,输入和输出都是单声道
scriptProcessor = audioContext.createScriptProcessor(
4096,
1,
1
);
// 将该分析对象与麦克风音频进行链接
mediaStreamSource.connect(scriptProcessor);
// 此举无甚效果,仅仅是由于解决 Chrome 自身的 bug
scriptProcessor.connect(audioContext.destination);
// 开始处理音频
scriptProcessor.onaudioprocess = function(e) {
// 得到缓冲区的输入音频,转换为包含了PCM通道数据的32位浮点数组
let buffer = e.inputBuffer.getChannelData(0);
// 获取缓冲区中最大的音量值
let maxVal = Math.max.apply(Math, buffer);
// 显示音量值
if (maxVal > TRIGGER_VALUE) {
//灰飞烟灭动画
start();
}
};
复制代码
一切搞定,就这么简单能够拿去装逼了,不过我同事问我,只能监测音量大小吗,这样的话不论是咳嗽声,大喊一声,啪啪啪声均可以触发,能不能真的只检测响指的声音。 嗯~这是个好问题,你们一块儿想一想吧。网络