Научите вас реализовывать спецэффект 🎉 смайликов «взрыва» WeChat 8.0.

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

напишите в начале

Недавно обновленный микро-канал 8.0, один из самых интересных способов обновить внешний вид сумки, и все в группе сыграли пакет эмоций Второй мировой войны.

Меня как фронтенд программиста это возбудило любопытство.Хотя я никогда не реализовывал подобную анимацию,но не мог не захотеть ее реализовать.В итоге я потратил 2 дня на просмотр исходников некоторых библиотек. добиться подобного эффекта сам, я подытожу здесь и научу вас, как научиться его достигать. И у 🎉 есть собственное имя, называемоеконфетти, английское названиеconfetti.

Чат + эффекты конфетти Онлайн адрес:www.qiufengh.com/#/

Адрес чата Github:GitHub.com/flower1995116/…

Конфетти Github адрес:GitHub.com/flower1995116/…

Для предварительного просмотра спецэффектов, по соображениям времени, я реализовал только небольшие цветные блоки параллелограммов, и принципы других фигур аналогичны.

Вы также можете задать направление

Предварительное исследование

До написания этого спецэффекта я практически не пользовался канвасом, хотя не знаю, как его использовать сейчас, и многие API не очень понятны, поэтому этот туториал тоже написан на основе канваса с нулевым отсчетом, вам не нужно беспокойство по поводу сложности этого урока было отклонено. Я буду реализовывать его шаг за шагом на основе холста с нулевой базой. Однако, прежде чем изучать этот спецэффект, вам нужно немного знать математику средней школы.Если вы помните функции sin и cos, то следующее содержание будет для вас очень простым, неважно, если вы этого не сделаете~

Лично я предпочитаю исследовать и исследовать, и я буду исследовать интересные вещи, поэтому я также стою на базе гигантов и иду в codepen, чтобы проверить много подобных реализаций для исследования.

наконец нацелиться наcanvas-confettiзачем эта библиотека? Потому что его эффект очень хорош для нас, и это библиотека с открытым исходным кодом, и1.3Kstar (я чувствую, что когда-нибудь смогу проанализировать принцип библиотеки реализации старшего брата~), и частота обслуживания также очень высока.

основная реализация

Нарезка сцены

Когда я впервые получил эту библиотеку, я был счастлив, потому что в библиотеке есть только один файл.

Но когда я открываю файл, я обнаружил, что файл не...500 строкКод, я слой за слоем счищал некоторый код пользовательской конфигурации и, наконец, вытащил траекторию одного конфетти. Я начал наблюдать за траекторией его движения... Бесконечный цикл наблюдения...

Вы можете видеть, что он совершает движение, подобное параболе, а затем я помечаю переменные в исходном коде одну за другой, а затем комбинирую исходный код.

fetti.x += Math.cos(fetti.angle2D) * fetti.velocity;
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity;

Ничего страшного, если вы не понимаете приведенный выше код, я просто подтверждаю написание в исходном коде и даю некоторые идеи для изучения исходного кода.Ниже приведена реальная реализация лекции!

Реализация параллелограмма

Прежде чем реализовать эту функцию, нам нужно знать несколько функций холста. узнать больше (ууууу. Беги, о, о, quilt.com/plus heat/Dom-oh…

beginPath

метод запускает путь или сбрасывает текущий путь.

moveTo

Перемещает путь в указанную точку на холсте без создания линии.

lineTo

Добавляет новую точку и создает линию на холсте от этой точки до последней указанной точки.

closePath

Создайте путь от текущей точки обратно к начальной точке.

fill

Заполняет текущий рисунок (путь).

fillStyle

Задает или возвращает цвет, градиент или узор, используемые для заливки краской.

Поскольку мы собираемся реализовать конфетти, я должен сначала реализовать конфетти, давайте реализуем конфетти в виде параллелограмма!

Все мы знаем, что реализация параллелограмма в css — это div, а по умолчанию — box, но в canvas это не так удобно, так как реализовать параллелограмм?

Четыре точки, нам нужно знать только четыре точки, чтобы определить параллелограмм. Система координат в канвасе немного отличается от нашей обычной веб-страницы для письма, она начинается с левого верхнего угла, но это не влияет на нее.

Я могу нарисовать параллелограмм шириной 20,(0, 0), (0, 20), (20,20), (20,0).

...(省略了一些前置初始化代码)
var context = canvas.getContext('2d');
// 清除画布
context.clearRect(0, 0, canvas.width, canvas.height);
// 设置颜色并开始绘制
context.fillStyle = 'rgba(2, 255, 255, 1)';
context.beginPath();
// 设置几个点
var point1 = { x: 0, y: 0 }
var point2 = { x: 0, y: 20 }
var point3 = { x: 20, y: 20 }
var point4 = { x: 20, y: 0 }
// 画4个点
context.moveTo(Math.floor(point1.x), Math.floor(point1.y));
context.lineTo(Math.floor(point2.x), Math.floor(point2.y));
context.lineTo(Math.floor(point3.x), Math.floor(point3.y));
context.lineTo(Math.floor(point4.x), Math.floor(point4.y));
// 完成路线,并填充
context.closePath();
context.fill();

Подведем итоги, на самом деле нам нужна только одна точка, чтобы определить начальное положение этого параллелограмма(0, 0), если вы знаете другой угол(90度), и переменная длина параллелограмма (20) для определения положения всего параллелограмма! (Просто нужны знания средней школы, чтобы найти весь параллелограмм).

Что ж, вы сделали огромный шаг к успеху, научившись рисовать это! Разве это не легко~

Внутри ОС больших парней: это все?

Вот и все.

Трек движения

путем постоянной отладкиcanvas-confettiДля движения по траектории каждого кадра обнаружено, что оно всегда совершает движение с переменным замедлением по оси x (оно не будет продолжать движение, пока скорость не станет равной 0), а ось y также является сначала движением с переменным замедлением, а затем равномерная скорость движения, следующие примерно траектории.

Это его траектория, не думайте, что это сложно увидеть, но основной код состоит всего из трех предложений.

// fetti.angle2D为一个角度(这个角度确定了运动轨迹 3 / 2 * Math.PI - 2 * Math.PI之间的一个值,由于要让轨迹往左上角移动,就是都要往负方向运动,因此选了以上范围),
// fetti.velocity 为一个初始为50长度的值。
// fetti.gravity = 3
fetti.x += Math.cos(fetti.angle2D) * fetti.velocity; // fetti.x 第一个点的x坐标
fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // fetti.y 第一个点的y坐标
fetti.velocity *= 0.8;

Подводя итог, x-неделя первой точки координат всегда увеличивается на отрицательное значение (Math.cos(3 / 2 * Math.PI - 2 * Math.PI) всегда является отрицательным значением), и это значение равно постоянно уменьшается. И ось Y первой точки также всегда добавляет отрицательное значение Math.cos(3 / 2 * Math.PI - 2 * Math.PI) всегда отрицательно), но из-заfetti.gravityвсегда положительна, поэтому в некоторой критической точке значение y продолжает увеличиваться.

Я смоделировал следующие координаты, потому что для того, чтобы все поняли эту траекторию, следующие оси координат противоположны тем, что в холсте, и я также обрабатывал данные соответственно и обрабатывал их в обратном направлении.

Используйте квадрат со сторонами 10 для реализации траектории.

const fetti = {
  "x": 445,
  "y": 541,
  "angle2D": 3 / 2 * Math.PI + 1 / 6 * Math.PI,
  "color": {r: 20, g: 30, b: 50},
  "tick": 0,
  "totalTicks": 200,
  "decay": 0.9,
  "gravity": 3,
  "velocity": 50
}
var animationFrame = null;
const update = () => {
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = 'rgba(2, 255, 255, 1)';
  context.beginPath();
  fetti.x += Math.cos(fetti.angle2D) * fetti.velocity; // 第一个点
  fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // 第一个点

  var x1 = fetti.x;
  var y1 = fetti.y;

  var x2 = fetti.x;// 第二个点
  var y2 = fetti.y + 10; // 第二个点

  var x3 = x1 + 10;
  var y3 = y1 + 10;

  var x4 = fetti.x + 10;
  var y4 = fetti.y;

  fetti.velocity *= fetti.decay;

  context.moveTo(Math.floor(x1), Math.floor(y1));
  context.lineTo(Math.floor(x2), Math.floor(y2));
  context.lineTo(Math.floor(x3), Math.floor(y3));
  context.lineTo(Math.floor(x4), Math.floor(y4));

  context.closePath();
  context.fill();
  animationFrame = raf.frame(update);
}

Есть ли у него такой вкус, кроме цвета и формы?

Инвертный эффект

Так как же сделать это падение более естественным, чтобы было ощущение падения?

На самом деле, он делал флип-эффект.

Разобрать их — значит совершить вращательное движение вокруг точки, а весь процесс — двигаться по траектории движения, переворачиваясь.

Фактически, для достижения этого особого эффекта было упомянуто ранее при реализации квадрата, реализации квадрата. Удовлетворение следующих трех пунктов может реализовать параллелограмм.

  • знать расположение точки

  • знать угол

  • узнать длину стороны

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

const update = () => {
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = 'rgba(2, 255, 255, 1)';
  context.beginPath();

  fetti.velocity *= fetti.decay;
  fetti.tiltAngle += 0.1 // 不断给这个四边形变化角度

  var length = 10;

  var x1 = fetti.x;
  var y1 = fetti.y;

  var x2 = fetti.x + (length * Math.sin(fetti.tiltAngle));// 第二个点
  var y2 = fetti.y + (length * Math.cos(fetti.tiltAngle)); // 第二个点

  var x3 = x2 + 10;
  var y3 = y2;

  var x4 = fetti.x + length;
  var y4 = fetti.y;


  context.moveTo(Math.floor(x1), Math.floor(y1));
  context.lineTo(Math.floor(x2), Math.floor(y2));
  context.lineTo(Math.floor(x3), Math.floor(y3));
  context.lineTo(Math.floor(x4), Math.floor(y4));

  context.closePath();
  context.fill();
  animationFrame = raf.frame(update);
}

Таким образом, мы достигли вышеуказанных эффектов.

комбинированное упражнение

А потом совместить то, что мы написали выше, — полный спецэффект.

const update = () => {
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = 'rgba(2, 255, 255, 1)';
  context.beginPath();
  fetti.x += Math.cos(fetti.angle2D) * fetti.velocity; // 第一个点
  fetti.y += Math.sin(fetti.angle2D) * fetti.velocity + fetti.gravity; // 第一个点

  fetti.velocity *= fetti.decay;
  fetti.tiltAngle += 0.1 // 不断给这个四边形变化角度

  var length = 10;

  var x1 = fetti.x;
  var y1 = fetti.y;

  var x2 = fetti.x + (length * Math.sin(fetti.tiltAngle));// 第二个点
  var y2 = fetti.y + (length * Math.cos(fetti.tiltAngle)); // 第二个点

  var x3 = x2 + 10;
  var y3 = y2;

  var x4 = fetti.x + length;
  var y4 = fetti.y;


  context.moveTo(Math.floor(x1), Math.floor(y1));
  context.lineTo(Math.floor(x2), Math.floor(y2));
  context.lineTo(Math.floor(x3), Math.floor(y3));
  context.lineTo(Math.floor(x4), Math.floor(y4));

  context.closePath();
  context.fill();
  animationFrame = raf.frame(update);
}

окончательная форма

Если вы хотите достичь конечного состояния, это плохо多个小块,渐变消失так же как随机颜色!

Установите, сколько кадров исчезнет, ​​здесь две переменныеtotalTicksа такжеtick, настройте, чтобы контролировать количество кадров после исчезновения маленького блока.

Что касается нескольких фрагментов, нам просто нужно выполнить цикл for.

И случайные цвета, сделанныеcolorsсписок.

const colors = [
  '#26ccff',
  '#a25afd',
  '#ff5e7e',
  '#88ff5a',
  '#fcff42',
  '#ffa62d',
  '#ff36ff'
];
var arr = []
for (let i = 0; i < 20; i++) {
  arr.push({
    "x": 445,
    "y": 541,
    "velocity": (45 * 0.5) + (Math.random() * 20),
    "angle2D": 3 / 2 * Math.PI + Math.random() * 1 / 4 * Math.PI,
    "tiltAngle":  Math.random() * Math.PI,
    "color": hexToRgb(colors[Math.floor(Math.random() * 7)]),
    "shape": "square",
    "tick": 0,
    "totalTicks": 200,
    "decay": 0.9,
    "random": 0,
    "tiltSin": 0,
    "tiltCos": 0,
    "gravity": 3,
  })
}

Пожалуйста, смотрите полный код

GitHub.com/flower1995116/…

дополнительная еда

Реализуйте битву смайликов в виде многопользовательской битвы. Отправка выражений в нашем WeChat — это не одна точка, а форма для нескольких человек, поэтому мы можем продолжать исследовать и комбинировать веб-сокеты с красочными небольшими блоками.

Здесь нужно обратить внимание на несколько моментов. (Из-за недостатка места мы не будем объяснять вебсокет, но упомянем основные моменты реализации).

  • мы можем передать tagЧтобы различать сообщения истории или сообщения в реальном времени
  • Различайте, является ли это вашим собственным сообщением или чужим сообщением, чтобы изменить направление конфетти.
  • Анимация будет сделана только в том случае, если это один 🎉.
  • Сначала увеличивайте и уменьшайте анимацию, задерживайте 200 мс, а затем добавляйте спецэффекты.
if(this.msg === '🎉' && this.status) {
        this.confetti = true;
        const rect = this.$refs.msg.querySelector('.msg-text').getBoundingClientRect();
        if(rect.left && rect.top) {
          setTimeout(() => {
            confetti({
              particleCount: r(100, 150),
              angle: this.isSelf ? 120 : 60,
              spread: r(45, 80),
              origin: {
                x: rect.left / window.innerWidth,
                y: rect.top / window.innerHeight
              }
            });
          }, 200)
        }
}

больше для изучения

При использовании холста для рисования очень и очень большого количества квадратов он будет относительно застрял. В настоящее время мы можем использовать веб-воркеры для выполнения вычислений для повышения производительности. Читателям предлагается изучить это самостоятельно, и вы также можете увидетьcanvas-confettiисходный код~

наконец

Оглядываясь назад на предыдущие высоко оцененные статьи автора, вы можете получить больше!

Эпилог

❤️Подписаться + Нравится + Избранное + Комментарий + Переслать ❤️, оригинальность - это не просто, поощряйте автора создавать лучшие статьи

Обратите внимание на публичный аккаунт秋风的笔记, внешний общедоступный аккаунт, посвященный предварительным интервью, разработке и открытому исходному коду.

  • Ответить после подписки简历Получите более 100 наборов красивых шаблонов резюме
  • Ответить после подписки好友Втяните вас в группу технического обмена + группу обмена интервью, вы также можете обсудить со мной содержание этой практики.
  • Добро пожаловать, чтобы следовать秋风的笔记