Add video editor timeline animator with animated playhead and scene transitions
This commit is contained in:
110
theme/assets/js/video-editor-animator.js
Normal file
110
theme/assets/js/video-editor-animator.js
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Video Editor Timeline Animator
|
||||
* Animates playhead scrubbing across the timeline and video preview crossfades.
|
||||
* Mirrors the structure and conventions of dashboard-animator.js.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var DURATION = 10000; // ms for one full playhead sweep (left → right, then loop)
|
||||
var X_MIN = 104; // leftmost playhead centre (SVG units)
|
||||
var X_MAX = 504; // rightmost playhead centre (end of clip area)
|
||||
|
||||
function makeState(svg) {
|
||||
return {
|
||||
svg: svg,
|
||||
playheadLine: svg.querySelector('#ve-playhead-line'),
|
||||
playheadHead: svg.querySelector('#ve-playhead-head'),
|
||||
scene1: svg.querySelector('#ve-scene-1'),
|
||||
scene2: svg.querySelector('#ve-scene-2'),
|
||||
scene3: svg.querySelector('#ve-scene-3'),
|
||||
innerScreen: svg.querySelector('#ve-inner-screen'),
|
||||
timecode: svg.querySelector('#ve-timecode'),
|
||||
scrubPct: 0,
|
||||
lastTime: performance.now(),
|
||||
paused: false
|
||||
};
|
||||
}
|
||||
|
||||
function lerp(a, b, t) { return a + (b - a) * t; }
|
||||
|
||||
function updatePlayhead(st) {
|
||||
var xC = lerp(X_MIN, X_MAX, st.scrubPct);
|
||||
if (st.playheadLine) st.playheadLine.setAttribute('x', (xC - 1).toFixed(1));
|
||||
if (st.playheadHead) st.playheadHead.setAttribute('transform', 'translate(' + xC.toFixed(1) + ',234)');
|
||||
if (st.timecode) {
|
||||
var s = Math.floor(st.scrubPct * 10);
|
||||
st.timecode.textContent = '0:' + (s < 10 ? '0' + s : s);
|
||||
}
|
||||
}
|
||||
|
||||
function updateScenes(st) {
|
||||
var p = st.scrubPct;
|
||||
var o1, o2, o3, fade;
|
||||
if (p < 0.30) {
|
||||
o1 = 1; o2 = 0; o3 = 0;
|
||||
} else if (p < 0.40) {
|
||||
fade = (p - 0.30) / 0.10; o1 = 1 - fade; o2 = fade; o3 = 0;
|
||||
} else if (p < 0.65) {
|
||||
o1 = 0; o2 = 1; o3 = 0;
|
||||
} else if (p < 0.75) {
|
||||
fade = (p - 0.65) / 0.10; o1 = 0; o2 = 1 - fade; o3 = fade;
|
||||
} else {
|
||||
o1 = 0; o2 = 0; o3 = 1;
|
||||
}
|
||||
if (st.scene1) st.scene1.setAttribute('opacity', o1.toFixed(3));
|
||||
if (st.scene2) st.scene2.setAttribute('opacity', o2.toFixed(3));
|
||||
if (st.scene3) st.scene3.setAttribute('opacity', o3.toFixed(3));
|
||||
if (st.innerScreen) {
|
||||
st.innerScreen.setAttribute('fill',
|
||||
p < 0.38 ? 'url(#ve-scr-warm)' :
|
||||
p < 0.72 ? 'url(#ve-scr-cold)' :
|
||||
'url(#ve-scr-go)'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function loop(st, now) {
|
||||
if (!st.paused) {
|
||||
var delta = now - st.lastTime;
|
||||
st.lastTime = now;
|
||||
st.scrubPct += delta / DURATION;
|
||||
if (st.scrubPct >= 1) st.scrubPct -= 1;
|
||||
updatePlayhead(st);
|
||||
updateScenes(st);
|
||||
} else {
|
||||
st.lastTime = now; // keep fresh so there is no jump on resume
|
||||
}
|
||||
requestAnimationFrame(function (t) { loop(st, t); });
|
||||
}
|
||||
|
||||
function observe(st) {
|
||||
if (!('IntersectionObserver' in window)) return;
|
||||
new IntersectionObserver(function (entries) {
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
st.paused = !entries[i].isIntersecting;
|
||||
}
|
||||
}, { rootMargin: '200px', threshold: 0.05 }).observe(st.svg);
|
||||
}
|
||||
|
||||
function boot() {
|
||||
var svgs = document.querySelectorAll('.ve-svg');
|
||||
if (!svgs.length) return;
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||
for (var i = 0; i < svgs.length; i++) {
|
||||
(function (svg) {
|
||||
if (svg._veAnim) return;
|
||||
var st = makeState(svg);
|
||||
svg._veAnim = st;
|
||||
observe(st);
|
||||
requestAnimationFrame(function (t) { loop(st, t); });
|
||||
})(svgs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', boot);
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user