Используйте анимацию частиц, чтобы запомнить время весеннего фестиваля | Поддержите эмодзи

внешний интерфейс JavaScript CSS

PK Creative празднует Китайский Новый год. Я участвую в "Творческом конкурсе Весеннего фестиваля". Подробности см.:Творческий конкурс "Праздник весны"

предисловие

Как говорится: после лабы новый год. Фестиваль Лаба закончился, а Праздник Весны уже гонится за ним на Hot Wheels!А вы с нетерпением ждете Праздника Весны в этом году?

Канун Нового года и Весенний фестиваль - самые важные праздники в году, а также прекрасные дни для воссоединения семьи.Вспоминая прошлые новогодние праздники, Сяобао не может не думать о счастье, радости, тоске, и горько, время Праздника Весны коротко, но вечно, глубоко выгравировано в море памяти Сяобао.

Я не знаю, как Китайский Новый год будет выглядеть в вашей памяти. Далее давайте взглянем на новогодние воспоминания Сяобао с помощью нескольких анимаций с ключевыми словами.

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

инструмент

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

  • сайт смайликов: вы можете перейти на веб-сайт, чтобы выбрать нужный смайлик (поставщик ссылки:Чун Лао)
  • Инструмент для записи экрана ScreenToGif: вы можете использовать инструмент для записи экрана, чтобы записать свои незабываемые воспоминания о китайском Новом году. Инструменты относительно просты и грубы в использовании.

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

как играть

Адрес опыта:Настройте свои собственные новогодние воспоминания

Игровой процесс очень прост, нужно помнить несколько вещей:

  • Ввод ключевого слова в подчеркнутой позиции
  • Несколько ключевых слов могут быть переданы черезПробел или китайская и английская запятаяотдельный
  • служба поддержкиКитайский и английский, текст эмодзи
  • Текст не должен быть слишком длинным,В пределах пяти символов
  • После ввода последовательности ключевых слов,Введите ключ для запуска анимации

Давайте сначала взглянем на воспоминания Сяобао о Весеннем фестивале.

детство

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

Канун Нового года

той эпохигодОн также полон новогоднего вкуса.В канун Нового года семья Сяобао из трех человек ест пельмени и смотрит гала-концерт Весеннего фестиваля. Ключевые слова: 👨‍👩‍👦,🥟,📺,👏,🕛,🌉,🧨,🧧

childEnd.gif

Весенний фестиваль

Дни Праздника Весны еще более наполнены: семья из трех человек встает в шесть часов, встает в шесть часов, поздравляет с Новым годом в семь часов, гуляет по аллеям, немного толпа, конфеты арахис, счастливые и беззаботные. Превратились в ключевые слова: 👨‍👩‍👦,🕕,🏮,🕖,🤝, 👨‍👨‍👦‍👦,🍬,🥜,🥰

childNew.gif

молодежь

Ключевые слова-выражения в последующих частях интерпретироваться не будут, вы можете угадать их и разместить в комментариях, наиболее близкие будут присланы Наггетсами.

Ключевые слова: 👨‍👨‍👧‍👦,🥟,📺,🕛,квадрат,🧨,🎇,🙏

adlEnd.gif

эпидемия

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

Ключевые слова: 👨‍👨‍👧‍👦,📺,🦠,🛌,🥼,🦸🏻,🏆, дань

convEnd.gif

Код

На самом деле, эффект частиц был реализован много раз в маленьком пакете, поэтому простая часть маленького пакета не будет подробно объясняться, а ядро ​​объяснит часть переключения анимации.

Создать класс частиц

Частицы в основном включают случайное положение частицы, целевое положение частицы, цвет частицы и радиус частицы. В этом проекте радиус частиц равномерно установлен равным 2.

class Particle {
  constructor({ x = 0, y = 0, tx = 0, ty = 0, radius = 2, color = "#F00000" }) {
    // 当前坐标为随机生成坐标
    this.x = particle.x;
    this.y = y;
    // 目标点坐标(副画布原有粒子坐标)
    this.tx = tx;
    this.ty = ty;
    this.radius = 2;
    this.color = color;
  }
 // 粒子绘制   
  draw(ctx) {
    ctx.save();
    ctx.translate(this.x, this.y);
    ctx.fillStyle = this.color;
    ctx.beginPath();
    // 绘制圆形
    ctx.arc(0, 0, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fill();
    ctx.restore();
    return this;
  }
}

Извлечение информации о пикселях

Создайте дополнительный холст и нарисуйте текст

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

const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const viewWidth = window.innerWidth * 0.5;
const viewHeight = window.innerHeight * 0.5;

canvas.width = viewWidth;
canvas.height = viewHeight;
// 预留处理图片的接口
if (typeof target === "string") {
    // 绘制文字
    // 保证长文字的在PC端及移动端的完整展示
    // 处理的有几分粗糙
    ctx.font = `${
          target.length < 3 ? textWidth : (textWidth * 3) / (1 + target.length)
        }px bold`;
    // 设置文字的基本样式
    ctx.fillStyle = colorList[rand(0, colorList.length)];
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    ctx.fillText(target, viewWidth / 2, viewHeight / 2);
}

Получение данных о пикселях

Сосредоточьтесь на методе getImageData

  • грамматикаctx.getImageData(sx, sy, sw, sh);

  • параметр

    • sx, sy: Чтобы извлечь верхний левый угол изображенияx yкоординировать
    • sw, sh: ширина и высота извлекаемого изображения.
  • возвращаемое значение

    вернутьImageDataОбъект, который копирует пиксели указанной области изображения. Для каждого пикселя изображения он хранится отдельноRGBAЧетыре аспекта информации, все данные пикселей хранятся в одномерном массиве в видеdataатрибут (каждый пиксель определяетсяrgbaЧетыре значения, поэтому каждый раз нужно умножать на 4, чтобы перейти к следующему пикселю)

const { data, width, height } = ctx.getImageData(0, 0, viewWidth, viewHeight);

image.png

Как видно из приведенного выше рисунка,imageDataсодержит четыре свойства, выше мы использовалиwidth height data,dataОгромное количество данных о пикселях хранится в файле .В нашем случае в качестве примера первое ключевое слово содержит737280Число, поэтому нам нужно отфильтровать пиксели по определенному алгоритму.

image.png

Извлеките пиксели, где нарисованы частицы

объясните здесьintervalРоль и почему может быть достигнуто управление количеством пикселей?

getImageDataИзвлеките все пиксели холста, как показано выше.dataпоказано, всего737280 / 4 = 184320пиксели, если они не отфильтрованы, все нарисованы в радиусе2, с одной стороны, частицы будут перекрываться, с другой стороны, если180 000Чтобы обеспечить плавность движения частиц, используйтеrequestAnimationFrameобновлять позиции частиц, такое огромное количествоЧастый рендеринг частиц требует чрезвычайно высокой производительности рендеринга компьютера.Например, небольшая сумка полностью не может работать, коробка.

Следовательно, нам нужно найти способ адаптироваться к различным дисплеям и отображать только нужное количество частиц.Так что же нам делать?getImageDataвернуть данные?

Большие ребята придумали хороший способ, холстwidth, heightпройти черезgetImageDataприобретаются синхронно, поэтому мы можем думать о холсте как оwidth * heightширина и высота есть1сетчатый состав. Мы берем данные в этой сетке,intervalЧтобы выбрать интервал, каждыйintervalвыбрать пиксель,intervalЧем больше значение, тем больше интервал, поэтому в каждой строке выбирается меньше пикселей. (еслиintervalЕсли выделение слишком большое, в области выделения будет меньше пикселей, а текст может отсутствовать, поэтому нам нужно выбрать подходящийinterval)

Следующий шаг — как найти этот пиксель?getImageDataВозвращаемые данные предназначены для чтения пикселей построчно, и каждый пиксель сохраняется с использованием четырех битов массива. Если взять за ориентир ширину и высоту, тоВертикальная координата y * ширина холста + горизонтальная координата xполучить позицию пикселя, затемумножить на 4рассчитано наImageDataПозиция индекса массива.Абсцисса x и ордината y являются целевым положением частицы..

// 获取目标对象的像素点,interval 控制像素点数量,值越大返回的像素点越少
  const pixeles = [];
  // 遍历像素数据,用interval减少取到的像素数据
  for (let x = 0; x < width; x += interval) {
    for (let y = 0; y < height; y += interval) {
      const pos = (y * width + x) * 4; 
      // 只提取 rgba 中透明度大于0.5的像素,即aplha > 128
      if (data[pos + 3] > 128) {
        pixeles.push({
          x,
          y,
          rgba: [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]],
        });
      }
    }
  }
  return pixeles;
}

Нарисуйте частицы

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

function createParticles({ text, radius, interval }) {
  const pixeles = getWordPxInfo(text, interval);
  return pixeles.map((particle) => {
    return new Particle({
      x: Math.random() * (50 + window.innerWidth * 0.5) - 50,
      y: Math.random() * (50 + window.innerHeight * 0.5) - 50,
      tx: particle.x,
      ty: particle.y,
      radius: particle.raduis,
      color: particle.rgba,
    });
  });
}

Сгенерированные частицы распределяются, как показано ниже:

particlesFirst.png

Переключение текста

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

Каждая частица имеетfinishedсвойство, значение по умолчаниюfalse. Указывает, что текущая позиция частицы меньше целевой позиции0.1Представляет конец текущего движения частицы, после окончания текущего движения частицы изменяет егоfinishedзначениеtrue; если больше0.1, частицы продолжают двигаться. (Коэффициент ослабления, принятый для движения частиц, равен 0,09)

Поскольку каждая частица имеетfinishedсвойства, мы можем передатьfilterотфильтровать всеfinishedсобственностьtrueЕсли количество отфильтрованных частиц равно общему количеству частиц, текущая текстовая анимация завершается и переключается следующий текст.

// 参数分别是粒子数组及下一个文字动画回调
function drawFrame(particles, finished) {
  // 开启粒子渲染动画
  const timer = window.requestAnimationFrame(() => {
    drawFrame(particles, finished);
  });
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  // 缓动系数设置为0.09
  const easing = 0.09;
  const finishedParticles = particles.filter((particle) => {
    // 当前坐标和目标点之间的距离
    const dx = particle.tx - particle.x;
    const dy = particle.ty - particle.y;
    // 粒子移动速度
    let vx = dx * easing;
    let vy = dy * easing;

    // 判断当前粒子是否完成动画
    if (Math.abs(dx) < 0.1 && Math.abs(dy) < 0.1) {
      // 完成动画位置不会在改变,并修改 finished 为 true  
      particle.finished = true;
      particle.x = particle.tx;
      particle.y = particle.ty;
    } else {
      // 未完成动画继续更新粒子位置   
      particle.x += vx;
      particle.y += vy;
    }
    // 绘制粒子新位置   
    particle.draw(ctx);
    // 判断返回完成运动的的粒子数
    return particle.finished;
  });

  if (finishedParticles.length === particles.length) {
    // 全部粒子运动结束,结束当前动画
    window.cancelAnimationFrame(timer);
    // 开启下一轮文字回调
    finished && finished();
  }
  return particles;
}

Здесь реализован код, может быть реализован анимационный эффект текущего текста и оценка окончания анимационного эффекта Нам нужно только выполнить выполнение после окончания текущего анимационного эффекта.finishedОбратный вызов для достижения переключения.

петля функции переключения анимации

loopФункция предназначена для предоставленияdrawFrameОбеспечивает визуализацию следующего текста. Логика кода относительно проста, особо нечего сказать.

function loop(words, i = 0) {
  return drawFrame(
    // 生成粒子
    createParticles({ text: words[i], radius: 2, interval: 5 }),
    // finish函数部分=》下一个文字
    () => {
      i++;
      // hack一下空文字
      if (i < words.length && words[i].length > 0) {
        loop(words, i);
      }
    }
  );
}

большой подарок на год тигра

Проще говоря, с тех пор, как я начал писать в «Наггетс» в октябре, я только что вошел в2022В 2019 году Xiaobao успешно приземлилсяLV4, представление Сяобао о себе все еще очень ясно, и Сяобао нужно быстро прогрессировать, чтобы стать достойным быть отличным автором. Но я очень благодарен за поддержку больших парней.Я встретил много новых друзей на стороне Nuggets.Я надеюсь, что дружба моих друзей будет длиться вечно. Кроме того, я хотел бы поблагодарить прекрасных операторов сообщества Nuggets, которые ответственны и обоснованы, чтобы Сяобао, пожилой писатель, мог восстановить свой энтузиазм и восстановить свои первоначальные намерения.Я надеюсь, что в новом году мы сможем расти и добиваться прогресса с Наггетс.

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

  1. Xiaobao резервирует два набора ключевых слов, чтобы угадать наиболее подходящих друзей-копателей, и Xiaobao отправит копию самородков по всему миру.
  2. Поделись своими новогодними воспоминаниями (можно записать анимацию или ключевые слова), собери больше всего лайков и комментариев, и пакетик тоже достанется Наггетсам.
  3. Других наград пока не придумал, если придумаю позже, добавлю динамически

Репозиторий исходного кода

Адрес источника:Настройте свои собственные новогодние воспоминания

Адрес опыта:Настройте свои собственные новогодние воспоминания

Не забудьте поставить пакету ⭐, если вы считаете его полезным.

Недавние события

Серия JavaScript для продвинутых пользователей

часть интервью

счастливое программирование

послесловие

Друзья, если вы чувствуете, что эта статья вам полезна, ставьте лайк Абао👍 или подписывайтесь➕ — это самая большая поддержка для меня.

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

Эпидемия скоро закончится, и мир вернется к миру