Мы прошли долгий путь от основ браузера. Такие инструменты, как Three.js и WebGL Studio, библиотеки анимации, такие как Raphael.js или GSAP, растущая популярность SVG и все более благоприятные изменения Javascript за последнее десятилетие изменили анимацию. в браузере из случайной новинки во что-то реальное и интересное для игры. HTML-холст — это отличный способ начать создавать интерактивные визуальные эффекты прямо в браузере. Но анимации много, и лучше всего начать с малого. В этом уроке мы узнаем, как создать простое облако частиц, используя полноэкранный холст, анимировать его и заставить реагировать на действия пользователя.
К концу этого урока вы создадите элемент, состоящий из
маленьких цветных частиц, которые вращаются вокруг положения мыши; когда левая кнопка мыши удерживается нажатой, эффект частиц будет масштабироваться, чтобы окружить весь экран. Надеемся, что эта статья даст любому новичку в Canvas несколько идей для экспериментов.
Попробуйте демо здесь: http://particle-vortex.herokuapp.com/
Полный исходный код читайте здесь: https://github.com/IsaacBell/Canvas-Particle-Vortex
Начальные переменные
Давайте сначала настроим начальные условия и переменные для эффекта. Таким образом, мы можем вносить глобальные изменения в одном месте. Здесь мы установим необходимые переменные для элемента canvas.
# Initial Setup
fps = 30 width = window.innerWidth height = window.innerHeight r = 70 scale = 1 scaleMin = 12.5 scaleMax = 100 particleCount = 250 canvas = undefined # You can of course omit this line context = undefined # Ditto particles = undefined # Ditto mouseX = width * 0.5 mouseY = height * 0.5 isMouseDown = false
Мы устанавливаем переменную для хранения размера окна дисплея, мы передадим эту информацию элементу холста, чтобы определить его границы. Переменная r
будет определять радиус облака частиц. scale изменяет размер облака, scaleMin
позволяет нам установить абсолютный минимальный масштаб облака, а scaleMax
позволяет установить максимальный масштаб, которого достигнет облако, когда пользователь удерживает мышь. particleCount
позволяет увеличить или уменьшить количество частиц, в которых появляется элемент; слишком много, и мы столкнемся с проблемами производительности. Для этой демонстрации вполне подойдет 30 кадров в секунду.
Наша функция инициализации
init = ->
# Create canvas element
canvas = document.createElement('canvas')
canvas.id = 'myCanvas'
document.body.appendChild(canvas)
if canvas and canvas.getContext
context = canvas.getContext('2d')
# Event handlers
window.addEventListener 'mousemove', onMouseMove, false
window.addEventListener 'mousedown', onMouseDown, false
window.addEventListener 'mouseup', onMouseUp, false
window.addEventListener 'onResize', onResize, false
createParticles()
onResize()
setInterval animLoop, 1000 / fps
return
Вот что здесь происходит в коде:
Шаг 1
Мы создаем элемент холста и устанавливаем для него идентификатор соответствующего HTML-элемента «myCanvas». Вам понадобится элемент с этим идентификатором в вашем HTML для сопоставления.
Шаг 2
Мы присоединяем (добавляем) элемент холста к телу нашего HTML. Если мы пропустим этот шаг, холст никогда не появится, так как он нигде не рисуется/рендерится в HTML-документе.
Шаг 3
Если холст настроен правильно и мы можем выполнить настройку с помощью getContext()
-> Мы устанавливаем наш контекст рисования на «2D»
-› Мы добавляем прослушиватели событий для событий мыши и изменения размера браузера
-> Мы вызываем createParticles(), который мы будем использовать для создания нашего облака частиц
-> Мы вызываем onResize(), который подгоняет наш элемент холста под нужные пропорции.
-> Мы устанавливаем animLoop() для повторения со скоростью нашей переменной fps
Цвет
Первая функция, которую мы определим, это colorLuminance(). Нам это нужно, когда мы генерируем наши частицы; мы будем использовать это, когда будем осветлять или затемнять шестнадцатеричные цвета, которые мы будем генерировать при создании нашего облака частиц. colorLuminance() принимает два аргумента, первый из которых представляет собой шестнадцатеричную строку, представляющую цвет, а второй представляет собой десятичное число от 1 до -1, указывающее, насколько осветлить или затемнить шестнадцатеричный цвет.
Для получения более подробной информации о том, что здесь происходит, ознакомьтесь с этой статьей Крейга Баклера.
colorLuminance = (hex, lum) -> # validate hex string hex = String(hex).replace(/[^0-9a-f]/gi, '') if hex.length < 6 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] lum = lum or 0
# convert to decimal and change luminosity rgb = '#' c = undefined i = undefined i = 0 while i < 3 c = parseInt(hex.substr(i * 2, 2), 16) c = Math.round(Math.min(Math.max(0, c + c * lum), 255)).toString(16) rgb += ('00' + c).substr(c.length) i++ return rgb
С этим покончено, давайте создадим функцию, которая будет служить основной частью нашего проекта.
Создание наших частиц
Мы будем использовать цикл while для создания массива частиц со случайными скоростями движения, цветом заливки и расстоянием по орбите от центра облака. Значение fillColor
использует функцию colorLuminance()
, которую мы только что добавили ранее; поиграйте с этой линией, чтобы найти цветовую гамму, которая вам нравится. Скорость и орбитальное расстояние каждой частицы в некоторой степени рандомизированы путем умножения значений, умноженных на значение, возвращаемое Math.random()
. Это очень распространенный шаблон в обработке графики/цвета.
createParticles = ->
particles = []
i = 0
while i < particleCount
particle =
size: 5
position:
x: mouseX
y: mouseY
offset:
x: 0
y: 0
shift:
x: mouseX
y: mouseY
speed: 0.02 + Math.random() * 0.02
targetSize: 1
fillColor: colorLuminance('#' + Math.random().toString(16), -0.23)
orbit: r / 3 * Math.random()
particles.push particle
i++
return
Ответ прослушивателям событий
Далее мы напишем функции, которые будут запускаться прослушивателями событий, которые мы настроили ранее. Ничего сложного, мы просто сохраняем динамическое положение мыши и статус mouseDown в наших переменных верхнего уровня. Наконец, при изменении размера окна браузера мы изменим размеры элемента холста.
# Simple event Listener functions
onMouseMove = (e) -> mouseX = e.clientX - ((window.innerWidth - width) * .5) mouseY = e.clientY - ((window.innerHeight - height) * .5) return
onMouseDown = -> isMouseDown = true
onMouseUp = -> isMouseDown = false
onResize = -> width = window.innerWidth height = window.innerHeight canvas.width = width canvas.height = height return
Цикл анимации
Теперь о самом цикле рендеринга. Вот где настоящее действие; не пугайтесь, глядя на это.
Вот разбивка того, что мы делаем:
- Масштабируем облако частиц до максимального или минимального размера в зависимости от того, нажата ли мышь.
- С помощью context.fillStyle() мы устанавливаем цвет для рисования фигур. Мы выбираем черный цвет с непрозрачностью 55%.
- С помощью context.fillRect() мы рисуем границы прямоугольника холста, который мы будем заполнять.
- Используя простой цикл while, мы перебираем частицы и поворачиваем, сдвигаем и плавно увеличиваем каждую по мере необходимости. Чем быстрее переменная fps, тем быстрее эти сдвиги и повороты будут казаться глазу.
- После того, как мы рассчитали новый размер и положение нашей частицы на каждой итерации, мы используем API холста для рисования нашего круга.
animLoop = -> if isMouseDown # Expand the cloud when mouse is clicked down scale += (scaleMax - scale) * 0.2 else # Or else shrink the cloud down scale -= (scale - scaleMin) * 0.2 # Apply whichever change we set above scale = Math.min(scale, scaleMax)
# Set our line opacity and limit our drawing board # size to the size of the screen context.fillStyle = 'rgba(0,0,0,0.55)' context.fillRect 0, 0, context.canvas.width, context.canvas.height i = 0 len = particles.length while i < len particle = particles[i] # Rotation particle.offset.x += particle.speed * 0.7 particle.offset.y += particle.speed * 0.7 # Follow the mouse, with a bit of blur/lag effect particle.shift.x += (mouseX - (particle.shift.x)) * particle.speed * 0.6 particle.shift.y += (mouseY - (particle.shift.y)) * particle.speed * 0.6 # Shift the particles accordingly, using a cosine function particle.position.x = particle.shift.x + Math.cos(i + particle.offset.x) * particle.orbit * scale particle.position.y = particle.shift.y + Math.sin(i + particle.offset.y) * particle.orbit * scale # Limit our animation to the screen bounds particle.position.x = Math.max(Math.min(particle.position.x, width), 0) particle.position.y = Math.max(Math.min(particle.position.y, height), 0) particle.size += (particle.targetSize - (particle.size)) * 0.05 if Math.round(particle.size) == Math.round(particle.targetSize) particle.targetSize = 1 + Math.random() * 10 # Finally, let's do some drawing! context.beginPath() # Select line color at random context.fillStyle = particle.fillColor context.strokeStyle = particle.fillColor context.lineWidth = particle.size context.moveTo particle.position.x, particle.position.y context.lineTo particle.position.x, particle.position.y context.stroke() context.arc particle.position.x, particle.position.y, particle.size / 2, 0, Math.PI * 2, true context.fill() i++ return
Наконец, нам нужно вызвать нашу функцию инициализации, чтобы сбросить все.
window.onload = init
Вывод
Надеюсь, вы сможете получить представление о том, как перенести полностью интерактивные визуальные эффекты в веб-браузер с помощью холста. Эту демонстрацию можно легко расширить, оптимизировать или изменить. Есть большой потенциал. Поэкспериментируйте с переменными, скачайте исходники на Github, поэкспериментируйте в браузере.
Репозиторий Github: https://github.com/IsaacBell/Canvas-Particle-Vortex