Анимация фейсплейта

Просмотр 8 сообщений - с 1 по 8 (из 8 всего)
  • Автор
    Записи
  • #45617
    manjey73
    Участник

    Собственно тема все та же отчасти.

    1. Обновление WEB 1 секунда по умолчанию.
    2. Выполнение скриптов domUpdated соответственно в начале каждой секунды

    Вопрос: каким образом возможно сделать анимацию, продолжительностью более секунды, 5,10,15 ?

    изначальное применение svg в Разметке. SMIL анимация svg будет прерываться из-за обновления разметки перед каждым domUpdated независимо, вызываем мы из него скрипт по условию или нет. Кстати когда будет очередное обновление, где будет убрано это безобразие? 🙂

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

    Какие есть решения для движущейся анимации в текущий момент, или с этим пока глухо?

    #45622
    Mikhail
    Модератор

    каким образом возможно сделать анимацию, продолжительностью более секунды, 5,10,15 ?

    Варианты:
    1. Разбить анимацию на состояния, которые обновляются раз в секунду.
    2. Использовать свой таймер или jQuery.

    Сохранять промежуточные переменные как?

    Например, в свойстве customData, которое есть у каждого компонента.


    @jurasskpark
    , может быть посоветуете что-то как разработчик анимированных компонентов?

    #45623
    manjey73
    Участник

    1. не вариант, банальное движение по кругу скажем от -76 градусов до 0 градусов
    Во втором варианте от 80 до 0. и все это туда и обратно.
    Откуда я знаю, в каком положении окажется тот же damper (делаю свой) через 1 секунду?

    з.ы. почему разные градусы — зависит от размера компонента, на одной схеме он больше, на другой меньше, если поставить 90 градусов, то происходит перекрытие «лезвий» клапана. Заниматься еще и вычислением координат оси тот еще квест…

    2. Использовать свой таймер или jQuery. вот тут подробнее, и не забываем, что вся Разметка обновляется каждую секунду.

    в customData я могу записать только стартовое значение, но как в него записать значение ПЕРЕД очередным обновлением?

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

    #45627
    JurasskPark
    Участник

    @manjey73, в customData можно записывать значения во время всей работы компонента, в том числе на каждом кадре анимации.

    Для движения клапана достаточно хранить время начала движения, начальный и конечный углы, длительность. Текущий угол рассчитывается по прошедшему времени. Например, при движении от −76° до 0° за 10 секунд через секунду получится −68,4°. Считывать положение непосредственно перед обновлением разметки не нужно.

    Практически это работает так:
    1. Состояние движения хранится в component.customData.
    2. Один цикл requestAnimationFrame плавно меняет угол SVG.
    3. После пересоздания разметки domUpdated обновляет ссылку на новый SVG-элемент и сразу выставляет рассчитанный текущий угол.
    4. Время начала движения сохраняется. Дополнительный таймер при каждом domUpdated не запускается.
    Пересоздание DOM тогда не возвращает движение к началу. Новый элемент продолжает отображать ту же фазу анимации. Для нескольких экземпляров фейсплейта состояние хранится отдельно.

    
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100%" height="100%">
        <g data-damper-blade="true">
            <rect x="20" y="46" width="60" height="8" fill="#137d9b" />
        </g>
    </svg>
    
    
    class extends ComponentScript {
        // подключает анимацию после первого создания разметки
        domCreated(args) {
            this.domUpdated(args);
        }
    
        // сохраняет состояние движения и привязывает его к новому элементу DOM
        domUpdated(args) {
            const component = args.component;
            const data = component.customData || (component.customData = {});
            if (!data.damperAnimation) {
                data.damperAnimation = { startedAt: performance.now(), node: null, frameId: null };
            }
            const state = data.damperAnimation;
            state.node = component.dom.find("[data-damper-blade]").get(0);
            this.draw(state, performance.now());
            if (state.frameId === null && state.node) {
                const frame = (now) => {
                    if (!state.node || !state.node.isConnected) {
                        state.frameId = null;
                        return;
                    }
                    this.draw(state, now);
                    state.frameId = requestAnimationFrame(frame);
                };
                state.frameId = requestAnimationFrame(frame);
            }
        }
    
        // вычисляет угол для полного цикла туда и обратно за 10 секунд
        draw(state, now) {
            if (!state.node) {
                return;
            }
            const phase = ((now - state.startedAt) % 10000) / 10000;
            const position = phase < 0.5 ? phase * 2 : (1 - phase) * 2;
            const angle = -76 + 76 * position;
            state.node.setAttribute("transform", <code>rotate(${angle} 50 50)</code>);
        }
    }
    
    #45629
    manjey73
    Участник

    печально, не понимаю половины написанного 🙂

    data-damper-blade=»true» — например для чего это ?

    • Ответ изменён 13 часов, 39 минут назад пользователем manjey73.
    #45632
    JurasskPark
    Участник

    data-damper-blade=»true» — это просто метка в SVG изображении. Она указывает скрипту, какую часть рисунка нужно вращать.
    Скрипт находит эту группу по метке и поворачивает её. Корпус клапана остаётся неподвижным. Само значение true анимацию не включает — движение выполняет скрипт.

    #45633
    JurasskPark
    Участник

    На примере Dumper.

    
    class extends ComponentScript {
        // сохраняет настройки оформления из исходного Damper.fp
        _setDamper(args) {
            const component = args.component;
            const idPrefix = component.dom.attr("id");
            const prop = component.parent.properties;
            const dom = component.dom;
            let stroke = prop._stroke;
            const strAct = prop._strActive;
            const strw = prop._strWidth;
            const val = Number.parseInt(prop.val);
            const fill = prop._fill;
            const offset = Number.parseInt(prop._rectOffset) || 0;
            const fillDamp = prop._fillDamper;
            const fillAx = prop._fillAxis;
            const strAx = prop._colorAxis;
            const type = prop._type;
    
            if (val > 0) {
                stroke = strAct;
            }
    
            if (offset > 0) {
                dom.find(<code>#${idPrefix}rect</code>)
                    .attr("stroke-width", prop._mainBorder.width)
                    .attr("stroke", prop._mainBorder.color)
                    .attr("fill", fill)
                    .attr("x", offset).attr("y", offset)
                    .attr("width", 160 - offset * 2)
                    .attr("height", 200 - offset * 2);
                dom.find(<code>#${idPrefix}Rect</code>).attr("href", <code>#${idPrefix}rect</code>);
            }
    
            if (type === "blade") {
                dom.find(<code>#${idPrefix}Damp</code>).attr("href", <code>#${idPrefix}damp</code>);
                dom.find(<code>#${idPrefix}damp</code>)
                    .attr("stroke-width", strw).attr("stroke", stroke).attr("fill", fillDamp);
            } else if (type === "line") {
                dom.find(<code>#${idPrefix}Line</code>).attr("href", <code>#${idPrefix}line</code>);
            }
    
            dom.find(<code>#${idPrefix}axis</code>)
                .attr("stroke-width", strw).attr("stroke", strAx).attr("fill", fillAx);
    
            this._animateDamper(component);
        }
    
        // после замены SVG находит новую группу и продолжает прежнее движение
        _animateDamper(component) {
            const data = component.customData || (component.customData = {});
            if (!data.damperAnimation) {
                data.damperAnimation = {
                    startedAt: performance.now(),
                    node: null,
                    frameId: null
                };
            }
    
            const animation = data.damperAnimation;
            const idPrefix = component.dom.attr("id");
            animation.node = component.dom.find(<code>#${idPrefix}damper</code>).get(0);
            this._drawDamper(animation, performance.now());
    
            if (animation.frameId === null && animation.node) {
                const frame = now => {
                    if (!animation.node || !animation.node.isConnected) {
                        animation.frameId = null;
                        return;
                    }
    
                    this._drawDamper(animation, now);
                    animation.frameId = requestAnimationFrame(frame);
                };
                animation.frameId = requestAnimationFrame(frame);
            }
        }
    
        // рассчитывает текущий угол по времени, сохраняя сдвиг и масштаб рисунка
        _drawDamper(animation, now) {
            if (!animation.node) {
                return;
            }
    
            const startAngle = -76;  // начальный угол в градусах
            const endAngle = 0;      // конечный угол в градусах
            const moveSeconds = 5;   // время движения в одну сторону, секунд
    
            const elapsed = (now - animation.startedAt) / 1000;
            const cycle = elapsed % (moveSeconds * 2);
            const position = cycle <= moveSeconds
                ? cycle / moveSeconds
                : 2 - cycle / moveSeconds;
            const angle = startAngle + (endAngle - startAngle) * position;
            animation.node.setAttribute("transform",
                <code>rotate(${angle}, 80, 100) translate(20, 82) scale(0.34)</code>);
        }
    
        // запускает оформление и движение при первом появлении компонента
        domCreated(args) {
            this._setDamper(args);
        }
    
        // восстанавливает оформление и текущий угол после обновления разметки
        domUpdated(args) {
            this._setDamper(args);
        }
    }
    
    
    #45634
    manjey73
    Участник

    вращение доступно только для части рисунка, оформленного как svg или это может быть группа <g> или символ <symbol> ?

    ну и такой вопрос, оформить в виде скрипта в mimic-custom.js ? чтобы не прописывать в каждом элементе ?

    буду пробовать. С другой стороны в большинстве случаев было бы достаточно SMIL анимации, если бы не рывки из-за постоянного обновления.

Просмотр 8 сообщений - с 1 по 8 (из 8 всего)
  • Для ответа в этой теме необходимо авторизоваться.