Сегодня мы рассмотрим, как создать эффект motion blur и применить его к анимациям CSS с помощью SVG.
▶Посмотреть на GitHub
▶Демо (рус.)
Motion blur — это широко используемая технология в анимационном дизайне и анимации вообще, чтобы придать движению более мягкий и естественный вид.
Motion blur (произносится: моушн блёр) — размытие изображения при воспроизведении сцен движения или быстро движущихся объектов.
В этой статье мы рассмотрим, как можно воссоздать этот эффект для горизонтальных и вертикальных перемещений.
Внимание: Пожалуйста, имейте ввиду, что этот проект несет чрезвычайно экспериментальный характер и поддерживается лишь некоторыми современными браузерами. На данный момент Chrome показывает наилучшие результаты…
Чтобы применить эффект motion blur, нужно нанести размытие по горизонтали на объект в соответствии с его скоростью и направлением, для каждого кадра в отдельности.
Устанавливаем размытие
Так как стандартный фильтр размытия CSS не поддерживает размытие в определенном направлении, мы будем использовать фильтры SVG.
Для желаемого результата, мы будем использовать лишь фильтр feGaussianBlur.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="filters">
<defs>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0,0" />
</filter>
</defs>
</svg>
Параметр stdDeviation используется для установки интенсивности размытия и может принимать два значение, одно для горизонтального, другое для вертикального размытия.
Применить фильтр к нужному элементу довольно просто:
.selector{
-webkit-filter: url("#blur");
filter: url("../index.html#blur");
}
Однако, нам необходимо изменять значение параметра для каждого кадра. Сделать это можно с помощью JS.
Для начала нам нужно выбрать и затем поместить фильтр в переменную, чтобы мы могли обратиться к нему позже. Так как jQuery не особо дружит с элементами SVG, мы будем использовать стандартные функции JS:
var filters = document.querySelector(".filters"), // SVG, который содержить фильтры
defs = filters.querySelector("defs"), // элемент внутри SVG
blur = defs.querySelector("#blur"), // фильтр размытия
blurFilter = blur.firstElementChild; // feGaussianBlur
Изменять интенсивность нужно с помощью корректировки значений параметра stdDeviation. Например, размытие по горизонтали в 12px:
blurFilter.setAttribute("stdDeviation","12,0");
Так как данный фильтр поддерживает только размытия в двух направлениях, по оси X и по оси Y, то нужно планировать анимацию заранее.
Заметьте, что изменение значение фильтра касается всех объектов, с которыми он связан. Поэтому нам нужно создать новый элемент <filter>
для каждого нового объекта, к которому мы хотим применить фильтр. Вот простой способ, как можно динамически создать эти фильтры:
// проходим через все объекты, которым нужен фильтр размытия
$(".js-blur").each(function(i){
// клонируем фильтр
var blurClone=blur.cloneNode(true);
// создаем и назначаем новый ID, чтобы использовать фильтр в CSS
var blurId="blur"+i;
blurClone.setAttribute("id",blurId);
defs.appendChild(blurClone);
// назначаем CSS
var filter="url(#"+blurId+")";
$(this)
.css({
webkitFilter:filter,
filter:filter
})
// храним упоминание фильтра
.data("blur",blurClone)
;
});
Измеряем скорость
Теперь нам нужно рассчитать, как далеко объект переместился по сравнению с последним его положением. Это нужно сделать для каждого кадра. Способ реализации напрямую зависит от того, как все настроено, как выполнена анимация и т.д. В нашем уроке мы используем наиболее общий подход, который будет работать с большинством анимаций CSS.
Чтобы получить положение объекта, мы будем использовать функцию jQuery offset. Она возвращает координаты объекта относительно всего документа.
Чтобы проверять изменения для каждого кадра, будем использовать requestAnimationFrame.
Вот пример:
// элемент, к которому применяем эффект
var $element=$(".selector");
// храним последнее положение, чтобы измерить изменения
var lastPos=$element.offset();
// коэффициент для контроля за интенсивностью эффекта
var multiplier=0.25;
// помощь в назначении размытия
function setBlur(x,y){
blurFilter.setAttribute("stdDeviation",x+","+y);
}
(function updateMotionBlur(){
// получаем текущее положение элемента
var currentPos=$element.offset();
// считаем изменения с момента последнего кадра и меняем коэффциент
var xDiff=Math.abs(currentPos.left-lastPos.left)*multiplier;
var yDiff=Math.abs(currentPos.top-lastPos.top)*multiplier;
// назначаем размытие
setBlur(xDiff,yDiff);
// храним текущее положение для следующего кадра
lastPos=currentPos;
// обновляем следующий кадр
requestAnimationFrame(updateMotionBlur);
})();
И вот наш результат:
Это базовый подход для применения эффекта лишь к одному элементу. Под каждый случай нужно оптимизировать код в отдельности.
Ну вот и все! Примите во внимание, что данный эффект требует довольно-таки много памяти, поэтому не стоит применять его для больших объектов.
От переводчика. Со всеми пожеланиями и замечаниями по поводу перевода прошу обращаться ко мне в личку. Спасибо!
Автор: shimapa23