Стартовая страница › Форумы › Понять, как работает ПО › Мнемосхемы › Анимация фейсплейта
- В этой теме 7 ответов, 3 участника, последнее обновление 3 часа, 39 минут назад сделано
manjey73.
-
АвторЗаписи
-
27.10.2026 в 09:35 #45617
manjey73УчастникСобственно тема все та же отчасти.
1. Обновление WEB 1 секунда по умолчанию.
2. Выполнение скриптов domUpdated соответственно в начале каждой секундыВопрос: каким образом возможно сделать анимацию, продолжительностью более секунды, 5,10,15 ?
изначальное применение svg в Разметке. SMIL анимация svg будет прерываться из-за обновления разметки перед каждым domUpdated независимо, вызываем мы из него скрипт по условию или нет. Кстати когда будет очередное обновление, где будет убрано это безобразие? 🙂
Сохранять промежуточные переменные как? Как бы с точки зрение, что вся наша анимация это состояние Вкл/Выкл то и проблем вроде нет, но если это движение, то я не понимаю как сохранить промежуточное состояние этого движения?, чтобы при следующем обновлении начать с того места, где произошла остановка.????
Какие есть решения для движущейся анимации в текущий момент, или с этим пока глухо?
27.10.2026 в 16:56 #45622
MikhailМодераторкаким образом возможно сделать анимацию, продолжительностью более секунды, 5,10,15 ?
Варианты:
1. Разбить анимацию на состояния, которые обновляются раз в секунду.
2. Использовать свой таймер или jQuery.Сохранять промежуточные переменные как?
Например, в свойстве customData, которое есть у каждого компонента.
@jurasskpark, может быть посоветуете что-то как разработчик анимированных компонентов?27.10.2026 в 17:27 #45623
manjey73Участник1. не вариант, банальное движение по кругу скажем от -76 градусов до 0 градусов
Во втором варианте от 80 до 0. и все это туда и обратно.
Откуда я знаю, в каком положении окажется тот же damper (делаю свой) через 1 секунду?з.ы. почему разные градусы — зависит от размера компонента, на одной схеме он больше, на другой меньше, если поставить 90 градусов, то происходит перекрытие «лезвий» клапана. Заниматься еще и вычислением координат оси тот еще квест…
2. Использовать свой таймер или jQuery. вот тут подробнее, и не забываем, что вся Разметка обновляется каждую секунду.
в customData я могу записать только стартовое значение, но как в него записать значение ПЕРЕД очередным обновлением?
уберите к чертям обновление Разметки перед domUpdated, снимет кучу проблем, в том числе и со SMIL анимацией, мы просто в коде проверяем требуется анимировать или анимация уже запущена, все блин. Дешево и сердито, без вот всякой ахинеи с лишними вычислениями…
27.10.2026 в 19:14 #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>); } }27.10.2026 в 22:36 #45629
manjey73Участникпечально, не понимаю половины написанного 🙂
data-damper-blade=»true» — например для чего это ?
-
Ответ изменён 13 часов, 39 минут назад пользователем
manjey73.
27.10.2026 в 23:50 #45632
JurasskParkУчастникdata-damper-blade=»true» — это просто метка в SVG изображении. Она указывает скрипту, какую часть рисунка нужно вращать.
Скрипт находит эту группу по метке и поворачивает её. Корпус клапана остаётся неподвижным. Само значение true анимацию не включает — движение выполняет скрипт.28.10.2026 в 00:05 #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); } }28.10.2026 в 08:37 #45634
manjey73Участниквращение доступно только для части рисунка, оформленного как svg или это может быть группа <g> или символ <symbol> ?
ну и такой вопрос, оформить в виде скрипта в mimic-custom.js ? чтобы не прописывать в каждом элементе ?
буду пробовать. С другой стороны в большинстве случаев было бы достаточно SMIL анимации, если бы не рывки из-за постоянного обновления.
-
Ответ изменён 13 часов, 39 минут назад пользователем
-
АвторЗаписи
- Для ответа в этой теме необходимо авторизоваться.