Десять минут, чтобы добиться эффекта щелчка пальцами Таноса.

внешний интерфейс Canvas

Любой, кто смотрел «Мстителей», знает, что Танос, как убежденный сторонник и исполнитель политики планирования семьи, щелкнул пальцами и уничтожил половину жизни во вселенной. Сцена уничтожения жизни в фильме очень крутая, поэтому после выхода Мстителей 4 несуществующий сайт гугл запустил пасхалку.Если в поисковике искать Таноса, то появится кнопка перчатка, жмем После этого половина результатов веб-поиска исчезнет.

Ну ~ это очень похоже на Google.
Эффект хоть и прикольный, но на самом деле не сложный.Вот возьмем всех реализовать.В интернете уже есть несколько туториалов(Thanos Snap Effect JavaScript Tutorial), добавьте сюда кое-что, Танос все-таки щелкает пальцами, вместо того, чтобы использовать мышь для нажатия кнопки, поэтому я добавлю определение громкости, когда звук щелчка пальца определяется (на самом деле он превышает определенную громкость предустановка), эффект сработает, и тогда вы сможете с радостью притворяться перед друзьями.

адрес исходного кода

Готов начать

Здесь мы используем только один элемент изображения, структура 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>

Реализация анимации

  1. Преобразование html в холст для подготовки к последующей обработке. Здесь мы используем библиотеку html2canvas, которая умеет рисовать узел dom в html на холсте, что эквивалентно снятию скриншота этого узла dom.Она очень проста в использовании, и код выглядит следующим образом (эту библиотеку можно использовать для создавать постеры на веб-страницах).
const imageBox = document.querySelector('#image')
html2canvas(imageBox, {
    backgroundColor: 'transparent' //背景设置为透明
}).then(canvas=>{
    //处理canvas的代码(注意.then这种写法只有在新版本的html2canvas可用)
});
  1. Здесь, после того как мы получим холст, содержащий информацию об изображении, нам нужно получить информацию о каждом пикселе на холсте через getImageData холста.
 //处理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-прозрачность). Нравится эта картинка

Его информация о пикселях

[0,0,0,255,255,255,255,255,255,0,0,255,0,255,0,255]
  1. Следующим шагом является создание нескольких массивов одинаковой длины, заполнение их прозрачной информацией о пикселях (значения в массиве все равны 0), а затем «случайным образом» распределение полученной ранее информации о пикселях изображения в эти массивы, эти массивы содержат часть исходного изображения, в оригинальном туториале использовалась библиотека для генерации случайных чиселchanceЧтобы контролировать вероятность появления случайных чисел, можно напрямую использовать полностью случайные числа.
//创建一个和图像信息数组长度相同的数组并填充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, эффект выглядит следующим образом:
холст оригинального изображения

Сгенерировано 3 холста, содержащих несколько пикселей исходного изображения.



Чем больше canvasCount, тем больше канвасов генерируется, и тем меньше пикселей выделяется на каждый канвас, и тем больше они разбросаны.
4. Следующий шаг очень простой.Скрываем исходное изображение и добавляем на сгенерированный холст анимацию дрейфа.Основными компонентами анимации дрейфа являются размытие по Гауссу, смещение, поворот и изменение прозрачности.Конкретный код писать здесь не буду Как видно из исходного кода демо, окончательный эффект выглядит следующим образом.

спусковой крючок с щелчком пальца

Анимация пепла завершена, и следующий шаг - как запустить эту анимацию. В начале статьи говорилось, что исходный эффект поиска Google запускается нажатием кнопки, и мы используем микрофон для определения громкости ввода. в режиме реального времени. При щелчке пальца (громкость достигает определенного уровня)) для запуска анимации.

  1. Сначала убедитесь, что на вашем устройстве есть устройство ввода звука, такое как микрофон, а затем отслеживайте и получайте устройство.Если есть устройство с микрофоном, на веб-странице появится запрос на авторизацию.
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        // 获取用户的 media 信息
        navigator.mediaDevices
            .getUserMedia({ audio: true })
            .then(stream => {
                //音频处理代码
            })
            .catch(error => {
                mystatus.innerHTML = '获取音频时好像出了点问题。' + error;
            });
} else {
        mystatus.innerHTML = '不支持获取媒体接口';
}           
  1. код обработки звука
// 当输入音量超过此值时,表示检测大音量输入(响指声)
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(); 
    }
};

Наконец

Все сделано, так легко использовать его, чтобы притворяться, но мой коллега спросил меня, могу ли я контролировать только уровень громкости, поэтому может ли быть вызван кашель, крик или хлопки, может ли он действительно только срабатывать? звук щелчка пальцев. Хм~ Это хороший вопрос, давайте подумаем над ним вместе.

Список всех статей