Статьи

Введение в переходы CSS3

Красивое приложение должно предоставлять пользователю визуальную обратную связь. Пользователь должен всегда знать, что заказ (щелчок, нажатие или что-то еще) хорошо воспринят и понят приложением, и анимации являются отличным инструментом для этого.

Новая спецификация HTML 5 (если честно, я должен сказать «новая спецификация CSS 3 ») представляет отличный инструмент для обработки простых анимаций: переходы .

В соответствии со спецификацией «Модуль переходов CSS уровня 3» на сайте W3C , переходы CSS3 позволяют плавно изменять свойства в значениях CSS в течение указанной продолжительности .

Целью этой статьи будет сначала описать концепцию переходов, а затем посмотреть, как работает CSS3 Transitions и как мы можем работать с браузерами, которые не поддерживают эту функцию:HTML5 работает с CSS3 / Styling

  1. CSS3 переходы
  2. Собираем все вместе
  3. Переходы без переходов CSS3 
  4. Вывод
  5. Идти дальше

Кроме того, я предлагаю вам прочитать « Введение в анимацию CSS3 » (автор Дэвид Руссе ), которая является отличным компаньоном для этой статьи.

Чтобы увидеть, как можно использовать переходы CSS3, я разработал пример игры, в которой переходы CSS3 используются для анимации ячеек головоломки (и откат к JavaScript, если ваш браузер не поддерживает переходы CSS3): 

Чтобы запустить игру, просто нажмите «Mix it!» и попробуйте решить головоломку, нажав на ячейки!

Код этой игры доступен здесь .

CSS3 переходы

Вступление

Вначале рабочая группа CSS W3C сопротивлялась добавлению переходов в CSS, утверждая, что переходы на самом деле не являются стилевыми свойствами. Но в итоге дизайнерам и разработчикам удалось убедить их, что переходы связаны с динамическими стилями и могут происходить в файле CSS.

Согласно сайту W3C, CSS3 Transitions может анимировать следующие типы свойств: ( нажмите здесь, чтобы показать их )

И следующие свойства должны поддерживаться для переходов: ( нажмите здесь, чтобы показать их )

SVG

Свойства объектов SVG являются анимируемыми, когда они определены как animatable: true в спецификации SVG: http://www.w3.org/TR/SVG/struct.html .

Объявления

Чтобы объявить переход в файле CSS, вам просто нужно написать следующий код:

    transition-property: all;
    transition-duration: 0.5s;
    transition-timing-function: ease;
    transition-delay: 0s;

Это объявление определяет, что любое обновление любого свойства будет выполнено за 0,5 с (а не сразу Sourire). 

Вы также можете определить свои переводы для каждого свойства:

    transition-property: opacity left top;
    transition-duration: 0.5s 0.8s 0.1s;
    transition-timing-function: ease linear ease;
    transition-delay: 0s 0s 1s;

И, наконец, вы можете использовать сокращенное свойство « transition » для определения всего, что вам нужно, в одной строке:

transition: all 0.5s ease 0s;

В этой сокращенной версии вы можете указать столько свойств, сколько вы хотите, через запятую:

    transition: opacity 0.5s ease 0s, left 0.8s linear 0s;

Переходы будут срабатывать при обновлении свойства целевого объекта. Обновление можно выполнить с помощью JavaScript или CSS3, назначив новый класс тегу .

Например, используя IE10, если у вас есть следующее объявление CSS3:

    -ms-transition-property: opacity left top;
    -ms-transition-duration: 0.5s 0.8s 0.5s;
    -ms-transition-timing-function: ease linear ease;

Когда вы обновите непрозрачность вашего тега, текущее значение будет анимировано до нового значения в течение 0,5 с с помощью функции замедления по времени (которая обеспечивает плавную анимацию).

Нелинейные переходы

Строка «Функция перехода времени» определяет, что переход не будет линейным, но будет использовать функцию синхронизации для создания нелинейной анимации.

По сути, переходы CSS3 будут использовать кубическую кривую Безье для сглаживания перехода путем вычисления различной скорости в течение его продолжительности.

Поддерживаются следующие функции:

  • линейный : постоянная скорость
  • cubic-bezier : Скорость будет рассчитываться в соответствии с кубической кривой Безье, определяемой двумя контрольными точками: P0 и P1 (поэтому вам нужно будет определить 4 значения здесь: P0x, P0y и P1x, P1y.
  • легкость : скорость будет вычислена с помощью кубического Безье (0,25, 0,1, 0,25, 1)
  • непринужденность : скорость будет вычислена с помощью кубического Безье (0,42, 0, 1, 1)
  • easy-inout : скорость будет вычислена с помощью кубического Безье (0,42, 0, 0,58, 1)
  • замедление : скорость будет вычислена с помощью кубического Безье (0, 0, 0,58, 1)

Вот инструмент моделирования (с использованием SVG, конечно Sourire), чтобы показать влияние каждой функции синхронизации:

Этот симулятор написан с чистым кодом JavaScript, чтобы облегчить понимание функции:

    TRANSITIONSHELPER.computeCubicBezierCurveInterpolation = function (t, x1, y1, x2, y2) {
        // Extract X (which is equal to time here)
        var f0 = 1 - 3 * x2 + 3 * x1;
        var f1 = 3 * x2 - 6 * x1;
        var f2 = 3 * x1;
     
        var refinedT = t;
        for (var i = 0; i < 5; i++) {
            var refinedT2 = refinedT * refinedT;
            var refinedT3 = refinedT2 * refinedT;
     
            var x = f0 * refinedT3 + f1 * refinedT2 + f2 * refinedT;
            var slope = 1.0 / (3.0 * f0 * refinedT2 + 2.0 * f1 * refinedT + f2);
            refinedT -= (x - t) * slope;
            refinedT = Math.min(1, Math.max(0, refinedT));
        }
     
        // Resolve cubic bezier for the given x
        return 3 * Math.pow(1 - refinedT, 2) * refinedT * y1 +
                3 * (1 - refinedT) * Math.pow(refinedT, 2) * y2 +
                Math.pow(refinedT, 3);
    };

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

задержка

Строка «transition-delay» определяет задержку между обновлением свойства и началом перехода

События

Событие возникает в конце перехода: « TransitionEnd ». В соответствии с вашим браузером правильное имя будет:

  • Chrome & Safari: webkitTransitionEnd
  • Firefox: mozTransitionEnd
  • Опера: oTransitionEnd
  • Internet Explorer: MSTransitionEnd

Событие предоставит вам следующую информацию:

  • propertyName : имя анимированного свойства
  • elapsedTime : время выполнения перехода в секундах.

Вот пример использования для IE10:

    block.addEventListener("MSTransitionEnd", onTransitionEvent);

Подробнее о переходах CSS3

В основном я могу предложить две причины, по которым переходы CSS3 действительно полезны:

  • Аппаратное ускорение: переходы CSS3 напрямую обрабатываются на графическом процессоре (если он доступен) и дают более плавные результаты. И это действительно важно на мобильных устройствах, где вычислительная мощность действительно ограничена
  • Лучшее разделение между кодом и дизайном : для меня разработчик не должен знать об анимации или чем-либо, связанном с дизайном. Точно так же дизайнер / художник не должен знать о JavaScript. Вот почему CSS3 Transitions действительно интересны, так как дизайнеры могут описывать все переходы в CSS без необходимости разработчиков

 

Поддержка и запасной вариант

Поскольку PP3, IE10 (который вы можете скачать с Windows 8 Developer Preview здесь ) поддерживает переходы CSS3:

образ

Этот отчет был создан с помощью http://caniuse.com/#search=CSS3 переходов .

Конечно, поскольку спецификация не закончена ( рабочий проект ), вы должны использовать префиксы поставщика, такие как -ms-, -moz-, -webkit-, -o-.

Очевидно, мы видим, что нам необходимо предоставить прозрачное решение для всех типов браузеров. Наилучшим способом будет разработка API, который сможет определять поддержку переходов CSS3. Если браузер не поддерживает эту функцию, мы вернемся к некоторому коду JavaScript.

Важно поддерживать запасной метод, если вы полагаетесь на переходы для функциональности веб-сайтов. Если вы не хотите этого делать, вам следует рассмотреть возможность использования переходов только для улучшения дизайна . В этом случае сайт все еще будет работать, но только поддерживаемые браузеры будут работать в полном объеме. Мы говорим здесь о « прогрессивных улучшениях », поскольку чем мощнее браузер, тем больше возможностей он получает.

Переходы без переходов CSS3

Таким образом, чтобы иметь возможность поддерживать откат к переходам CSS3, мы разработаем небольшой инструментарий для обеспечения переходов по коду.

Прежде всего, мы создадим объект контейнера для нашего пространства имен:

    var TRANSITIONSHELPER = TRANSITIONSHELPER || {};
     
    TRANSITIONSHELPER.tickIntervalID = 0;
     
    TRANSITIONSHELPER.easingFunctions = {
        linear:0,
        ease:1,
        easein:2,
        easeout:3,
        easeinout:4,
        custom:5
    };
     
    TRANSITIONSHELPER.currentTransitions = [];

Чтобы поддерживать одинаковый уровень функций замедления, мы должны объявить «enum» со всеми обязательными полями.

Инструментарий основан на функции, которая вызывается каждые 17 мс (для достижения анимации при 60 кадрах в секунду). Функция будет перечислять через коллекцию активных переходов. Для каждого перехода код будет оценивать следующее значение с учетом текущего значения и целевого значения.

Нам понадобятся некоторые удобные функции для извлечения значений используемых свойств и единиц:

    TRANSITIONSHELPER.extractValue = function (string) {
        try {
            var result = parseFloat(string);
     
            if (isNaN(result)) {
                return 0;
            }
     
            return result;
        } catch (e) {
            return 0;
        }
    };
     
    TRANSITIONSHELPER.extractUnit = function (string) {
     
        // if value is empty we assume that it is px
        if (string == "") {
            return "px";
        }
     
        var value = TRANSITIONSHELPER.extractValue(string);
        var unit = string.replace(value, "");
     
        return unit;
    };

Основная функция обработает активные переходы и вызовет функцию кубического Безье для оценки текущих значений:

    TRANSITIONSHELPER.tick = function () {
        // Processing transitions
        for (var index = 0; index < TRANSITIONSHELPER.currentTransitions.length; index++) {
            var transition = TRANSITIONSHELPER.currentTransitions[index];
     
            // compute new value
            var currentDate = (new Date).getTime();
            var diff = currentDate - transition.startDate;
     
            var step = diff / transition.duration;
            var offset = 1;
     
            // Timing function
            switch (transition.ease) {
                case TRANSITIONSHELPER.easingFunctions.linear:
                    offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0, 0, 1.0, 1.0);
                    break;
                case TRANSITIONSHELPER.easingFunctions.ease:
                    offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0.25, 0.1, 0.25, 1.0);
                    break;
                case TRANSITIONSHELPER.easingFunctions.easein:
                    offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0.42, 0, 1.0, 1.0);
                    break;
                case TRANSITIONSHELPER.easingFunctions.easeout:
                    offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0, 0, 0.58, 1.0);
                    break;
                case TRANSITIONSHELPER.easingFunctions.easeinout:
                    offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, 0.42, 0, 0.58, 1.0);
                    break;
                case TRANSITIONSHELPER.easingFunctions.custom:
                    offset = TRANSITIONSHELPER.computeCubicBezierCurveInterpolation(step, transition.customEaseP1X, transition.customEaseP1Y, transition.customEaseP2X, transition.customEaseP2Y);
                    break;
            }
     
            offset *= (transition.finalValue - transition.originalValue);
     
            var unit = TRANSITIONSHELPER.extractUnit(transition.target.style[transition.property]);
            var currentValue = transition.originalValue + offset;
     
            transition.currentDate = currentDate;
     
            // Dead transition?
            if (currentDate >= transition.startDate + transition.duration) {
                currentValue = transition.finalValue; // Clamping
                TRANSITIONSHELPER.currentTransitions.splice(index, 1); // Removing transition
                index--;
     
                // Completion event
                if (transition.onCompletion) {
                    transition.onCompletion({propertyName:transition.property, elapsedTime:transition.duration});
                }
            }
     
            // Affect it
            transition.target.style[transition.property] = currentValue + unit;
        }
    };

Текущая версия инструментария поддерживает только числовые значения, но если вы хотите анимировать сложные значения (например, цвет), вам просто нужно разложить их на простые значения.

Регистрация перехода в системе будет осуществляться с использованием следующего кода:

    TRANSITIONSHELPER.transition = function (target, property, newValue, duration, ease, customEaseP1X, customEaseP1Y, customEaseP2X, customEaseP2Y, onCompletion) {
     
        // Create a new transition
        var transition = {
            target: target,
            property: property,
            finalValue: newValue,
            originalValue: TRANSITIONSHELPER.extractValue(target.style[property]),
            duration: duration,
            startDate: (new Date).getTime(),
            currentDate: (new Date).getTime(),
            ease:ease,
            customEaseP1X:customEaseP1X,
            customEaseP2X:customEaseP2X,
            customEaseP1Y: customEaseP1Y,
            customEaseP2Y: customEaseP2Y,
            onCompletion: onCompletion
        };
     
        // Launching the tick service if required
        if (TRANSITIONSHELPER.tickIntervalID == 0) {
            TRANSITIONSHELPER.tickIntervalID = setInterval(TRANSITIONSHELPER.tick, 17);
        }
        
        // Remove previous transitions on same property and target
        for (var index = 0; index < TRANSITIONSHELPER.currentTransitions.length; index++) {
            var temp = TRANSITIONSHELPER.currentTransitions[index];
     
            if (temp.target === transition.target && temp.property === transition.property) {
                TRANSITIONSHELPER.currentTransitions.splice(index, 1);
                index--;
            }
        }
     
        // Register
        if (transition.originalValue != transition.finalValue) {
            TRANSITIONSHELPER.currentTransitions.push(transition);
        }
    };

Функция « галочка » запускается при активации первого перехода.

Наконец, вам просто нужно использовать modernizr, чтобы определить, поддерживается ли переходы CSS3 текущим браузером. Если нет, то вы можете Откат к нашему инструментарию. 

Код для TransitionsHelper можно скачать здесь: http://www.catuhe.com/msdn/transitions/transitionshelper.js 

Например, в моей игре-головоломке для анимации ячеек используется следующий код:

    if (!PUZZLE.isTransitionsSupported) {
        TRANSITIONSHELPER.transition(block.div, "top", block.x * totalSize + offset, 500, TRANSITIONSHELPER.easingFunctions.ease);
        TRANSITIONSHELPER.transition(block.div, "left", block.y * totalSize + offset, 500, TRANSITIONSHELPER.easingFunctions.ease);
    }
    else {
        block.div.style.top = (block.x * totalSize + offset) + "px";
        block.div.style.left = (block.y * totalSize + offset) + "px";
    }

Мы можем заметить, что я мог бы использовать другой способ для анимации моих ячеек, когда поддерживаются переходы CSS3: я мог бы определить коллекцию классов CSS3 с предопределенными левым и верхним значениями (по одному для каждой ячейки), чтобы воздействовать на них для правых ячеек.

Некоторые платформы и наборы инструментов уже существуют для поддержки программных переходов:

Кстати, вы также можете использовать старый добрый метод animate () jQuery.

Вывод

Как мы уже видели, CSS3 Transitions — это действительно простой способ добавить анимацию в ваш проект. Вы можете создать более реактивное приложение, просто используя некоторые переходы, когда вы хотите изменить значения.

Кстати, есть два решения, если вы хотите реализовать запасной вариант JavaScript:

  • Вы можете делать все на стороне JavaScript, и если вы обнаружите поддержку переходов CSS3, вы добавите объявления CSS3 на страницу.
  • Или вы можете использовать стандартный способ (используя настоящие объявления CSS3 в файлах CSS) и просто обнаружить необходимость возврата в JavaScript. Для меня это лучший вариант, поскольку запасной вариант должен быть вариантом, а не основным предметом. В ближайшем будущем все браузеры будут поддерживать переходы CSS3, и в этом случае вам просто нужно будет удалить запасной код. Кроме того, это лучший способ оставить весь CSS под контролем творческой группы, а не в части кода.

Идти дальше