Привет Хабр! Попалась недавно интересная вещичка , которая создает эффект конфетти на страничке. Решил глянуть , что же там внутри находится, как работает и познакомиться с канвасом поближе. Подробности под катом.
Вступление
Повествование будет вестись с точки зрения человека, который первый раз в жизни увидел канвас ( ладно, второй, первой была вот эта статья с хабра про фрактальные снежинки) и хочет почерпнуть себе разные полезные моменты, которые , возможно, захочется использовать в будущем. Отталкивался от исходного кода библиотечки canvas-confetti.
Настройки
Удобно представить себе некую пушку, которая находится в некой точке origin, наклонена под углом angle и стреляет зарядом в виде конуса, который отклоняется от направления выстрела влево и вправо на угол spread со скоростью startVelocity . Потом частицы начинают терять скорость в зависимости от сопротивления воздуха decay и падать под действием силы тяжести gravity. Еще есть параметры для колличества частиц, цвета, формы, размера ( particleCount, colors, shapes, scalar). Достаточно добавить только интересующие опции, остальные подтянутся по умолчанию.
confetti({ particleCount: 100, startVelocity: 30, spread: 360, origin: { x: Math.random(), y: Math.random() - 0.2 }}
Полезные моменты внутри
Штука, которая обеспечивает 60 кадров в секунду с помощью requestAnimationFrame , если есть или откатывается к setTimeout
var raf = (function () { var TIME = Math.floor(1000 / 60); var frame, cancel; var frames = {}; var lastFrameTime = 0; if (typeof requestAnimationFrame === 'function' && typeof cancelAnimationFrame === 'function') { frame = function (cb) { var id = Math.random(); frames[id] = requestAnimationFrame(function onFrame(time) { if (lastFrameTime === time || lastFrameTime + TIME - 1 < time) { lastFrameTime = time; delete frames[id]; cb(); } else { frames[id] = requestAnimationFrame(onFrame); } }); return id; }; cancel = function (id) { if (frames[id]) { cancelAnimationFrame(frames[id]); } }; } else { frame = function (cb) { return setTimeout(cb, TIME); }; cancel = function (timer) { return clearTimeout(timer); }; } return { frame: frame, cancel: cancel };}());
Заполнение канвасом всей видимой области странички с помощью createElement, appendChild, clientWidth, clientHeight
function getCanvas(zIndex) { var canvas = document.createElement('canvas'); canvas.style.position = 'fixed'; canvas.style.top = '0px'; canvas.style.left = '0px'; canvas.style.pointerEvents = 'none'; canvas.style.zIndex = zIndex; return canvas; }// ..... document.body.appendChild(canvas);// .....function setCanvasWindowSize(canvas) { canvas.width = document.documentElement.clientWidth; canvas.height = document.documentElement.clientHeight;}
Получение двумерного контекста канваса с getContext
var context = canvas.getContext('2d');
Чистка, которая происходит перед отрисовкой каждого кадра с помощью clearRect в методе update
context.clearRect(0, 0, size.width, size.height);
Для создания каждого кадра вызывается update, внутри которого для каждой "конфетиточки" вызывается код, который считает ее геометрические координаты и рисует ее с помощью методов контекста beginPath, moveTo, lineTo, closePath и fill. Также каждая фетишка отслеживает сколько у нее прошло кадров-апдейтов и потом, когда у всех фетишек закончатся кадры, анимация отрапортует о своем завершении.
function updateFetti(context, fetti) {// ... // пара десятков строк косинусов и синусов, которые посчитают новые координаты для конфетишки // ... context.fillStyle = 'rgba(' + fetti.color.r + ', ' + fetti.color.g + ', ' + fetti.color.b + ', ' + (1 - progress) + ')'; context.beginPath(); // ... context.moveTo(Math.floor(fetti.x), Math.floor(fetti.y)); context.lineTo(Math.floor(fetti.wobbleX), Math.floor(y1)); context.lineTo(Math.floor(x2), Math.floor(y2)); context.lineTo(Math.floor(x1), Math.floor(fetti.wobbleY)); context.closePath(); context.fill(); // ...// когда кадры закончатся фетишка отфильтруется из массива частиц для апдейта return fetti.tick < fetti.totalTicks; }
Заключение
Надеюсь этот пост поможет кому-нибудь быстро ознакомится с рисованием на двумерном канвасе. В исходнике есть много полезного касательно анимации из воркера и геометрии. Желающие могут ознакомиться более детально по ссылке вначале статьи.