Маленькие шедевры JavaScript — Эпизод 4

Добро пожаловать на четвертую встречу из серии «Маленькие шедевры JavaScript». Как вы, возможно, уже знаете, я беру пример с некоторых замечательных работ, проделанных программистами со всего мира (которые можно найти в основном на codepen.io), чтобы представить их на всеобщее обозрение и воспользоваться возможностью, чтобы проанализировать их и изучить новый JavaScript и CSS. методы.

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

Преобразование текста

Первое предложение исходит от Джеми Браун и учит нас, как растворять один текст в другом всего несколькими умелыми штрихами.

Используемая техника на самом деле будет новой для большинства из нас. Он включает в себя применение «порогового» SVG-фильтра к двум текстовым строкам, которые анимируются с постепенным изменением размытия и непрозрачности. Но давайте послушаем самого автора, который любезно предоставил обширные комментарии к своему коду.

/*
 This pen cleverly utilizes SVG filters to create a "Morphing Text" effect. 
 Essentially, it layers 2 text elements on top of each other, and blurs them 
 depending on which text element should be more visible. Once the blurring
 is applied, both texts are fed through a threshold filter together, 
 which produces the "gooey" effect. Check the CSS - Comment the #container 
 rule's filter out to see how the blurring works!
*/

const elts = {
 text1: document.getElementById("text1"),
 text2: document.getElementById("text2")
};

... [omitted]

// A lot of the magic happens here, this is what applies the blur filter to 
// the text.
function setMorph(fraction) {
 // fraction = Math.cos(fraction * Math.PI) / -2 + .5;
 
 elts.text2.style.filter = `blur(${Math.min(8 / fraction - 8, 100)}px)`;
 elts.text2.style.opacity = `${Math.pow(fraction, 0.4) * 100}%`;
 
 fraction = 1 - fraction;
 elts.text1.style.filter = `blur(${Math.min(8 / fraction - 8, 100)}px)`;
 elts.text1.style.opacity = `${Math.pow(fraction, 0.4) * 100}%`;
 elts.text1.textContent = texts[textIndex % texts.length];
 elts.text2.textContent = texts[(textIndex + 1) % texts.length];
}
 
... [omitted]

Вот часть CSS, на которую он ссылается:

#container {
  ... [omitted]
  
  /* this filter is a lot of the magic */
  filter: url(#threshold) blur(0.6px);
}

… и «пороговый» фильтр SVG:

<!-- The SVG filter used to create the Merging effect -->
<svg id="filters">
  <defs>
    <filter id="threshold">
      <!-- Basically just a threshold effect - pixels with a high enough 
           opacity are set to full opacity, and all other pixels are set 
           to completely transparents. --> 
          <feColorMatrix in="SourceGraphic"
                         type="matrix"
                         values="1 0 0 0 0
                                 0 1 0 0 0
                                 0 0 1 0 0
                                 0 0 0 225 -140" />
    </filter>
  </defs>
</svg>

Очистка лука

Второе предложение исходит от Джона Хили и использует пути SVG для анимации преобразования одного персонажа в другого.

Четыре буквы, видимые на изображении, определены как пути SVG:

var paths = [
 'M27.7,13.1v13.6c-0.7,0.5-1.4,1-2.3,1.4c-0.9,0.5-1.8,0.9-2.9,1.2s-2.2,0.6-3.4,0.9c-1.2,0.2-2.5,0.3-3.9,0.3c-2.5,0-4.6-0.3-6.5-1c-1.8-0.7-3.4-1.7-4.6-3c-1.2-1.3-2.1-2.9-2.7-4.7c-0.6-1.8-0.9-3.9-0.9-6.1c0-2.3,0.3-4.5,0.9-6.3S3,5.8,4.2,4.5c1.2-1.3,2.7-2.3,4.5-3c1.8-0.7,3.8-1,6.1-1c2,0,3.8,0.3,5.3,0.8c1.5,0.5,2.8,1.2,3.8,2.1c1,0.9,1.8,1.9,2.4,3.1c0.6,1.2,1,2.4,1.2,3.7l-9.1,0.8c-0.2-1-0.6-1.8-1.2-2.2c-0.6-0.4-1.4-0.6-2.4-0.6c-1.7,0-3,0.6-3.8,1.7c-0.8,1.1-1.2,3-1.2,5.6c0,2.7,0.4,4.6,1.3,5.8c0.9,1.2,2.4,1.8,4.5,1.8c1.4,0,2.6-0.2,3.6-0.6V20h-3.8v-6.9H27.7z',
 'M0.5,8.3V0.5h25v7.8h-8v20.8H8.5V8.3H0.5z',
 'M20.2,0v7.2H8.9v4.7h10.7V19H8.9v9.6H0V0H20.2z',
 'M8.1,29c-1.8-0.7-3.3-1.7-4.5-2.9c-1.2-1.3-2.1-2.8-2.7-4.7C0.3,19.5,0,17.4,0,15.1s0.3-4.4,0.9-6.3s1.5-3.4,2.8-4.8c1.2-1.3,2.7-2.3,4.6-3S12.1,0,14.5,0c2.3,0,4.4,0.3,6.1,1c1.8,0.7,3.3,1.7,4.5,2.9c1.2,1.3,2.1,2.8,2.7,4.7s0.9,3.9,0.9,6.2s-0.3,4.4-0.9,6.3s-1.5,3.5-2.8,4.8c-1.2,1.3-2.7,2.3-4.5,3S16.7,30,14.3,30C12,30,9.9,29.7,8.1,29z M19.4,15c0-4.8-1.7-7.2-5-7.2c-3.3,0-5,2.4-5,7.2c0,4.8,1.7,7.2,5,7.2C17.7,22.2,19.4,19.8,19.4,15z'
]

Замечательный графический эффект достигается благодаря функциям анимационной библиотеки GSAP TweenMax. Для подробного объяснения на этот раз я полагаюсь на моего нового учителя кода, ChatGPT:

Магический текст (частицы)

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

Анимация начинается с создания текста на холсте и превращения его в изображение, из которого захватываются все пиксели:

function init() {
  ... [omitted]

  var fontSize = Math.min(height, width) / 2.5;
  ctx.font = fontSize + 'px Arial';
  ctx.textAlign = 'center';
  ctx.fillText(text, width / 2, height / 2 + fontSize / 4);

  imageData = ctx.getImageData(0, 0, width, height);
  pixels8 = imageData.data;
  pixels32 = new Uint32Array(pixels8.buffer);

  for (var x = 0; x < width; x += gatherStep) {
    for (var y = 0; y < height; y += gatherStep) {
      var color = pixels32[y * width + x];
      if (color != 0) {
        var color =  (255 << 24) | (~~(Math.random() * 50) << 16) | ((~~(Math.random() * 200) + 54) << 8) | ~~(Math.random() * 50);
        particles.push({ x: x, y: y , color: color});
      }
    }
  }
}

После этого запускает стандартный цикл анимации: на каждой итерации все частицы по спирали перемещаются от периферии к центру и наоборот:

function animation() {
  // Increment time frame by 0.01 at each iteration
  frame += .01;
  
  // Calculate scale based on the current time frame
  scale = (Math.cos(frame * 1) + 1) * Math.max(width, height) / 1.8;
  
  // Clear all pixels on the canvas
  pixels32.fill(0);
  
  // Loop through each particle and update its position and color
  for (var i = 0; i < particles.length; i++) {
    var particle = particles[i];
    
    // Calculate the new x and y position of the particle based on the 
    // current scale and time frame
    var dX = scale * Math.cos(i + frame); 
    var dY = scale * Math.sin(i + frame);
    
    // Calculate the index of the pixel on the canvas corresponding to 
    // the particle position
    var xPart =  ~~(particle.x + dX);
    if(xPart >= 0 && xPart < width){
      var yPart = ~~(particle.y + dY);
      if(yPart >= 0 && yPart < height){
        var ptr = yPart * width + xPart;
        var color = particle.color;
        
        // Randomly set the color to white
        if(Math.random() < 0.05) color = colorWhite; 
        
        // Set the color of the pixel and its surrounding pixels
        pixels32[ptr] = color;
        pixels32[ptr + 1] = color;
        pixels32[ptr + width] = color;
        pixels32[ptr + width + 1] = color;
      }
    }
  }
  
  // Draw the updated pixels on the canvas
  ctx.putImageData(imageData, 0, 0);
  
  // Call the animation function again for the next frame
  requestAnimationFrame(animation);
}

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

Сбой

Мой четвертый выбор — работа от Evan Jin, автора, которого я уже освещал в первой статье этой серии за его прекрасное творение Интерактивный фон с веревками.

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

Вся игра реализована с использованием свойства преобразования CSS для искажения и масштабирования текста в соответствии с двумя переменными:

h1 {
  font-size: 4rem;
  color: #f1f1f1;
  transform: skew(var(--skew));
  transform: skew(var(--skew)) scale(var(--scale));
}

Каждую десятую секунды значение перекоса переназначается максимум на ±10 градусов, а масштаб устанавливается равным 1. Каждые 1,5 секунды перекос вместо этого может увеличиваться до ± 90 градусов, при этом масштаб меняется каждые 3 секунды на случайное значение вплоть до максимального значения 1,5.

function glitch(element) {
  let count = 0
  setInterval(() => {
    // element
    const skew = Math.random() * 20 - 10
...
    element.style.setProperty('--skew', `${skew}deg`)
...
    element.style.setProperty('--scale', `1`)

    count++

    if (count % 15 === 0) {
      const bigSkew = Math.random() * 180 - 90
      element.style.setProperty('--skew', `${bigSkew}deg`)
    }

    if (count % 30 === 0) {
      const bigScale = 1 + Math.random() / 2
      element.style.setProperty('--scale', `${bigScale}`)
    }
  }, 100)
}

const h1 = document.querySelector('h1')
glitch(h1)

Цветной ввод

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

Анимация запускается событием keyup и использует библиотеку GSAP TweenLite для создания эффекта случайного подпрыгивания и подъема:

const animateLetterIn = letter => {
  const yOffset = (0.5+Math.random()*0.5) * textSize;
  TweenLite.fromTo(letter, 0.4, {scale:0}, {scale:1, ease: Back.easeOut});
  TweenLite.fromTo(letter, 0.4, {opacity:0}, {opacity: 1, ease: Power3.easeOut});
  TweenLite.to(letter, 0.2, {y:-yOffset, ease: Power3.easeInOut});
  TweenLite.to(letter, 0.2, {y:0, ease: Power3.easeInOut, delay: 0.2});
  const rotation = -50 + Math.random()*100;
  TweenLite.to(letter, 0.2, {rotation: rotation, ease: Power3.easeInOut});
  TweenLite.to(letter, 0.2, {rotation: 0, ease: Power3.easeInOut, delay: 0.2});
}

«Конфетти» — это 8 треугольников и 8 кругов, сгенерированных как SVGElements в случайных положениях и перемещенных с помощью функций TweenLite.

Гиперпространственный текст

Мой последний выбор — работа неординарного творца Йохана Карлссона, и я рекомендую всем посетить его галерею потрясающих творений.

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

Это все на сегодня! Не пропустите другие выпуски:







«Волшебный часовщик с JavaScript
Маленькие шедевры JavaScript — Эпизод 3javascript.plainenglish.io»



Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.