Omi использует веб-анимацию со скоростью 60 кадров в секунду

внешний интерфейс JavaScript React.js Omi Zepto.js
Omi использует веб-анимацию со скоростью 60 кадров в секунду

написать впереди

Фреймворк Омиофициально выпущен→ оми-преобразование.

Made css3 transform super easy. Made 60 FPS easy.

Являясь решением Omi для разработки специальных эффектов движения на основе компонентов, оно позволяет быстро и легко поддерживать настройки CSS3 Transform в проектах Omi. css3transform был крещен большим количеством проектов.Как решение для мобильных веб-спецэффектов, он широко используется в WeChat, мобильном племени интересов QQ, Riji, группе QQ, районе QQ и других проектах.ИсправлятьЗа счет свойств DOM это обеспечивает чрезвычайно удобную программируемость.

ты можешь пройтиcss3transform официальная домашняя страницаПознакомьтесь с ним быстро.

Все примеры на официальном сайте выше — это нативный js, а у css3transform также есть версия для реакции Вы также можете использовать css3transform декларативным образом в реакции:

render() {
    return (
        <Transform
          translateX={100}
          scaleX={0.5}
          originX={0.5}>
          <div>你要运动的 DOM</div>
        </Transform>
    )
}

Суть не в этом, а в оми-трансформации.

Освойте оми-трансформацию за 3 минуты

Установить через нпм

npm install omi-transform

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

import { render, WeElement, define } from "omi"
import "omi-transform"

define("my-app", class extends WeElement {
  static observe = true

  install() {
    this.data.rotateZ = 30
    this.linkRef = (e) => {
      this.animDiv = e
    }
  }

  installed() {
    setInterval(() => {
      //slow
      //this.data.rotateZ += 2

      //fast
      this.animDiv.rotateZ += 2
      //sync for update call of any scenario
      this.data.rotateZ = this.animDiv.rotateZ
    }, 16)
  }

  render(props, data) {
    return (
      <css3-transform rotateZ={data.rotateZ} translateX={0} perspective={0} >
        <div ref={this.linkRef}>
          omi-transform
        </div>
      </css3-transform>
    )
  }
})

render(<my-app />, "body")
  • Используйте DOM, который нужно переместить<css3-transform></css3-transform>пакет
  • Разметка в DOM, которая должна использовать css3transformrefИспользуется для прямого управления DOM
  • В функции компонента вы можете использовать this.refs.animDiv для чтения или установки свойства преобразования css.
  • this.refs.xxxПоддерживает "translateX", "translateY", "translateZ", "scaleX", "scaleY", "scaleZ", "rotateX", "rotateY", "rotateZ", "skewX", "skewY", "originX", " originY", "originZ", "perspective" эти свойства устанавливаются и считываются
  • перспектива представляет собой расстояние перспективной проекции

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

Вы видите, что приведенный выше код не различается, пока DOM находится в движении? Компоненты не обновляются? Если компонент обновится, состояние всех перемещений потеряется? Как Оми решает эту проблему? Приведенный выше код уже дает ответ:

использоватьthis.data.rotateZдля синхронизации состояния движущегося DOM для предотвращения случайных обновлений (update)

→ Демо

Поддерживаемые свойства

Property Describe
translateX translateX
translateY translateY
translateZ translateZ
scaleX scaleX
scaleY scaleY
scaleZ scaleZ
rotateX rotateX
rotateY rotateY
rotateZ rotateZ
skewX skewX
skewY skewY
originX the basic x point of rotation
originY the basic y point of rotation
originZ the basic z point of rotation
perspective Perspective projection distance

Вы можете получить или установить.

Сравнение производительности

Поскольку версия реакции будет иметь процесс сравнения, а затем процесс применения различия к dom, изменение состояния не заменит весь innerHTML, поэтому он по-прежнему очень дешев для рендеринга браузера, но процесс сравнения в js занимает много времени. все равно нужно зайти в profiles One, если это занимает много времени, если не запускать в вебворкере, то все равно будет зависать в потоке UI, что приведет к лагу, лагу анимации, потере кадров, задержке взаимодействия, и т.п. Поэтому необходимо взглянуть на потребление времени ЦП.

Следующие данные представляют собой сравнение omi-transform и react-transform с использованием профилей Chrome двумя способами.

Сначала посмотрите на общее сравнение времени:

реагировать-преобразование:

оми-преобразование:

  • React потратил примерно 8739 секунд на процессорное время.1686ms
  • Режим Omi занимает примерно 9254 мс процессорного времени в секундах.700ms

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

Метод Omi точно такой же, как и традиционный нативный js. Поскольку процесс перемещения не выполняет DOM Diff, непосредственно управляйте DOM!!

Оми самосравнение

//slow
this.data.rotateZ += 2
//fast
this.animDiv.rotateZ += 2
this.data.rotateZ = this.animDiv.rotateZ

В основном сравните эффективность выполнения двух вышеуказанных блоков кода, откройте производительность Google Chrome, чтобы запустить ее примерно на 10 секунд, и откройте сводное сравнение:

Slow Fast
Omi
Omi

Видно, что оба метода omi имеют высокую производительность и много времени простоя в пределах 10 секунд, но fast действительно быстрее и скриптинг занимает меньше времени. Но почему преимущество неочевидно? Поскольку структура DOM проста, если структура DOM более сложна, быстрый способ прямого манипулирования DOM отбросит большую часть медленного способа! Проверьте это ниже:

Структура DOM первого рендера изменена, чтобы быть сложной:

Omi

Откройте производительность Google Chrome, чтобы запустить его примерно на 10 секунд, и откройте сводное сравнение:

Slow Fast
Omi
Omi

Вы можете видеть, что Scripting Time открыл брешь!

Сравните данные до и после:

DOM-структура Slow Fast
Простой
Omi
Omi
сложный
Omi
Omi

Видно, что между двумя временами быстрого и двумя временами медленного нет большой разницы. Так в чем же принцип Fast kernel css3transform?

css3transform

Установить

npm install css3transform

API

Transform(domElement, [notPerspective])

Вызвав приведенную выше строку кода, вы можете установить или прочитать «translateX», «translateY», «translateZ», «scaleX», «scaleY», «scaleZ», «rotateX», «rotateY», «rotateZ» элемента domElement. , "skewX", "skewY", "originX", "originY", "originZ"!

Дорога к простоте.

использовать позу

Transform(domElement)//or Transform(domElement, true);

//set 
domElement.translateX = 100
domElement.scaleX = 0.5
domElement.originX = 50

//get 
console.log(domElement.translateX)

Проблемы с традиционным программированием на CSS3

В прошлом мы обычно использовали animate.css, метод анимации zepto/jQuery или tween.js+css3 для программирования интерактивных спецэффектов. Подводя итог, можно выделить три недостатка:

  • Не интуитивный
  • косвенный
  • неудобно

Не интуитивный

Посмотрите на картинку ниже:

Результаты эффекта порядка, не интуитивно понятны. Так почему этот результат? Окончательная матрица может быть коррелирована новой WebKitcssmatrix (Transform_str).

Это также прямо показывает, что матрица не подчиняется закону перестановочности.A*B != B*A

косвенный

цепто поза:

$("#some_element").animate({
  opacity: 0.25, left: '50px',
  color: '#abcdef',
  rotateZ: '45deg', translate3d: '0,10px,0'
}, 500, 'ease-out')

translate3d: '0,10px,0' очень неудобно и не может управляться поэтапно. Не говоря уже о программировании с некоторыми библиотеками движения или времени. Может быть, вы возразите, что «облегчение» не может обеспечить смягчение? Но если мне нужно, чтобы x, y и z соответствовали разным функциям плавности, эта форма строкового программирования будет трудоемкой~~ Здесь также важно отметить, что порядок в zepto также влияет на результат. Потому что он также пишется как строка и назначается элементу dom.

поза tween.js

var position = { x: 100, y: 100, rotation: 0 },
    target = document.getElementById('target')

    new TWEEN.Tween(position)
    .to({ x: 700, y: 200, rotation: 359 }, 2000)
    .delay(1000)
    .easing(TWEEN.Easing.Elastic.InOut)
    .onUpdate(function update() {
        var t_str= 'translateX(' + position.x + 'px) translateY(' + position.y + 'px) rotate(' + Math.floor(position.rotation) + 'deg)'
        element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = t_str
    });

Способ использования строк утомительно смотреть. Не говоря уже о том, насколько мучительным является процесс написания.

animate.css поза:

@keyframes pulse {
  from {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }

  50% {
    -webkit-transform: scale3d(1.05, 1.05, 1.05);
    transform: scale3d(1.05, 1.05, 1.05);
  }

  to {
    -webkit-transform: scale3d(1, 1, 1);
    transform: scale3d(1, 1, 1);
  }
}

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

  • Программируемость недостаточно высока
  • Подходит для простых сценариев
  • Толькоendобратный звонок, нетchangeПерезвоните

неудобно

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

Обратите внимание, что это еще одно свойство transform-origin, а не transform. Но что, если требуется источник преобразования движения? Эта конструкция устарела? Существуют ли какие-либо сценарии, требующие источника движения? Это часто используется в геймдизайне, об этом позже будет сказано отдельно, дело в том, что есть сцены, в которых нужно переместить начало координат для достижения определенного эффекта.

резюме

Исходя из вышеперечисленных неудобств, есть css3transform!

  • css3transform Сверхлегкая js-библиотека, ориентированная на чтение и настройку преобразований CSS3, значительно улучшающая программируемость преобразований CSS3.
  • css3transform очень абстрактен и не привязан к какой-либо структуре времени и движения, поэтому его можно легко использовать с любой средой времени и движения.
  • css3transform использует matrix3d ​​в ​​качестве конечного вывода для объекта dom, аппаратное ускорение без потери программируемости
  • css3transform имеет очень простой в использовании API, простой в использовании за одну минуту и ​​встроенный в реальные проекты за две минуты.
  • css3transform расширяет возможности самого преобразования, делая его происхождение более удобным.

настоящий бой

Вы можете легко создать описанный выше эффект качания с помощью tweenjs createjs:

var element = document.querySelector("#test")
Transform(element)
element.originY = 100
element.skewX = -20

var Tween = createjs.Tween,
    sineInOutEase = createjs.Ease.sineInOut
Tween.get(element, {loop: true}).to({scaleY: .8}, 450, sineInOutEase).to({scaleY: 1}, 450, sineInOutEase)
Tween.get(element, {loop: true}).to({skewX: 20}, 900, sineInOutEase).to({skewX: -20}, 900, sineInOutEase)

Приведенный выше код очень компактен. Вот небольшое пояснение:

  • Начальный skewX элемента равен -20, чтобы не отставать от масштаба.
  • элемент originY 100, в нижний центр в качестве ориентира для пингвина

Как видите, потому что CSS3Transform сильно абстрактна, его можно легко использовать с TweenJS без какого-либо давления.

принцип

css3transform может не только смешивать преобразования CSS3 с элементами DOM, но и любыми объектными литералами.Вы также можете использовать css3transform как инструмент, который предоставляет некоторые базовые математические возможности.

Здесь нужно особое внимание, можно продолжать использовать предыдущие позы, а вот еще три использованные позы.

Грамматика 1

Transform(obj, [notPerspective]);

Как видите, больше ничего не меняется. Просто первый параметр может передавать не только элементы DOM, но и любые литералы объектов и т.д.

Не продавайте, сначала посмотрите на позу использования

var element = document.querySelector("#test"),
    obj = {}

Transform(obj)

obj.rotateZ = 90

element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = obj.transform

Видите ли, вы можете передавать не только элементы DOM, но и литералы объектов. Вы можете распечатать obj.transform, выше выбрано 90 градусов, поэтому генерируемая матрица:

perspective(500px) matrix3d(0,1,0,0,-1,0,0,0,0,0,1,0,0,0,0,1)

Вы также можете отключить перспективную проекцию, например:

var element = document.querySelector("#test"),
    obj = {}
//关闭透视投影
Transform(obj, true)

obj.rotateZ = 90

element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = obj.transform

Результирующая матрица:

matrix3d(0,1,0,0,-1,0,0,0,0,0,1,0,0,0,0,1)

Так что насчет позы движения? совпадение здесьtween.jsПример выглядит следующим образом:

var element = document.querySelector("#test"),
    obj = { translateX: 0, translateY: 0 }

Transform(obj);

var tween = new TWEEN.Tween(obj)
    .to({ translateX: 100, translateY: 100 }, 1000)
    .onUpdate(function () {
        element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = obj.transform
    })
    .start()

requestAnimationFrame(animate)

function animate(time) {
    requestAnimationFrame(animate)
    TWEEN.update(time)
}

А что, если использовать традиционную позу?

var element = document.querySelector("#test")

Transform(element)

var tween = new TWEEN.Tween({ translateX: element.translateX, translateY: element.translateY })
    .to({ translateX: 100, translateY: 100 }, 1000)
    .onUpdate(function () {
        element.translateX = this.translateX
        element.translateY = this.translateY
    })
    .start()

requestAnimationFrame(animate)

function animate(time) {
    requestAnimationFrame(animate)
    TWEEN.update(time)
}

Здесь, потому что TWEEN.Tween будет проходить по всем свойствам и устанавливать начальные значения, такие как код в tween:

 // Set all starting values present on the target object
for (var field in object) {
    _valuesStart[field] = parseFloat(object[field], 10)
}

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

var Tween = function (obj) {
    this.obj = obj
    return this
}

Tween.prototype = {
    to: function (targets, duration, easing) {
        this.duration = duration
        this.targets = targets
        return this
    },
    start: function () {
        this.startTime = new Date()
        this._beginTick()
    },
    _beginTick: function () {
        var _startValues = {},
            targets = this.targets
        for (var key in targets) {
            if (targets.hasOwnProperty(key)) {
                _startValues[key] = this.obj[key]
            }
        }
        var self  = this
        this._interval = setInterval(function () {
            var dt = new Date() - self.startTime
            for (var key in targets) {
                if (targets.hasOwnProperty(key)) {
                    if (dt >= self.duration) {
                        clearInterval(self._interval)
                    } else {
                        var p = dt / self.duration;
                        var dv = targets[key] - self.obj[key]
                        self.obj[key] += dv * p
                    }
                }
            }
        }, 15)

    }
}

Здесь, чтобы упростить использование setInterval в цикле, конечно, его можно заменить другими методами. Теперь вы можете использовать его так:

var element = document.querySelector("#test")
Transform(element)
var tween = new Tween(element)
    .to({ translateX: 100, translateY: 100 }, 1000)
    .start();

Конечно это немного не по теме. Это просто сравнение разницы между прямым монтированием DOM и монтированием сторонних объектов. Сторонние маунты чем-то напоминают боевой скот. Конечно.., это еще не конец, не то что выше. Это также может полностью использовать css3transform как вычислительный инструмент.

Грамматика 2

 Transform.getMatrix3D(option)

поза

var matrix3d = Transform.getMatrix3D({
    translateX: 0,
    translateY: 100,
    scaleX:2
})
console.log(matrix3d)

Распечатав его, вы получите следующие значения:

Делайте что хотите с этим значением. Глядя на исходный код css3transform, вы можете получить свойства, поддерживаемые Transform.getMatrix3D:

Transform.getMatrix3D = function (option) {
    var defaultOption = {
        translateX: 0,
        translateY: 0,
        translateZ: 0,
        rotateX: 0,
        rotateY: 0,
        rotateZ: 0,
        skewX: 0,
        skewY: 0,
        originX: 0,
        originY: 0,
        originZ: 0,
        scaleX: 1,
        scaleY: 1,
        scaleZ: 1
    };
    for (var key in option) {
    ...
    ...
    ...

}

Грамматика 3

 Transform.getMatrix2D(option)

Не только 3D-матрица, css3transform также обеспечивает поддержку служебных функций 2D.

поза

var matrix2d = Transform.getMatrix2D({
    translateX: 0,
    translateY: 100,
    scaleX:2
});
console.log(matrix2d);

Распечатав его, вы получите следующие значения:

  • Горизонтальный зум
  • B Горизонтальный рисунок
  • c Вертикальное растяжение
  • г Вертикальное масштабирование
  • TX горизонтальное смещение
  • вертикальное смещение

Итак, какой смысл в получении этого Matrix2D?

  • Масштаб: масштаб (sx, sy) эквивалентен матрице (sx, 0, 0, sy, 0, 0);
  • Перевод: translate(tx, ty) эквивалентен matrix(1, 0, 0, 1, tx, ty);
  • Вращение: rotate(deg) эквивалентно matrix(cos(deg), sin(deg), -sin(deg), cos(deg), 0, 0);
  • Растяжение: skew(degx, degy) эквивалентно matrix(1, tan(degy), tan(degx), 1, 0, 0);

Глядя на исходный код css3transform, вы можете получить свойства, поддерживаемые Transform.getMatrix2D:

Transform.getMatrix2D = function(option){
    var defaultOption = {
        translateX: 0,
        translateY: 0,
        rotation: 0,
        skewX: 0,
        skewY: 0,
        originX: 0,
        originY: 0,
        scaleX: 1,
        scaleY: 1
    };
    ...
    ...
    ...
}

предметы особого внимания

Transform.getMatrix2D и Transform.getMatrix3D поддерживают функцию происхождения, пожалуйста, попрощайтесь с transform-origin Transform.getMatrix2D и Transform.getMatrix3D не используют традиционный Math.tan для реализации show, а вместо этого используют половину вращения

Например, 2d перекос:

Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX)

Ранее студенты из Tencent IEG спрашивали, зачем использовать половинное вращение вместо Math.tan? Причина проста, Math.tan очень сильно искажает, и есть бесконечные значения, которые заставляют искажение охватывать весь экран.

Половины оборота нет.

Полезен ли getMatrix2D?

При использовании для Dom Transformation его можно использовать для браузеров, которые не поддерживают CSS3 3D Transforms.

Например, мы можем легко преобразовать некоторые свойства преобразования в свойства CSS3 и присвоить их DOM:

var matrix = Transform.getMatrix2D({
    rotation: 30,
    scaleX: 0.5,
    scaleY: 0.5,
    translateX: 100
});
ele.style.transform = ele.style.msTransform = ele.style.OTransform = ele.style.MozTransform = ele.style.webkitTransform = "matrix(" + [matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty].join(",") + ")"

Для холста и преобразования SVG

Какой? Можно ли его также использовать для Canvas и SVG?Да, например, нарисовать изображение на Canvas, которое повернуто на 30 градусов, масштабировано в 0,5 раза и переведено (200 200):

var canvas = document.getElementById("ourCanvas"),
    ctx = canvas.getContext("2d"),
    img = new Image(),
    rotation = 30 * Math.PI / 180

img.onload = function () {
    ctx.sava();
    ctx.setTransform(
        0.5 * Math.cos(rotation), 0.5 * Math.sin(rotation),
        -0.5 * Math.sin(rotation), 0.5 * Math.cos(rotation),
        200, 200
    )
    ctx.drawImage(img, 0, 0)
    ctx.restore()
};

img.src = "asset/img/test.png"

Это наша традиционная поза. После использования Transform.getMatrix2D стало вот так:

var canvas = document.getElementById("ourCanvas"),
    ctx = canvas.getContext("2d"),
    img = new Image()

var matrix = Transform.getMatrix2D({
    rotation: 30,
    scaleX: 0.5,
    scaleY: 0.5,
    translateX: 200,
    translateY: 200
});

img.onload = function () {
    ctx.sava();
    ctx.setTransform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
    ctx.drawImage(img, 0, 0);
    ctx.restore();
}

img.src = "asset/img/test.png"

Как видите, разработчикам не нужно самим собирать матрицу. Частицы SVG больше не используются в качестве примеров, которые аналогичны примерам, используемым для DOM, Я думаю, что каждый может сделать это быстро.

Star & Fork

→ оми-преобразование