Определить, можно ли анимировать свойство с помощью перехода CSS3?

Список свойств, которые можно анимировать с помощью перехода CSS3, различается в разных браузерах и может меняться в новых версиях браузеров. Например, -moz-transform нельзя анимировать с -moz-transition в FF3.6, но можно в FF4.

Итак, есть ли способ определить в JavaScript, является ли конкретное свойство анимируемым? Я бы не хотел использовать обнюхивание пользовательского агента, поскольку это ненадежно.

Заранее спасибо!


person Mourner    schedule 13.01.2011    source источник
comment
@Mourner Было бы круто, если бы вы могли не принять мой ответ и принять ответ Джордана, только для людей, которые ищут такие вещи в Google.   -  person methodofaction    schedule 29.06.2012


Ответы (2)


Редактировать: см. ответ Джордана, чтобы узнать о хорошем методе обнаружения анимируемых свойств.

Боюсь, нет простого способа определить, является ли свойство анимируемым. Однако свойства по большей части совпадают (единственная проблема, с которой я столкнулся, - это переход FF4 + тень текста + преобразование).

http://www.w3.org/TR/css3-transitions/#the-transition-property-property-#properties-from-css-

Firefox 3.6 не поддерживает переходы css, вы можете обнаружить это с помощью библиотеки js, такой как Modernizr:

http://www.modernizr.com/

person methodofaction    schedule 13.01.2011
comment
Спасибо! Я был уверен, что была какая-то стабильная версия FF, где была поддержка перехода без анимационного преобразования, но оказалось, что это было в некоторых бета-версиях FF4, и теперь все вроде бы в порядке. - person Mourner; 13.01.2011
comment
Я думал об этом для другого проекта, и это действительно возможно — см. ниже. :) - person Jordan Gray; 28.06.2012

Да, есть способ. Далее следует демонстрация, объяснение ниже. Есть несколько очень важных предостережений, так что обязательно прочитайте.

Следующий код проверяет, может ли браузер выполнять анимацию между двумя значениями.

Код

демонстрация jsFiddle.

/*
@param property  The property to test.
@param from      A valid starting value for the animation.
@param to        A valid ending value for the animation.
@param [element] The element to test with. (Required for testing
                 properties with prerequisites, e.g. "top" requires
                 non-static position.)
*/
function isAnimationSupported(property, from, to, element) {
    var doc = document.documentElement,
        style = doc.appendChild(document.createElement("style")),
        rule = [
                'capTest{',
                    '0%{',   property, ':', from, '}',
                    '100%{', property, ':', to,   '}',
                '}'
               ].join(''),
        propCamel = property.toCamelCase(),
        prefixes = 'moz ms o webkit '.split(' '), // Unprefixed last, see comments.
        prefixCount = prefixes.length,
        canAnimate = false;

    element = doc.appendChild((element)
            ? element.cloneNode(false)
            : document.createElement('div'));

    // Detect invalid start value. (Webkit tries to use default.)
    element.style[propCamel] = to;

    // Iterate through supported prefixes.
    for (var i = 0; i < prefixCount; i++) {

        // Variations on current prefix.
        var prefix  = prefixes[i],
            hPrefix = (prefix) ? '-' + prefix + '-' : '',
            uPrefix = (prefix) ? prefix.toUpperCase() + '_' : '';

        // Test for support.
        if (CSSRule[uPrefix + 'KEYFRAMES_RULE']) {

            // Rule supported; add keyframe rule to test stylesheet.
            style.sheet.insertRule('@'+ hPrefix + 'keyframes ' + rule, 0);

            // Apply animation.
            var animationProp = (hPrefix + 'animation').toCamelCase();
            element.style[animationProp] = 'capTest 1s 0s both';

            // Get initial computed style.
            var before = getComputedStyle(element)[propCamel];

            // Skip to last frame of animation.
            // BUG: Firefox doesn't support reverse or update node style while
            // attached.
            doc.removeChild(element);
            element.style[animationProp] = 'capTest 1s -1s alternate both';
            doc.appendChild(element);
            // BUG: Webkit doesn't update style when animation skipped ahead.
            element.style[animationProp] = 'capTest 1s 0 reverse both';

            // Get final computed style.
            var after = getComputedStyle(element)[propCamel];

            // If before and after are different, property and values are animable.
            canAnimate = before !== after;
            break;
        }
    }

    // Clean up the test elements.
    doc.removeChild(element);
    doc.removeChild(style);

    return canAnimate;
}

// Cribbed from Lea Verou's prefixfree.
String.prototype.toCamelCase = function() {
    return this.replace(/-([a-z])/g, function($0, $1) { return $1.toUpperCase(); })
               .replace('-','');
};

Как использовать

Обязательными аргументами для этого являются свойство для анимации и начальное и конечное значения, которые оно должно принимать. При желании вы можете передать элемент с другим исходным набором стилей, например. position: absolute. (Функция клонирует элемент, так что вы можете передавать узлы из документа, и они не будут изменены.) Если вы не передаете какой-либо элемент, анимация тестируется на div с любыми стилями по умолчанию, применяемыми АП.

Как это работает

Правило анимации по ключевым кадрам добавляется в фиктивную таблицу стилей, где для начального кадра устанавливается значение from, а для конечного кадра — значение to. Эта анимация применяется к элементу. Затем мы проверяем вычисленный стиль для анимированного свойства, чтобы увидеть, отличается ли он, когда анимация начинается с начального кадра, по сравнению с тем, когда она начинается с последнего кадра.

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

Предостережения (прочитайте перед использованием, некоторые из них неприятны!)

Есть несколько несоответствий в том, как браузеры обрабатывают анимацию. Пару из них я обработал как можно более перспективным способом; однако некоторые из них неразрешимы.

В частности, Firefox анимирует значения позиции (например, left) для статических элементов, в то время как другие (например, Webkit и Opera) этого не делают. На самом деле он не перемещает элемент, но значение этого свойства обновляется. Таким образом, вы получите разные результаты в разных браузерах, если попытаетесь анимировать значение позиции без передачи нестатически позиционированного элемента.

Самые последние версии основных браузеров, которые поддерживают переходы CSS, также поддерживают ключевые кадры CSS, хотя некоторые более старые версии поддерживают первое, но не второе. (Например, Опера 11.)

Наконец, если бы я делал это более элегантно, я бы использовал prefixfree, чтобы определить правильный префикс для использования напрямую. ; в настоящее время я тестирую массив префиксов, начиная с версии без префикса.

person Jordan Gray    schedule 26.06.2012
comment
+1 Очень круто! Это дает false на top right left bottom, потому что тестовый div не абсолютно позиционирован. Я не уверен, как с этим справиться, возможно, вы можете передать элемент, чтобы переопределить тестовый div? - person methodofaction; 29.06.2012
comment
Хорошая точка зрения! Добавление необязательного параметра элемента кажется хорошей идеей; На самом деле я дошел до того, что обновил скрипку, когда мой широкополосный доступ отключился. Я обновлю объяснение, когда вернусь в онлайн! :) - person Jordan Gray; 29.06.2012
comment
Прохладный! Еще одним хорошим тестом будет анимация преобразований css на встроенных элементах, которые должны потерпеть неудачу. - person methodofaction; 29.06.2012
comment
@Duopixel О боже. Я внес некоторые существенные изменения в то, как это работает, но обратите особое внимание на предостережения. tl;dr: Firefox анимирует свойства положения статических элементов. Также обратите внимание, что оба браузера анимируют матрицу преобразования для встроенных элементов, поэтому он возвращает положительное значение для анимации преобразований как для встроенных, так и для блочных элементов. С положительной стороны, обновленная версия сообщит вам, можете ли вы анимировать между обоими свойствами, поэтому возвращает false, например. Webkit при анимации от значения в пикселях до значения в процентах. - person Jordan Gray; 29.06.2012
comment
Это так странно. Преобразование не применяется к вычисляемому стилю в веб-инспекторе со встроенными элементами. Я попробую. - person methodofaction; 29.06.2012
comment
@Duopixel Это так. Вычисленное значение стиля определенно изменяется; Демонстрация только для Webkit: jsfiddle.net/Jordan/kpPNx (я предполагаю, что она применяется в случае дисплей обновляется позже.) - person Jordan Gray; 29.06.2012
comment
Вы правы, я думаю, что это настолько хорошо, насколько это возможно. Я обновил свой ответ, чтобы указать на ваш. - person methodofaction; 29.06.2012
comment
Chrome выдает ошибку «Не удалось проанализировать правило @keyframes capTest{0%{height:1px}100%{height:100px}}». Я думаю, что версии с префиксом должны быть первыми, а затем без префикса следующим образом: prefixes = 'moz ms o webkit '.split(' '),. - person Timo Kähkönen; 09.11.2014
comment
Привет, @Timo, извините за отсутствие ответа! Я проверил это, и вы правы насчет ошибки Chrome; Я обновлю свой ответ; может вернуться к этому позже, потому что я тестировал в Chrome ранее, и тогда он не вызывал исключения! - person Jordan Gray; 17.11.2014
comment
@Timo И, естественно: спасибо! Очень ценю, что вы заметили это и предложили исправить. :) - person Jordan Gray; 17.11.2014
comment
Это может быть связано с тем, что Chrome удалил префикс в какой-то последней версии. Ответ с 2012 года и тогда нужен был префикс. - person Timo Kähkönen; 17.11.2014
comment
@ Тимо А, конечно, я был тупицей. На самом деле это то, что я намеревался (чтобы по возможности использовалась версия без префикса), но по какой-то причине моя ментальная логика была обратной, и я по ошибке поставил ее первой. - person Jordan Gray; 17.11.2014