Мой опыт
<canvas>
привлек мое внимание в 2006 году, когда был выпущен Firefox v2.0. Статья на Ajaxian , описывающая матрицу преобразования, вдохновила меня на создание моего первого веб-приложения <canvas>
; Цветная сфера (2007). Что погрузило меня в мир цветов и графических примитивов; вдохновил на создание Sketchpad (2007–2008) с целью создать в браузере приложение «лучше, чем Paint». Эти эксперименты в конечном итоге привели к созданию стартапа Mugtug с моим давним другом Чарльзом Притчардом. Мы разрабатываем Darkroom на HTML5 <canvas>
. Darkroom — это неразрушающее приложение для обмена фотографиями, сочетающее в себе возможности пиксельных фильтров с векторной типографикой и рисованием.
Введение
<canvas>
дает Javascript-программистам полный контроль над цветами , векторами и пикселями на экранах — визуальным составом монитора.
Следующие примеры относятся к одной области <canvas>
, которой не уделяется много внимания; создание текстовых эффектов . Разнообразие текстовых эффектов, которые можно создать с помощью <canvas>
настолько велико, насколько вы можете себе представить — эти демонстрации охватывают лишь часть того, что возможно. Хотя в этой статье мы имеем дело с «текстом», методы можно применять к любым векторным объектам ; создание захватывающих визуальных эффектов в играх и других приложениях:
- Текстовые тени в
<canvas>
. - Текстовые эффекты в стиле CSS в
<canvas>
, создание обтравочных масок, поиск показателей в<canvas>
и использование свойстваshadow. - Неоново-радуга, зебра-отражение – цепочка эффектов.
- Текстовые эффекты в стиле Photoshop в
<canvas>
— примеры использования globalCompositeOperation, createLinearGradient, createPattern. - Внутренние и внешние тени в
<canvas>
- Раскрываем малоизвестную особенность ; использование намотки по часовой стрелке или против часовой стрелки для создания обратной тени ( внутренняя тень ).
- Spaceage – генеративный эффект.
- Генеративный текстовый эффект в
<canvas>
с использованием циклического изменения цвета hsl() иwindow.requestAnimationFrame
для создания ощущения движения.
Текстовые тени на холсте
Одно из моих любимых дополнений к спецификациям CSS3 (наряду с border-radius, веб-градиентами и другими) — возможность создавать тени. Важно понимать различия между тенями CSS и <canvas>
, а именно:
CSS использует два метода; box-shadow для блочных элементов, таких как div, span и т. д.; и text-shadow для текстового контента.
<canvas>
имеет один тип тени; он используется для всех векторных объектов; ctx.moveTo, ctx.lineTo, ctx.bezierCurveTo, ctx.quadradicCurveTo, ctx.arc, ctx.rect, ctx.fillText, ctx.strokeText и т. д. Чтобы создать тень в <canvas>
, коснитесь этих четырех свойств:
- ctx.shadowColor = "красный" // строка
- Цвет тени; Допустимы RGB, RGBA, HSL, HEX и другие входы.
- ctx.shadowOffsetX = 0; // целое число
- Горизонтальное расстояние тени относительно текста.
- ctx.shadowOffsetY = 0; // целое число
- Расстояние тени по вертикали относительно текста.
- ctx.shadowBlur = 10; // целое число
- Эффект размытия тени: чем больше значение, тем сильнее размытие.
Для начала давайте посмотрим, как <canvas>
может эмулировать эффекты CSS. Поиск в изображениях Google по запросу «css text-shadow» привел к появлению нескольких отличных демонстраций, которые мы могли подражать; Line25 , Стереоскопический и Тень 3D .
Стереоскопический 3D-эффект (подробнее см. в разделе «Анаглифное изображение ») — это пример простой строки кода, которая нашла широкое применение. С помощью следующей строки CSS мы можем создать иллюзию глубины при просмотре в 3D-красно-голубых очках (таких, которые дают при просмотре 3D-фильмов):
text-shadow: -0.06em 0 0 red, 0.06em 0 0 cyan;
При преобразовании этой строки в <canvas>
следует обратить внимание на две вещи:
- Размытие тени (третье значение) отсутствует, поэтому нет смысла фактически запускать тень, поскольку fillText приведет к тем же результатам:
var text = "Hello world!"
ctx.fillStyle = "#000"
ctx.fillText(text, -7, 0);
ctx.fillStyle = "red"
ctx.fillText(text, 0, 0);
ctx.fillStyle = "cyan"
ctx.fillText(text, 7, 0);</pre>
- EM не поддерживаются в
<canvas>
, поэтому их необходимо преобразовать в PX. Мы можем найти коэффициент преобразования для преобразования между PT, PC, EM, EX, PX и т. д., создав элемент с теми же свойствами шрифта в DOM и установив ширину в соответствии с измеряемым форматом; Или, например, чтобы зафиксировать преобразование EM -> PX, мы измерим элемент DOM с «высотой: 1em», результирующее значение offsetHeight будет равно количеству PX в каждом EM.
var font = "20px sans-serif"
var d = document.createElement("span");
d.style.cssText = "font: " + font + " height: 1em; display: block"
// the value to multiply PX 's by to convert to EM 's
var EM2PX = 1 / d.offsetHeight;</pre>
Предотвращение альфа-умножения
В более сложном примере, таком как эффект «Неон» в Line25, для правильной эмуляции эффекта необходимо использовать свойствоshadowBlur. Поскольку эффект Неона основан на нескольких тенях, мы сталкиваемся с проблемой; в <canvas>
каждый векторный объект может иметь только одну тень. Итак, чтобы нарисовать несколько теней, вы должны нарисовать несколько версий текста поверх самого себя. Это приводит к альфа-умножению и, в конечном итоге, к неровным краям.
Я попробовал запустить ctx.fillStyle = "rgba(0,0,0,0)"
или "transparent"
, чтобы скрыть текст и отобразить тень… однако эта попытка оказалась тщетной; поскольку тень является произведением альфа fillStyle, тень никогда не может быть более непрозрачной, чем fillStyle.
К счастью, есть способ обойти эту проблему: мы можем нарисовать смещение тени от текста, сохраняя их разделенными (чтобы они не перекрывались) и тем самым скрывая текст за пределами экрана:
var text = "Hello world!"
var blur = 10;
var width = ctx.measureText(text).width + blur * 2;
ctx.textBaseline = "top"
ctx.shadowColor = "#000"
ctx.shadowOffsetX = width;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -width, 0);
Обрезка текстового блока
Чтобы немного это исправить, мы можем в первую очередь запретить отрисовку fillText (но позволяя рисовать тень), добавив обтравочный контур. Чтобы создать обтравочный контур вокруг текста, нам нужно знать высоту текста (исторически называемую «em-высотой» — высоту буквы «М» на печатной машине) и ширину текста. Мы можем получить ширину, используя ctx.measureText().width
, однако ctx.measureText().height
не существует.
К счастью, с помощью CSS-хаков ( см. «Типографские метрики» , чтобы узнать больше о способах исправления старых реализаций <canvas>
с помощью измерений CSS ), мы можем найти высоту текста, измеряя offsetHeight
<span>
с теми же свойствами шрифта. :
var d = document.createElement("span");
d.font = "20px arial"
d.textContent = "Hello world!"
var emHeight = d.offsetHeight;
Отсюда мы можем создать прямоугольник, который будет использоваться в качестве обтравочного контура; заключив «тень» и удалив фиктивную форму.
ctx.rect(0, 0, width, emHeight);
ctx.clip();
Связываем все это вместе и оптимизируем по ходу работы — если тень не имеет размытия, для того же эффекта можно использовать fillText, избавляя нас от настройки обтравочной маски:
var width = ctx.measureText(text).width;
var style = shadowStyles[text];
// add a background to the current effect
ctx.fillStyle = style.background;
ctx.fillRect(0, offsetY, ctx.canvas.width, textHeight - 1)
// parse text-shadows from css
var shadows = parseShadow(style.shadow);
// loop through the shadow collection
var n = shadows.length; while(n--) {
var shadow = shadows[n];
var totalWidth = width + shadow.blur * 2;
ctx.save();
ctx.beginPath();
ctx.rect(offsetX - shadow.blur, offsetY, offsetX + totalWidth, textHeight);
ctx.clip();
if (shadow.blur) { // just run shadow (clip text)
ctx.shadowColor = shadow.color;
ctx.shadowOffsetX = shadow.x + totalWidth;
ctx.shadowOffsetY = shadow.y;
ctx.shadowBlur = shadow.blur;
ctx.fillText(text, -totalWidth + offsetX, offsetY + metrics.top);
} else { // just run pseudo-shadow
ctx.fillStyle = shadow.color;
ctx.fillText(text, offsetX + (shadow.x||0), offsetY - (shadow.y||0) + metrics.top);
}
ctx.restore();
}
// drawing the text in the foreground
if (style.color) {
ctx.fillStyle = style.color;
ctx.fillText(text, offsetX, offsetY + metrics.top);
}
// jump to next em-line
ctx.translate(0, textHeight);
Поскольку вам не понадобится вводить все эти команды <canvas>
вручную, я включил в исходный код демонстрации простой анализатор текстовых теней; таким образом вы можете передать ему команды CSS и заставить его генерировать команды <canvas>
. Теперь наши элементы <canvas>
имеют целый ряд стилей, к которым они могут быть привязаны. Эти же теневые эффекты можно использовать для любого векторного объекта: от веб-шрифтов до сложных фигур, импортированных из SVG, до генеративных векторных фигур и так далее!
Антракт (касательная при нажатии пикселя)
При написании этого раздела статьи меня заинтересовал стереоскопический пример. Насколько сложно было бы создать эффект 3D-киноэкрана, используя <canvas>
и два изображения, снятые с немного разных точек зрения? Судя по всему, не слишком сложно. Следующее ядро объединяет красный канал первого изображения (данные) с голубым каналом второго изображения (данные2):
data[i] = data[i] * 255 / 0xFF;
data[i+1] = 255 * data2[i+1] / 0xFF;
data[i+2] = 255 * data2[i+2] / 0xFF;
Теперь кому-то просто нужно приклеить изолентой два iPhone ко лбу, одновременно нажать «записать видео», и мы сможем создавать свои собственные 3D-фильмы в HTML5. Есть добровольцы?
Неоновая радуга, отражение зебры — цепочка эффектов
Объединение нескольких эффектов в <canvas>
может быть простым, но необходимы базовые знания globalCompositeOperation (GCO). Чтобы сравнить операции с GIMP (или Photoshop): есть 12 GCO в <canvas>
darker , а более светлый можно рассматривать как режимы наложения слоев; остальные 10 операций применяются к слоям как альфа-маски (один слой удаляет пиксели другого слоя). GlobalCompositeOperation связывает «слои» (или, в нашем случае, строки кода) вместе, комбинируя их новыми и интересными способами:
Диаграмма globalCompositeOperation показывает рабочие режимы GCO; эта диаграмма использует большую часть цветового спектра и несколько уровней альфа-прозрачности, чтобы детально увидеть, чего ожидать. Я бы порекомендовал проверить ссылку на globalCompositeOperation Mozilla для текстовых описаний. Для дальнейшего исследования вы можете узнать, как работает эта операция, в книге Porter Duff’s Compositing Digital Images .
Мой любимый режим — globalCompositeOperation="lighter". Светлее смешивает добавленные пиксели аналогично тому, как смешивается свет; когда красный, зеленый и белый свет имеют полную интенсивность, мы видим белый свет. Это интересная функция, с которой можно поиграть, особенно когда для <canvas>
установлено низкое значение globalAlpha; обеспечивая более точный контроль и более плавные края. Lighter нашел множество применений, моим недавним фаворитом стал инструмент для создания фона рабочего стола HTML5, который можно найти на http://weavesilk.com/ . В одной из моих демонстраций, Breathing Galaxies (JS1k), также используется более светлый режим — рисуя шаблоны из этих двух примеров, вы начинаете видеть, какой эффект дает этот режим.
Обработка браузером globalCompositeOperation .
Неоново-радужный эффект джиттера
В следующей демонстрации мы собираемся добиться неонового радужного свечения в стиле Photoshop с дрожащим контуром, объединяя эффекты вместе с помощью globalCompositeOperation (источник, светлее и темнее). Эта демонстрация является развитием демонстрации «Text-Shadows in <canvas>
», в которой используется та же стратегия отделения тени от текста (см. предыдущий раздел):
function neonLightEffect() {
var text = "alert('"+String.fromCharCode(0x2665)+"')";
var font = "120px Futura, Helvetica, sans-serif";
var jitter = 25; // the distance of the maximum jitter
var offsetX = 30;
var offsetY = 70;
var blur = getBlurValue(100);
// save state
ctx.save();
ctx.font = font;
// calculate width + height of text-block
var metrics = getMetrics(text, font);
// create clipping mask around text-effect
ctx.rect(offsetX - blur/2, offsetY - blur/2,
offsetX + metrics.width + blur, metrics.height + blur);
ctx.clip();
// create shadow-blur to mask rainbow onto (since shadowColor doesn't accept gradients)
ctx.save();
ctx.fillStyle = "#fff";
ctx.shadowColor = "rgba(0,0,0,1)";
ctx.shadowOffsetX = metrics.width + blur;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -metrics.width + offsetX - blur, offsetY + metrics.top);
ctx.restore();
// create the rainbow linear-gradient
var gradient = ctx.createLinearGradient(0, 0, metrics.width, 0);
gradient.addColorStop(0, "rgba(255, 0, 0, 1)");
gradient.addColorStop(0.15, "rgba(255, 255, 0, 1)");
gradient.addColorStop(0.3, "rgba(0, 255, 0, 1)");
gradient.addColorStop(0.5, "rgba(0, 255, 255, 1)");
gradient.addColorStop(0.65, "rgba(0, 0, 255, 1)");
gradient.addColorStop(0.8, "rgba(255, 0, 255, 1)");
gradient.addColorStop(1, "rgba(255, 0, 0, 1)");
// change composite so source is applied within the shadow-blur
ctx.globalCompositeOperation = "source-atop";
// apply gradient to shadow-blur
ctx.fillStyle = gradient;
ctx.fillRect(offsetX - jitter/2, offsetY,
metrics.width + offsetX, metrics.height + offsetY);
// change composite to mix as light
ctx.globalCompositeOperation = "lighter";
// multiply the layer
ctx.globalAlpha = 0.7
ctx.drawImage(ctx.canvas, 0, 0);
ctx.drawImage(ctx.canvas, 0, 0);
ctx.globalAlpha = 1
// draw white-text ontop of glow
ctx.fillStyle = "rgba(255,255,255,0.95)";
ctx.fillText(text, offsetX, offsetY + metrics.top);
// created jittered stroke
ctx.lineWidth = 0.80;
ctx.strokeStyle = "rgba(255,255,255,0.25)";
var i = 10; while(i--) {
var left = jitter / 2 - Math.random() * jitter;
var top = jitter / 2 - Math.random() * jitter;
ctx.strokeText(text, left + offsetX, top + offsetY + metrics.top);
}
ctx.strokeStyle = "rgba(0,0,0,0.20)";
ctx.strokeText(text, offsetX, offsetY + metrics.top);
ctx.restore();
};
Эффект отражения зебры
Эффект «Отражение зебры» был создан на основе превосходного ресурса WebDesignerWall о том, как оживить вашу страницу с помощью CSS. Это развивает идею немного дальше, создавая «отражение» текста — например, то, что вы можете увидеть в iTunes. Эффект сочетает в себе fillColor (белый), createPattern (zebra.png) и линейный градиент (блеск); это иллюстрирует возможность применения нескольких типов заливки к каждому векторному объекту:
function sleekZebraEffect() {
// inspired by - http://www.webdesignerwall.com/demo/css-gradient-text/
var text = "Sleek Zebra...";
var font = "100px Futura, Helvetica, sans-serif";
// save state
ctx.save();
ctx.font = font;
// getMetrics calculates:
// width + height of text-block
// top + middle + bottom baseline
var metrics = getMetrics(text, font);
var offsetRefectionY = -20;
var offsetY = 70;
var offsetX = 60;
// throwing a linear-gradient in to shine up the text
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.1, '#000');
gradient.addColorStop(0.35, '#fff');
gradient.addColorStop(0.65, '#fff');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient
ctx.fillText(text, offsetX, offsetY + metrics.top);
// draw reflected text
ctx.save();
ctx.globalCompositeOperation = "source-over";
ctx.translate(0, metrics.height + offsetRefectionY)
ctx.scale(1, -1);
ctx.font = font;
ctx.fillStyle = "#fff";
ctx.fillText(text, offsetX, -metrics.height - offsetY + metrics.top);
ctx.scale(1, -1);
// cut the gradient out of the reflected text
ctx.globalCompositeOperation = "destination-out";
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.0, 'rgba(0,0,0,0.65)');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient;
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height);
// restore back to original transform state
ctx.restore();
// using source-atop to allow the transparent .png to show through to the gradient
ctx.globalCompositeOperation = "source-atop";
// creating pattern from <image> sourced.
ctx.fillStyle = ctx.createPattern(image, 'repeat');
// fill the height of two em-boxes, to encompass both normal and reflected state
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height * 2);
ctx.restore();
};
Внутренние/внешние тени в Canvas
Спецификации <canvas>
не затрагивают тему «внутренних» и «внешних» теней. Фактически, на первый взгляд можно ожидать, что «внутренняя» тень не поддерживается. Это не так. Просто включить его немного сложнее ;) Как было предложено в недавнем сообщении F1LT3R , вы можете создавать внутренние тени, используя уникальные свойства правил намотки по часовой стрелке и против часовой стрелки. Для этого вы создаете «внутреннюю тень», рисуя прямоугольник контейнера, а затем, используя противоположные правила намотки, рисуете фигуру выреза, создавая обратную форму.
В следующем примере можно одновременно стилизовать внутреннюю тень и стиль заливки с помощью цвета+градиента+узора. Вы можете указать вращение шаблона индивидуально; обратите внимание, что полосы зебры теперь перпендикулярны друг другу. Используется обтравочная маска размером с ограничивающую рамку, что устраняет необходимость в сверхбольшом контейнере для размещения вырезаемой формы, что повышает скорость за счет предотвращения обработки ненужных частей тени.
function innerShadow() {
function drawShape() { // draw anti-clockwise
ctx.arc(0, 0, 100, 0, Math.PI * 2, true); // Outer circle
ctx.moveTo(70, 0);
ctx.arc(0, 0, 70, 0, Math.PI, false); // Mouth
ctx.moveTo(-20, -20);
ctx.arc(30, -30, 10, 0, Math.PI * 2, false); // Left eye
ctx.moveTo(140, 70);
ctx.arc(-20, -30, 10, 0, Math.PI * 2, false); // Right eye
};
var width = 200;
var offset = width + 50;
var innerColor = "rgba(0,0,0,1)";
var outerColor = "rgba(0,0,0,1)";
ctx.translate(150, 170);
// apply inner-shadow
ctx.save();
ctx.fillStyle = "#000";
ctx.shadowColor = innerColor;
ctx.shadowBlur = getBlurValue(120);
ctx.shadowOffsetX = -15;
ctx.shadowOffsetY = 15;
// create clipping path (around blur + shape, preventing outer-rect blurring)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
ctx.clip();
// apply inner-shadow (w/ clockwise vs. anti-clockwise cutout)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
drawShape();
ctx.fill();
ctx.restore();
// cutout temporary rectangle used to create inner-shadow
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
// prepare vector paths
ctx.beginPath();
drawShape();
// apply fill-gradient to inner-shadow
ctx.save();
ctx.globalCompositeOperation = "source-in";
var gradient = ctx.createLinearGradient(-offset/2, 0, offset/2, 0);
gradient.addColorStop(0.3, '#ff0');
gradient.addColorStop(0.7, '#f00');
ctx.fillStyle = gradient;
ctx.fill();
// apply fill-pattern to inner-shadow
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 1;
ctx.rotate(0.9);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();
// apply fill-gradient
ctx.save();
ctx.globalCompositeOperation = "destination-over";
var gradient = ctx.createLinearGradient(-offset/2, -offset/2, offset/2, offset/2);
gradient.addColorStop(0.1, '#f00');
gradient.addColorStop(0.5, 'rgba(255,255,0,1)');
gradient.addColorStop(1.0, '#00f');
ctx.fillStyle = gradient
ctx.fill();
// apply fill-pattern
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 0.2;
ctx.rotate(-0.4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();
// apply outer-shadow (color-only without temporary layer)
ctx.globalCompositeOperation = "destination-over";
ctx.shadowColor = outerColor;
ctx.shadowBlur = 40;
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 10;
ctx.fillStyle = "#fff";
ctx.fill();
};
Из этих примеров вы можете видеть, что, используя globalCompositeOperation, мы можем объединять эффекты в цепочку, создавая более сложные эффекты (используя маскирование и смешивание). Экран - твоя устрица ;)
Spaceage — генерирующие эффекты
В <canvas>
начиная с символа Юникода 0x2708:
… к этому затененному примеру:
…может быть достигнуто путем нескольких вызовов ctx.strokeText()
с тонкой линиейWidth (0,25), при этом медленно уменьшая смещение по оси X и альфу; придавая нашим векторным элементам ощущение движения.
Сопоставляя положение элементов XY с синусоидальной/косинусоидальной волной и циклически меняя цвета с помощью свойства HSL, мы можем создавать более интересные эффекты, такие как этот пример «биологической опасности»:
HSL: оттенок, насыщенность, легкость (1978)
HSL — это новый формат, поддерживаемый в спецификациях CSS3. Если HEX был разработан для компьютеров, то HSL удобен для чтения человеком.
Иллюстрируя простоту HSL; для циклического переключения цветового спектра мы просто увеличиваем «оттенок» с 360; оттенок отображается на спектре цилиндрическим образом. «Яркость» определяет, насколько темным/светлым будет цвет; 0% указывает на черный пиксель, тогда как 100% указывает на белый пиксель. Насыщенность определяет, насколько ярким будет цвет; серый цвет создается с насыщенностью 0%, а яркие цвета создаются с использованием значения 100%.
Поскольку HSL является недавним стандартом, возможно, вы захотите продолжить поддержку старых браузеров, что возможно посредством преобразования цветового пространства. Следующий код принимает объект HSL { H: 360, S: 100, L: 100} и выводит объект RGB { R: 255, G: 255, B: 255 }. Оттуда вы можете использовать эти значения для создания строки RGB или RGBA. Для получения более подробной информации см. содержательную статью Википедии о HSL .
// HSL (1978) = H: Hue / S: Saturation / L: Lightness
HSL_RGB = function (o) { // { H: 0-360, S: 0-100, L: 0-100 }
var H = o.H / 360,
S = o.S / 100,
L = o.L / 100,
R, G, B, _1, _2;
function Hue_2_RGB(v1, v2, vH) {
if (vH < 0) vH += 1;
if (vH > 1) vH -= 1;
if ((6 * vH) < 1) return v1 + (v2 - v1) * 6 * vH;
if ((2 * vH) < 1) return v2;
if ((3 * vH) < 2) return v1 + (v2 - v1) * ((2 / 3) - vH) * 6;
return v1;
}
if (S == 0) { // HSL from 0 to 1
R = L * 255;
G = L * 255;
B = L * 255;
} else {
if (L < 0.5) {
_2 = L * (1 + S);
} else {
_2 = (L + S) - (S * L);
}
_1 = 2 * L - _2;
R = 255 * Hue_2_RGB(_1, _2, H + (1 / 3));
G = 255 * Hue_2_RGB(_1, _2, H);
B = 255 * Hue_2_RGB(_1, _2, H - (1 / 3));
}
return {
R: R,
G: G,
B: B
};
};
Создание анимации с помощью requestAnimationFrame
Раньше для создания анимации в Javascript было два варианта; setTimeout
и setInterval
.
window.requestAnimationFrame
— новый стандарт, заменяющий оба; экономия мирового электричества (и вашего компьютера на несколько ударов сердца), позволяя браузеру регулировать анимацию на основе доступных ресурсов. Некоторые важные функции включают в себя:
- Когда пользователь находится в кадре, анимация может замедлиться или полностью остановиться, чтобы предотвратить использование ненужных ресурсов.
- Существует ограничение на частоту кадров на уровне 60FPS. Причина этого в том, что это намного выше уровня, который люди могут заметить (большинство людей при 30 кадрах в секунду видят анимацию «плавной»).
На момент написания для использования requestAnimationFrame
требовались префиксы, специфичные для поставщика. Пол Айриш создал в requestAnimationFrame слой прокладки с поддержкой разных поставщиков для умной анимации :
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
Если пойти дальше, более амбициозные люди могут связать это вместе с поли-заполнением, таким как requestAnimationFrame.js (есть несколько функций, которые предстоит проработать), которые будут в большей степени поддерживать старые браузеры при переходе на этот новый стандарт. .
(function animate() {
var i = 50;
while(i--) {
if (n > endpos) return;
n += definition;
ctx.globalAlpha = (0.5 - (n + startpos) / endpos) * alpha;
if (doColorCycle) {
hue = n + color;
ctx.strokeStyle = "hsl(" + (hue % 360) + ",99%,50%)"; // iterate hue
}
var x = cos(n / cosdiv) * n * cosmult; // cosine
var y = sin(n / sindiv) * n * sinmult; // sin
ctx.strokeText(text, x + xoffset, y + yoffset); // draw rainbow text
}
timeout = window.requestAnimationFrame(animate, 0);
})();
Исходный код
Благодаря поддержке со стороны производителей браузеров нет никаких сомнений в будущем <canvas>
его можно портировать на исполняемые файлы iPhone/Android/Desktop с помощью PhoneGap или
Титан .