Анализ принципа реализации анимации лото-паутины

внешний интерфейс
Анализ принципа реализации анимации лото-паутины

Источник изображения:aescripts.com/bodymovin/

Автор этой статьи:Цинчжоу

предисловие

LottieЭто решение для сложной покадровой анимации, которое обеспечивает поток инструментов от дизайнеров, использующих AE (Adobe After Effects), до разработчиков на всех концах для достижения анимации. После того, как дизайнер завершит анимацию через AE, можно использовать расширение для AEBodymovinЭкспортируйте данные анимации в формате JSON, а затем разработчики могут преобразовать сгенерированные данные JSON в анимацию через Lottie.

1. Как реализовать анимацию Lottie

实现一个 Lottie 动画流程

  1. Дизайнеры используют AE для создания анимации.
  2. Анимация экспортируется в файл данных JSON через подключаемый модуль AE Bodymovin, предоставленный Lottie.
  3. Загрузка библиотеки Lottie в сочетании с файлом JSON и следующими строками кода может реализовать анимацию Lottie.
import lottie from 'lottie-web';
import animationJsonData from 'xxx-demo.json';  // json 文件

const lot = lottie.loadAnimation({
   container: document.getElementById('lottie'), 
   renderer: 'svg',
   loop: true,
   autoplay: false,
   animationData: animationJsonData,
 });

// 开始播放动画
lot.play();

Другие анимированные шаблоны JSON можно посмотретьlottiefiles.com/

2. Интерпретация формата данных файла JSON

Я сам сделал Lottie Demo ->нажмите на меня, чтобы просмотреть

  • от 0 до 3 с,scaleЗначение атрибута изменено со 100% до 50%.
  • от 3с до 6с,scaleЗначение свойства меняется с 50% на 100% для завершения анимации.

动画变化路径

Структура данных JSON, экспортированная через плагин Bodymovin, показана на следующем рисунке:

JSON 数据结构

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

2.1 Глобальная информация

全局信息

Слева находится информация, которую необходимо заполнить для создания новой композиции анимации с использованием AE, которая соответствует первому слою информации JSON справа следующим образом:

  • wа такжеh: ширина 200, высота 200
  • v: Плагин Bodymovin версии 4.5.4
  • fr:частота кадров 30fps
  • ipа такжеop: начальный кадр 0, конечный кадр 180
  • assets: Статическая информация о ресурсах (например, изображения)
  • layers: информация о слое (каждый слой и информация о действии в анимации).
  • ddd: Это 3D
  • comps: Синтетический слой

вfr,ip,opЭто особенно важно в процессе анимации Lottie.Как упоминалось ранее, наша демонстрационная анимация составляет 0–6 с, но Lottie рассчитывает время анимации по частоте кадров. Частота кадров, установленная в демонстрации, составляет 30 кадров в секунду, поэтому 0–6 с эквивалентны 0–180 кадрам.

2.2 Информация, относящаяся к уровню

Поняв внешнюю информацию JSON, давайте расширим и посмотрим на JSON.layersконкретная информация, сначалаdemoДетали анимации следующие:

动画细节

В основном это 3 направления:

  • Область содержимого, которая содержит такую ​​информацию, как размер, положение и округлость слоя формы.
  • Область изменения, содержит 5 свойств изменения (точка привязки, положение, масштаб, вращение, непрозрачность).
  • Увеличьте 3 кадра (зеленая область на картинке) и измените свойства масштабирования на кадры 0, 90 и 180. На картинке показан 90-й кадр, а слой увеличен до 50%.

В соответствии с информацией о производстве анимации на рисунке выше, это может соответствоватьlayers. Как показано ниже:

layers

2.3 Информация об изменении атрибута

см. далееks(изменить свойство) вsРазвернуть, то есть увеличить информацию.

ks信息

в:

  • tПредставляет количество ключевых кадров
  • sПредставляет до изменения (слой двумерный, поэтому третье значение зафиксировано на 100).
  • eПосле изменения представления (слой двумерный, поэтому третье значение фиксируется равным 100).

3. Как Lottie перемещает данные JSON

Значение данных в формате JSON было кратко рассмотрено ранее, так как же Lottie перемещает данные JSON? Затем прочитайте исходный код демо-версии Lottie, будет отображаться только часть исходного кода, ключ в том, чтобы прояснить идею, не придерживаться исходного кода.

Следующее введение в исходный код в основном разделено на 2 части:

  • Инициализация анимации (разделы 3.1 - 3.3)
  • Воспроизведение анимации (раздел 3.4)

3.1 Инициализация средства визуализации

Такие какDemoпоказано, Лотти проходитloadAnimationметод для инициализации анимации. Процесс инициализации рендерера выглядит следующим образом:

loadAnimation

function loadAnimation(params){
    // 生成当前动画实例
    var animItem = new AnimationItem();
    // 注册动画
    setupAnimation(animItem, null);
    // 初始化动画实例参数
    animItem.setParams(params);
    return animItem;
}

function setupAnimation(animItem, element) {
    // 监听事件
    animItem.addEventListener('destroy', removeElement);
    animItem.addEventListener('_active', addPlayingCount);
    animItem.addEventListener('_idle', subtractPlayingCount);
    // 注册动画
    registeredAnimations.push({elem: element, animation:animItem});
    len += 1;
}
  • AnimationItemЭтот класс является базовым классом для анимации Lottie.loadAnimationметод сначала сгенерируетAnimationItemЭкземпляр и возврат, разработчик используетПараметры и методы конфигурациииз этого класса.

  • генерироватьanimItemПосле экземпляра позвонитеsetupAnimationметод. Этот метод сначала прослушиваетdestroy,_active,_idleТри события ожидают запуска. Поскольку несколько анимаций могут быть распараллелены, определяется глобальная переменнаяlen,registeredAnimationsи т. д., для оценки и кэширования зарегистрированных экземпляров анимации.

  • Следующий звонокanimItemпримерsetParamsметод для инициализации параметров анимации, кроме initializeloop,autoplayПомимо прочих параметров, самое главное — выбрать рендерер. следующим образом:

AnimationItem.prototype.setParams = function(params) {
    // 根据开发者配置选择渲染器
    switch(animType) {
        case 'canvas':
            this.renderer = new CanvasRenderer(this, params.rendererSettings);
            break;
        case 'svg':
            this.renderer = new SVGRenderer(this, params.rendererSettings);
            break;
        default:
            // html 类型
            this.renderer = new HybridRenderer(this, params.rendererSettings);
            break;
    }

    // 渲染器初始化参数
    if (params.animationData) {
        this.configAnimation(params.animationData);
    }
}

Lottie предоставляет три режима рендеринга: SVG, Canvas и HTML, обычно используя первый или второй.

  • Средство визуализации SVG поддерживает большинство функций и является наиболее часто используемым методом визуализации. И SVG масштабируется, без искажений при любом разрешении.

  • Визуализатор Canvas непрерывно перерисовывает объекты каждого кадра в соответствии с данными анимации.

  • Визуализатор HTML ограничен своими функциями и поддерживает наименьшее количество функций: он может отображать только очень простую графику или текст и не поддерживает эффекты фильтров.

Каждый рендерер имеет свою реализацию и сложность, но чем сложнее анимация, тем выше потребление производительности, что зависит от реальной ситуации. Исходный код рендерера находится вplayer/js/renderers/Демонстрация в этой статье под папкой анализирует только реализацию анимации рендеринга SVG. Поскольку три рендерера основаны наBaseRendererкласс, поэтому следующее, кромеSVGRendererтакже появляютсяBaseRendererметод класса.

3.2 Инициализация свойств анимации и загрузка статических ресурсов

Убедившись, что средство визуализации SVG используется, вызовитеconfigAnimationметод для инициализации рендерера.

AnimationItem.prototype.configAnimation = function (animData) {
    if(!this.renderer) {
        return;
    }
    
    // 总帧数
    this.totalFrames = Math.floor(this.animationData.op - this.animationData.ip);
    this.firstFrame = Math.round(this.animationData.ip);
    
    // 渲染器初始化参数
    this.renderer.configAnimation(animData);

    // 帧率
    this.frameRate = this.animationData.fr;
    this.frameMult = this.animationData.fr / 1000;
    this.trigger('config_ready');
    
    // 加载静态资源
    this.preloadImages();
    this.loadSegments();
    this.updaFrameModifier();
    
    // 等待静态资源加载完毕
    this.waitForFontsLoaded();
};

В этом методе будут инициализированы дополнительные свойства объекта анимации, такие как общее количество кадров.totalFrames, частота кадровframeMultЖдать. Затем загрузите некоторые другие ресурсы, такие как изображения, шрифты и т. д. Как показано ниже:

渲染流程

в то же времяwaitForFontsLoadedВ методе дождитесь загрузки статического ресурса, а затем вызовите средство визуализации SVG.initItemsМетод отрисовывает слой анимации, то есть анимация отрисовывается.

AnimationItem.prototype.waitForFontsLoaded = function(){
    if(!this.renderer) {
        return;
    }
    // 检查加载完毕
    this.checkLoaded();
}

AnimationItem.prototype.checkLoaded = function () {
    this.isLoaded = true;

    // 初始化所有元素
    this.renderer.initItems();
    setTimeout(function() {
        this.trigger('DOMLoaded');
    }.bind(this), 0);

    // 渲染第一帧
    this.gotoFrame();
    
    // 自动播放
    if(this.autoplay){
        this.play();
    }
};

существуетcheckLoadedКак видно из метода, поinitItemsПосле того, как все элементы инициализированы, перейдитеgotoFrameРендеринг первого кадра, если он настроен разработчикомautoplayдляtrue, он будет напрямую вызыватьplayспособ играть. Здесь хорошо иметь впечатление, и я расскажу об этом подробно позже. посмотрим дальшеinitItemsДетали реализации.

3.3 Отрисовка начального слоя анимации

initItemsМетод в основном заключается в вызовеbuildAllItemsСоздайте все слои.buildItemметод будет вызван сноваcreateItemОпределите конкретный тип слоя. Исходный код метода здесь разделен на более мелкие детали. В этой статье сохранены толькоcreateItemметод, другие заинтересованные могут просмотреть детали исходного кода.

initItems

При создании анимации дизайнеры манипулируют множеством элементов слоев, таких как изображения, фигуры, текст и т. д. такlayersТам будет поле для каждого слоя вtyразличать. комбинироватьcreateItemС точки зрения методов, существует в общей сложности следующие 8 типов.

BaseRenderer.prototype.createItem = function(layer) {
    // 根据图层类型,创建相应的 svg 元素类的实例
    switch(layer.ty){
        case 0:
            // 合成
            return this.createComp(layer);
        case 1:
            // 固态
            return this.createSolid(layer);
        case 2:
            // 图片
            return this.createImage(layer);
        case 3:
            // 兜底空元素
            return this.createNull(layer);
        case 4:
            // 形状
            return this.createShape(layer);
        case 5:
            // 文字
            return this.createText(layer);
        case 6:
            // 音频
            return this.createAudio(layer);
        case 13:
            // 摄像机
            return this.createCamera(layer);
    }
    return this.createNull(layer);
};

Поскольку автор и большинство разработчиков не являются профессиональными игроками в AE, не нужно беспокоиться о том, что представляет собой каждый тип, просто уточните основные идеи. В сочетании с авторской демонстрацией есть только один слой, а слойty4 . ЯвляетсяShapeслой формы, поэтому во время инициализации слоя выполняется толькоcreateShapeметод.

Логика рендеринга для других типов слоев, таких какImage,Text,AudioИ так далее, логика отрисовки каждого элемента реализована в исходном кодеplayer/js/elements/Под папкой конкретная логика реализации здесь не будет развернута, и заинтересованные учащиеся могут просмотреть ее самостоятельно.

Следующим шагом является выполнениеcreateShapeметод для инициализации свойств, связанных с элементом.

绘制Shape图层

Помимо некоторых деталей метода инициализации, стоит отметить, чтоinitTransformметод.

initTransform: function() {
    this.finalTransform = {
        mProp: this.data.ks
            ? TransformPropertyFactory.getTransformProperty(this, this.data.ks, this)
            : {o:0},
        _matMdf: false,
        _opMdf: false,
        mat: new Matrix()
    };
},

использоватьTransformPropertyFactoryправильноtransformИнициализация в сочетании с 0-м кадром Demo соответствует следующему:

动画变化路径

  • Непрозрачность 100%
  • Масштаб 100%
transform: scale(1);
opacity: 1;

Итак, почему вам нужно инициализировать при инициализации слоя рендеринга?transformа такжеopacityШерстяная ткань? Ответ на этот вопрос будет дан в разделе 3.4.

3.4 Воспроизведение анимации Lottie

Прежде чем анализировать воспроизведение анимации исходного кода Lottie, давайте вспомним ее. Настройки анимации авторской Демо:

  • от 0 до 3 с,scaleЗначение атрибута изменено со 100% до 50%.
  • от 3с до 6с,scaleЗначение атрибута изменено с 50% на 100%.

Если вы сделаете изменение в 3 с в соответствии с этой настройкой, анимация будет слишком жесткой. Поэтому дизайнер установил частоту кадров 30 кадров в секунду, что означает каждые 33,3 мс.РазнообразиеСделайте анимацию не слишком жесткими. Так как добиться этогоРазнообразие, который упоминается в разделе 3.3transformа такжеopacity.

5 свойств изменения, упомянутых в разделе 2.2 (точка привязки, положение, масштаб, вращение, непрозрачность). где непрозрачность передается через CSSopacityконтролировать остальные 4 (точка привязки, положение, масштаб, вращение) черезtransformизmatrixконтролировать. Фактические начальные значения в Демо автора следующие:

transform: matrix(1, 0, 0, 1, 100, 100);
/* 上文的 transform: scale(1); 只是为了方便理解*/
opacity: 1;

Это связано с тем, что такие свойства, как вращение или масштабирование, в основном применяютсяtransformизmatrix()метод, поэтому Лотти использует его единообразноmatrixиметь дело с. Обычно разработчики используют что-то вродеtransform: scaleЭта форма выражения просто потому, что ее легче понять, запомнить и начать.matrixСоответствующие знания можно получить у г-на Чжан Синьсюя.Понимание Matrix в преобразованиях CSS3.

矩阵

Таким образом, процесс воспроизведения анимации Lottie может бытьвременныйРезюме:

  1. Рендеринг слоев, инициализация всех слоевtransformа такжеopacity
  2. Рассчитать каждый кадр (33,3 мс на 33 мс) в соответствии с частотой кадров 30 кадров в секундуtransformа такжеopacityи изменить DOM

Но как Lottie контролирует интервал 30 кадров в секунду? Что если дизайнер установит 20 кадров в секунду или 40 кадров в секунду? в состоянии пройтиsetTimeout,setIntervalПойми? Возьмите эту задачу, чтобы увидеть, как обрабатывается исходный код и как реализовать общее решение.

Воспроизведение анимации Lottie в основном используетAnimationItemпримерplayметод.如果开发者配置了autoplayдляtrue, после того как вся работа по инициализации подготовлена ​​(упомянутая в Разделе 3.2), вызовите напрямуюplayспособ играть. В противном случае он активно вызывается разработчикомplayспособ играть.

Далее изplayМетод Узнайте подробности всего процесса воспроизведения:

AnimationItem.prototype.play = function (name) {
    this.trigger('_active');  
};

удалить лишний код,playМетод в основном запускается_activeсобытие, это_activeСобытия регистрируются при инициализации в разделе 3.1.

animItem.addEventListener('_active', addPlayingCount);

function addPlayingCount(){
    activate();
}

function activate(){
    // 触发第一帧渲染
    window.requestAnimationFrame(first);
}

После срабатывания вызовомrequestAnimationFrameметод, который вызывается непрерывноresumeспособ управления анимацией.

function first(nowTime){
    initTime = nowTime;
    // requestAnimationFrame 每次都进行计算修改 DOM
    window.requestAnimationFrame(resume);
}

Параметры анимации, упомянутые выше:

  • начальный кадр с 0
  • конечный кадр 180
  • Частота кадров 30 кадров в секунду

requestAnimationFrame60 кадров в секунду в нормальных условиях (каждые 16,7 мс или около того). Так как же Lottie обеспечивает плавную работу анимации со скоростью 30 кадров в секунду (каждые 33,3 мс). В настоящее время нам нужно изменить наше мышление.Дизайнер надеется вычислять изменение каждые 33,3 мс.requestAnimationFrameметод, он рассчитывается каждые 16,7 мс, а также могут быть рассчитаны изменения анимации. Просто расчет более детальный, и он сделает анимацию более плавной, чтобы ее можно было обрабатывать будь то 20фпс или 40фпс, посмотрим как обрабатывается исходный код.

постоянно звонюresumeВ методе основная логика следующая:

function resume(nowTime) {
    // 两次 requestAnimationFrame 间隔时间
    var elapsedTime = nowTime - initTime;

    // 下一次计算帧数 = 上一次执行的帧数 + 本次间隔的帧数
    // frameModifier 为帧率( fr / 1000 = 0.03)
    var nextValue = this.currentRawFrame + value * this.frameModifier;
    
    this.setCurrentRawFrameValue(nextValue);
    
    initTime = nowTime;
    if(playingAnimationsNum && !_isFrozen) {
        window.requestAnimationFrame(resume);
    } else {
        _stopped = true;
    }
}

AnimationItem.prototype.setCurrentRawFrameValue = function(value){
    this.currentRawFrame = value;
    // 渲染当前帧
    this.renderFrame();
};

resumeметод:

  • Сначала вычисляется текущее время и последнее времяdiffвремя.

  • Затем вычислите текущий номер кадра времени от начала анимации до настоящего времени. Обратите внимание здесьномер кадраПросто единица расчета относительно настройки AE, которая может иметь десятичные дроби.

  • наконец прошлоrenderFrame()Метод обновляет изменения DOM, соответствующие текущему кадру.

Например:

Предполагая, что предыдущий кадр равен 70,25 кадра, на этот разrequestAnimationFrameИнтервал равен 16,78 мс, тогда:

当前帧数:70.25 +  16.78 * 0.03 =  70.7534帧

Поскольку 70,7534 кадра находится в диапазоне анимации от 0 до 90 кадров в демонстрации, масштаб кадра (который представляет собой процент времени, в течение которого выполняется анимация) рассчитывается следующим образом:

帧比例:70.7534 / 90 = 0.786148889

Анимация для кадров 0 - 90 масштабирует слой от 100% до 50%, так как рассчитывается только 50% изменение, поэтому масштаб выглядит следующим образом:

缩放比例: 100 - (50 * 0.781666)= 60.69255555%

Соответствующий код расчета находится вTransformPropertyFactoryВ классе:

// 计算百分比
perc = fnc((frameNum - keyTime) / (nextKeyTime - keyTime ));
endValue = nextKeyData.s || keyData.e;
// 计算值
keyValue = keyData.s[i] + (endValue[i] - keyData.s[i]) * perc;

вfncДля функции расчета, если задана функция кривой движения Безье, тоfncПравила расчета также изменены соответствующим образом. Текущая демонстрация использует линейные изменения для простоты понимания. Учащиеся, заинтересованные в конкретном исходном коде, могут просмотреть его самостоятельно.

Рассчитать текущийscaleзначение, затем используйтеTransformPropertyFactoryРассчитать текущий соответствующийtransformизmatrixзначение, а затем измените свойство CSS соответствующего элемента DOM. так черезrequestAnimationFrameПостоянно подсчитывая количество кадров, а затем вычисляя соответствующие изменения CSS, анимация реализуется в течение определенного периода времени. Процесс воспроизведения выглядит следующим образом:

播放流程

Рассчитанное здесь количество кадров нужно помнить всегда,В Lottie количество кадров, заданное AE как единица расчета, Лотти вносит все изменения не в соответствии с заданными дизайнером 30 кадрами в секунду (каждые 33,3 мс), а в соответствии сrequestAnimationFrame Интервал (каждые 16,7 мс или около того) рассчитывает более подробные изменения, чтобы обеспечить плавную работу анимации.

не прошелsetTimeout,setIntervalЭто реализовано, потому что все они имеют свои недостатки, поэтому я не буду их здесь распространять, и каждый может ознакомиться с информацией самостоятельно.requestAnimationFrameСистемный временной интервал используется для поддержания наилучшей эффективности прорисовки, чтобы анимация могла иметь унифицированный механизм обновления, тем самым экономя системные ресурсы, повышая производительность системы и улучшая визуальные эффекты.

4. Резюме

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

4.1 Преимущества Лотти

  1. Дизайнеры делают анимацию через AE, а фронтенд можно напрямую восстановить, и не будет ситуации, когда покупатели хвастаются, а продавцы хвастаются.
  2. SVG масштабируется и не будет искажаться при любом разрешении.
  3. Файл JSON, который можно повторно использовать в нескольких терминалах (веб, Android, iOS, React Native).
  4. Размер файла JSON будет намного меньше, чем файлы GIF и APNG, а производительность будет лучше.

4.2 Недостатки Лотти

  1. Сам Lottie-Web-файл по-прежнему относительно большой, при 513 тыс. Несбежавших, 144K сжал в световой версии и 39K после GZIP. Таким образом, вам нужно обратить внимание на загрузку Lottie-Web.
  2. Ненужные кадры последовательности. Основная идея анимации Lottie заключается в том, чтобы рисовать определенный слой и постоянно менять свойства CSS.Если дизайнер ленив и использует некоторые плагины для достижения эффекта анимации, это может привести к тому, что каждый кадр будет картинкой, как показано на рисунке. На следующем рисунке это приведет к тому, что этот файл JSON будет очень большим, обратите внимание на то, чтобы заранее связаться с дизайнером.

不必要的序列帧

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

5. Ссылки

Эта статья была опубликована сКоманда внешнего интерфейса NetEase Cloud Music, Любое несанкционированное воспроизведение статьи запрещено. Мы набираем front-end, iOS и Android круглый год.Если вы готовы сменить работу и любите облачную музыку, присоединяйтесь к нам на grp.music-fe(at)corp.netease.com!