Случайность и генеративность

Кодинг
Q_Time

8 минут чтения

hero image

Содержание

В предыдущей статье мы научились управлять элементами по клику, переключать состояния и менять содержимое. Всё это — предсказуемые реакции на предсказуемые действия. Теперь добавим случайность. С помощью Math.random() веб-плакат перестаёт быть одинаковым при каждом просмотре — элементы меняют позиции, цвета, размеры и порядок. Это то, что сближает формат с creative coding и делает каждое взаимодействие со страницей немного другим.

Math.random()

Всё начинается с одного метода. Math.random() возвращает случайное дробное число от 0 (включительно) до 1 (не включительно). Само по себе это число бесполезно, но из него строятся все остальные формулы.

0.0000

Math.random()              // 0.0 ... 0.999
Math.random() * 10         // 0.0 ... 9.999
Math.floor(Math.random() * 10)  // 0, 1, 2 ... 9
 
/* Math.random() — дробное от 0 до 1 (не включая 1).
   Умножаем на диапазон, Math.floor округляет вниз.
   Это базовая формула для любой случайности. */
      

Три формулы, которые стоит запомнить.

Math.random() — дробное от 0 до 0.999. Полезно для прозрачности и пропорций.

Math.random() * N — дробное от 0 до N. Полезно для размеров и координат.

Math.floor(Math.random() * N) — целое от 0 до N−1. Полезно для индексов массива и дискретных значений.

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

Число в диапазоне от A до B: A + Math.random() * (B - A). Например, случайный размер от 20 до 80 пикселей: 20 + Math.random() * 60. Случайная прозрачность от 0.3 до 1: 0.3 + Math.random() * 0.7. Запомните эту формулу — она универсальна.

Случайный выбор из палитры

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


const colors = ['#fe3904','#9eff70','#82e1f1','#febbed','#3898e2']
 
box.addEventListener('click', () => {
  const i = Math.floor(Math.random() * colors.length)
  box.style.backgroundColor = colors[i]
  box.style.borderRadius = Math.random() >0.5 ? '50%' : '8px'
  box.style.transform = `scale(${0.8 + Math.random() * 0.5})`
})
/* Math.random() >0.5 — случайное условие,
   монетка: true или false.
   0.8 + Math.random() * 0.5 —
   число в диапазоне от 0.8 до 1.3. */
      

Каждый клик задаёт квадрату случайный цвет, форму и масштаб. Обратите внимание на запись Math.random() >0.5 — это случайное условие, которое работает как бросок монетки: в половине случаев true, в половине false. Удобно для бинарных решений вроде «круг или квадрат».

Массив: список значений

Массив — это упорядоченный список значений, записанный в квадратных скобках. Каждый элемент имеет индекс, начиная с нуля. Свойство .length возвращает количество элементов. Формула Math.floor(Math.random() * arr.length) даёт случайный индекс, который гарантированно попадает в пределы массива.

Нажми кнопку

const phrases = [
  'Дизайн — это код',
  'Ошибки — это прогресс',
  'Браузер — это холст',
  'CSS — это магия',
]
 
const i = Math.floor(Math.random() * phrases.length)
display.textContent = phrases[i]
 
/* Массив — список значений в [ ].
   phrases.length — сколько элементов.
   phrases[i] — элемент по индексу.
   Формула Math.floor(Math.random() * arr.length)
   даёт случайный индекс. Запомните её. */
      

Массив фраз, кнопка выбирает случайную и выводит на экран. Маленький штрих — opacity на мгновение сбрасывается до нуля и через setTimeout возвращается обратно, создавая лёгкий эффект смены. Массив можно заполнить чем угодно: цветами, координатами, URL-адресами, CSS-классами, HTML-тегами.

Случайное позиционирование

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

Кликните — точки перемешаются

.rand-dot {
  position: absolute;
  border-radius: 50%;
  transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* transition делает перемешивание
   анимированным — точки перелетают,
   а не телепортируются. */
      

for (let i = 0; i < 12; i++) {
  const dot = document.createElement('div')
  const size = 10 + Math.random() * 30
  dot.style.width = size + 'px'
  dot.style.height = size + 'px'
  dot.style.left = Math.random() * 90 + '%'
  dot.style.top = Math.random() * 85 + '%'
  root.appendChild(dot)
}
/* for создаёт 12 точек.
   Каждая получает случайный размер (10–40px),
   случайную позицию (в процентах от контейнера)
   и случайный цвет из палитры. */
      

12 точек, каждая со случайным размером (10—40px), позицией и цветом из палитры. Клик перемешивает их — все точки перелетают на новые случайные позиции. CSS transition делает перемещение анимированным: точки не телепортируются, а плавно перелетают. Эта связка JS (вычислил новые координаты) + CSS (анимировал переход) — типичный паттерн для динамичных веб-плакатов.

Генерация элементов циклом

Следующий шаг — создавать элементы целиком через JavaScript, а не размечать их вручную в HTML. Цикл for позволяет создать десятки элементов с разными параметрами за несколько строк.


function generate() {
  root.querySelectorAll('.gen-item').forEach(el => el.remove())
  const count = 15 + Math.floor(Math.random() * 20)
 
  for (let i = 0; i < count; i++) {
    const el = document.createElement('div')
    el.style.width = (10 + Math.random() * 60) + 'px'
    el.style.height = (10 + Math.random() * 40) + 'px'
    el.style.left = Math.random() * 95 + '%'
    el.style.top = Math.random() * 85 + '%'
    el.style.transform = `rotate(${Math.random() * 360}deg)`
    root.appendChild(el)
  }
}
/* Каждый вызов: удаляем старые, создаём новые.
   Даже количество элементов случайное (15–35).
   Размер, позиция, поворот — всё через Math.random().
   Каждый клик — новая композиция. */
      

При каждом клике старые элементы удаляются remove(), и создаются новые — от 15 до 35 штук. Каждый со случайным размером, позицией, поворотом, цветом и прозрачностью. По сути, каждый клик создаёт новую абстрактную композицию. Именно так устроен плакат «Склянка» из нашей подборки — при каждой загрузке страница пересобирается.

document.createElement('div') создаёт элемент, но не добавляет его на страницу. Чтобы он стал видимым, нужно вставить его через root.appendChild(element). А чтобы удалить — element.remove(). Создание и удаление элементов на лету — это то, чего CSS не умеет и для чего нужен JavaScript.

Объект с набором свойств

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


function makeCreature() {
  return {
    name: names[Math.floor(Math.random() * names.length)],
    face: faces[Math.floor(Math.random() * faces.length)],
    color: colors[Math.floor(Math.random() * colors.length)],
    size: 40 + Math.floor(Math.random() * 40),
    energy: Math.floor(Math.random() * 100)
  }
}
/* Объект { } — набор свойств с именами.
   Функция makeCreature() каждый раз
   возвращает нового персонажа
   со случайными параметрами.
   Клик на каждом — перегенерация. */
      

Функция makeCreature() возвращает объект с пятью свойствами: имя, символ, цвет, размер и энергия. Каждое свойство выбирается случайно из своего массива или диапазона. Клик на любом существе пересоздаёт его с новыми параметрами.

Объект в JavaScript записывается в фигурных скобках: { name: 'Blob', color: '#fe3904', size: 60 }. Свойства читаются через точку: creature.name, creature.color. Это способ группировать связанные данные вместе, вместо того чтобы хранить их в отдельных переменных.

Перемешивание массива

Иногда нужно не выбрать один случайный элемент, а перемешать весь массив — изменить порядок элементов, как карты в колоде. Для этого существует алгоритм Фишера-Йейтса.


function shuffle(arr) {
  const copy = [...arr]
  for (let i = copy.length - 1; i >0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [copy[i], copy[j]] = [copy[j], copy[i]]
  }
  return copy
}
/* Алгоритм Фишера-Йейтса.
   Проходим массив с конца, каждый элемент
   меняем местами со случайным.
   [...arr] — копия, чтобы не менять оригинал.
   Результат — честное перемешивание. */
      

Шесть пронумерованных блоков. Кнопка перемешивает их порядок. Алгоритм проходит массив с конца и каждый элемент меняет местами со случайным из оставшихся. Запись [...arr] создаёт копию массива, чтобы не менять оригинал. Это важно, когда вам нужно перемешивать одни и те же данные многократно.

Собираем всё вместе

Финальный пример объединяет всё, что мы разобрали. Восемь фигур со случайными размерами, формами, позициями и прозрачностью. Клик перегенерирует всю композицию, а CSS transition анимирует переход — фигуры перелетают, морфятся и меняют цвет.

Кликайте для перегенерации

.comp-shape {
  position: absolute;
  transition: all 0.6s ease;
}
/* transition на фигурах — при перегенерации
   они не телепортируются,
   а плавно перелетают на новые места. */
      

function randomize() {
  shapes.forEach(shape => {
    const size = 20 + Math.random() * 120
    const isCircle = Math.random() >0.4
    shape.style.width = size + 'px'
    shape.style.height = size + 'px'
    shape.style.borderRadius = isCircle ? '50%' : '4px'
    shape.style.left = Math.random() * 85 + '%'
    shape.style.top = Math.random() * 75 + '%'
    shape.style.opacity = (0.2 + Math.random() * 0.5).toFixed(2)
  })
}
/* 8 фигур пересобираются при каждом клике.
   Размер, форма, позиция, прозрачность —
   всё случайное. CSS transition анимирует
   переход между состояниями.
   Каждый клик — новый плакат. */
      

Это, по сути, простейший генеративный плакат. Заголовок фиксирован, а фон каждый раз новый. Добавьте сюда setInterval — и композиция будет пересобираться автоматически каждые несколько секунд, без участия зрителя.

Случайность работает лучше всего в рамках ограничений. Пять цветов из палитры, а не бесконечный hsl(Math.random()*360, 70%, 50%). Размеры от 20 до 120 пикселей, а не от 1 до 1000. Чем уже диапазон случайности — тем целостнее результат. Полный хаос редко выглядит хорошо, а контролируемая случайность в заданных рамках создаёт живые и при этом гармоничные композиции.

Понравилась статья?

Забыли главное или есть что предложить?
Напишите нам в телеграм

Читайте также