improve: complete event-driven animation
This commit is contained in:
parent
a4ddd83f91
commit
2742ba43f2
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aquavox",
|
"name": "aquavox",
|
||||||
"version": "2.9.1",
|
"version": "2.9.2",
|
||||||
"private": false,
|
"private": false,
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
import type { LyricWord, ScriptItem } from '@core/lyrics/type';
|
import type { LyricWord, ScriptItem } from '@core/lyrics/type';
|
||||||
import type { LyricPos } from './type';
|
import type { LyricPos } from './type';
|
||||||
import type { Spring } from '@core/graphics/spring/spring';
|
import type { Spring } from '@core/graphics/spring/spring';
|
||||||
|
import userAdjustingProgress from '@core/state/userAdjustingProgress';
|
||||||
|
|
||||||
const viewportWidth = document.documentElement.clientWidth;
|
const viewportWidth = document.documentElement.clientWidth;
|
||||||
const blurRatio = viewportWidth > 640 ? 1.2 : 1.4;
|
const blurRatio = viewportWidth > 640 ? 1.2 : 1.4;
|
||||||
|
const scrollDuration = 0.2;
|
||||||
|
|
||||||
export let line: ScriptItem;
|
export let line: ScriptItem;
|
||||||
export let index: number;
|
export let index: number;
|
||||||
@ -23,6 +25,10 @@
|
|||||||
let positionY: number = 0;
|
let positionY: number = 0;
|
||||||
let blur = 0;
|
let blur = 0;
|
||||||
let stopped = false;
|
let stopped = false;
|
||||||
|
let we_are_scrolling = false;
|
||||||
|
let scrollTarget: number | undefined = undefined;
|
||||||
|
let scrollFrom: number | undefined = undefined;
|
||||||
|
let scrollingStartTime: number | undefined = undefined;
|
||||||
let lastPosX: number | undefined = undefined;
|
let lastPosX: number | undefined = undefined;
|
||||||
let lastPosY: number | undefined = undefined;
|
let lastPosY: number | undefined = undefined;
|
||||||
let lastUpdateY: number | undefined = undefined;
|
let lastUpdateY: number | undefined = undefined;
|
||||||
@ -39,7 +45,7 @@
|
|||||||
time = (new Date().getTime() - lastUpdateY) / 1000;
|
time = (new Date().getTime() - lastUpdateY) / 1000;
|
||||||
springY.update(time);
|
springY.update(time);
|
||||||
positionY = springY.getCurrentPosition();
|
positionY = springY.getCurrentPosition();
|
||||||
if (!springY.arrived() && !stopped) {
|
if (!springY.arrived() && !stopped && !we_are_scrolling) {
|
||||||
requestAnimationFrame(updateY);
|
requestAnimationFrame(updateY);
|
||||||
}
|
}
|
||||||
lastUpdateY = new Date().getTime();
|
lastUpdateY = new Date().getTime();
|
||||||
@ -87,6 +93,7 @@
|
|||||||
blurRadius = Math.min(offset * blurRatio, 16);
|
blurRadius = Math.min(offset * blurRatio, 16);
|
||||||
}
|
}
|
||||||
if (scrolling) blurRadius = 0;
|
if (scrolling) blurRadius = 0;
|
||||||
|
if ($userAdjustingProgress) blurRadius = 0;
|
||||||
blur = blurRadius
|
blur = blurRadius
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,6 +114,26 @@
|
|||||||
lastPosY = pos.y;
|
lastPosY = pos.y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function updateScroll(timestamp: number) {
|
||||||
|
const elapsedTime = (new Date().getTime() - scrollingStartTime!) / 1000;
|
||||||
|
const percentage = Math.min(elapsedTime / scrollDuration, 1);
|
||||||
|
positionY = scrollFrom! + (scrollTarget! - scrollFrom!) * percentage;
|
||||||
|
if (percentage < 1) {
|
||||||
|
requestAnimationFrame(updateScroll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const scrollTo = (targetY: number) => {
|
||||||
|
scrollFrom = positionY;
|
||||||
|
scrollTarget = targetY;
|
||||||
|
scrollingStartTime = new Date().getTime();
|
||||||
|
we_are_scrolling = true;
|
||||||
|
requestAnimationFrame(updateScroll);
|
||||||
|
springY!.setPosition(targetY);
|
||||||
|
we_are_scrolling = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const getInfo = () => {
|
export const getInfo = () => {
|
||||||
return {
|
return {
|
||||||
x: positionX,
|
x: positionX,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import LyricLine from './lyricLine.svelte';
|
import LyricLine from './lyricLine.svelte';
|
||||||
import createLyricsSearcher from '@core/lyrics/lyricSearcher';
|
import createLyricsSearcher from '@core/lyrics/lyricSearcher';
|
||||||
import { createRule } from 'eslint-plugin-svelte/lib/utils';
|
import userAdjustingProgress from '@core/state/userAdjustingProgress';
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
const viewportHeight = document.documentElement.clientHeight;
|
const viewportHeight = document.documentElement.clientHeight;
|
||||||
@ -48,7 +48,8 @@
|
|||||||
function initLyricComponents() {
|
function initLyricComponents() {
|
||||||
initLyricTopList();
|
initLyricTopList();
|
||||||
for (let i = 0; i < lyricComponents.length; i++) {
|
for (let i = 0; i < lyricComponents.length; i++) {
|
||||||
lyricComponents[i].init({ x: 0, y: lyricTopList[i] });
|
const currentLyric = lyricComponents[i];
|
||||||
|
currentLyric.init({ x: 0, y: lyricTopList[i] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +86,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function seekForward() {
|
||||||
|
if (!originalLyrics.scripts) return;
|
||||||
|
const relativeOrigin = lyricTopList[currentLyricIndex] - currentLyricTop;
|
||||||
|
for (let i = 0; i < lyricElements.length; i++) {
|
||||||
|
const currentLyricComponent = lyricComponents[i];
|
||||||
|
currentLyricComponent.scrollTo(lyricTopList[i] - relativeOrigin);
|
||||||
|
}
|
||||||
|
lastSeekForward = new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (!originalLyrics || !originalLyrics.scripts) return;
|
if (!originalLyrics || !originalLyrics.scripts) return;
|
||||||
lyricLines = originalLyrics.scripts!;
|
lyricLines = originalLyrics.scripts!;
|
||||||
@ -171,69 +182,40 @@
|
|||||||
scrollEventAdded = true;
|
scrollEventAdded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
let lastTriggered = $state(0);
|
|
||||||
let lastEventLyricIndex = $state(0);
|
let lastEventLyricIndex = $state(0);
|
||||||
let lastEventProgress = $state(0);
|
let lastEventProgress = $state(0);
|
||||||
|
let lastSeekForward = $state(0);
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const progressDelta = progress - lastEventProgress;
|
const progressDelta = progress - lastEventProgress;
|
||||||
const deltaInRange = 0 <= progressDelta && progressDelta <= 0.15;
|
const deltaInRange = 0 <= progressDelta && progressDelta <= 0.15;
|
||||||
const deltaTooBig = progressDelta > 0.15;
|
const deltaTooBig = progressDelta > 0.15;
|
||||||
const deltaIsNegative = progressDelta < 0;
|
const deltaIsNegative = progressDelta < 0;
|
||||||
const lyricChanged = currentLyricIndex !== lastEventLyricIndex;
|
const lyricChanged = currentLyricIndex !== lastEventLyricIndex;
|
||||||
const lyricIndexDeltaTooBig = Math.abs(currentLyricIndex - lastEventLyricIndex) > 1;
|
const lyricIndexDeltaTooBig = Math.abs(currentLyricIndex - lastEventLyricIndex) > 1;
|
||||||
if (lyricChanged && !lyricIndexDeltaTooBig && deltaInRange) {
|
|
||||||
console.log("Event: regular move");
|
|
||||||
}
|
|
||||||
else if (deltaTooBig && lyricChanged) {
|
|
||||||
console.log("Event: seek forward");
|
|
||||||
}
|
|
||||||
else if (deltaIsNegative && lyricChanged) {
|
|
||||||
console.log("Event: seek backward");
|
|
||||||
}
|
|
||||||
lastEventLyricIndex = currentLyricIndex;
|
|
||||||
lastEventProgress = progress;
|
|
||||||
});
|
|
||||||
|
|
||||||
$effect(() => {
|
lastEventLyricIndex = currentLyricIndex;
|
||||||
if (!lyricsContainer || lyricComponents.length < 0) return;
|
lastEventProgress = progress;
|
||||||
if (progress >= nextUpdate - 0.5 && !scrolling) {
|
if (!lyricChanged) return;
|
||||||
|
if (!lyricIndexDeltaTooBig && deltaInRange) {
|
||||||
|
console.log("Event: regular move");
|
||||||
|
console.log(new Date().getTime() , lastSeekForward);
|
||||||
computeLayout();
|
computeLayout();
|
||||||
}
|
}
|
||||||
if (Math.abs(lastProgress - progress) > 0.5) {
|
else if ($userAdjustingProgress) {
|
||||||
scrolling = false;
|
if (deltaTooBig && lyricChanged) {
|
||||||
}
|
console.log("Event: seek forward");
|
||||||
if (lastProgress - progress > 0) {
|
seekForward();
|
||||||
computeLayout();
|
} else if (deltaIsNegative && lyricChanged) {
|
||||||
nextUpdate = progress;
|
console.log("Event: seek backward");
|
||||||
} else {
|
seekForward();
|
||||||
const lyricLength = originalLyrics.scripts!.length;
|
|
||||||
const currentEnd = originalLyrics.scripts![currentLyricIndex].end;
|
|
||||||
const nextStart = originalLyrics.scripts![Math.min(currentLyricIndex + 1, lyricLength - 1)].start;
|
|
||||||
if (currentEnd !== nextStart) {
|
|
||||||
nextUpdate = currentEnd;
|
|
||||||
} else {
|
|
||||||
nextUpdate = nextStart;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastProgress = progress;
|
else {
|
||||||
|
console.log("Event: regular move");
|
||||||
|
computeLayout();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// $: {
|
|
||||||
// for (let i = 0; i < lyricElements.length; i++) {
|
|
||||||
// const s = originalLyrics.scripts![i].start;
|
|
||||||
// const t = originalLyrics.scripts![i].end;
|
|
||||||
// // Explain:
|
|
||||||
// // The `currentLyricIndex` is also used for locating & layout computing,
|
|
||||||
// // so when the current progress is in the interlude between two lyrics,
|
|
||||||
// // `currentLyricIndex` still needs to have a valid value to ensure that
|
|
||||||
// // the style and scrolling position are calculated correctly.
|
|
||||||
// // But in that situation, the “current lyric index” does not exist.
|
|
||||||
// const isCurrent = i == currentLyricIndex && s <= progress && progress <= t;
|
|
||||||
// const currentLyricComponent = lyricComponents[i];
|
|
||||||
// currentLyricComponent.setCurrent(isCurrent);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Initialize
|
// Initialize
|
||||||
if (localStorage.getItem('debugMode') == null) {
|
if (localStorage.getItem('debugMode') == null) {
|
||||||
@ -258,16 +240,17 @@
|
|||||||
function lyricClick(lyricIndex: number) {
|
function lyricClick(lyricIndex: number) {
|
||||||
if (player === null || originalLyrics.scripts === undefined) return;
|
if (player === null || originalLyrics.scripts === undefined) return;
|
||||||
player.currentTime = originalLyrics.scripts[lyricIndex].start;
|
player.currentTime = originalLyrics.scripts[lyricIndex].start;
|
||||||
|
userAdjustingProgress.set(false);
|
||||||
player.play();
|
player.play();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--<svelte:window on:keydown={onKeyDown} />-->
|
<svelte:window on:keydown={onKeyDown} />
|
||||||
|
|
||||||
{#if debugMode}
|
{#if debugMode}
|
||||||
<span
|
<span
|
||||||
class="text-white text-lg absolute z-50 px-2 py-0.5 m-2 rounded-3xl bg-white bg-opacity-20 backdrop-blur-lg right-0 font-mono">
|
class="text-white text-lg absolute z-50 px-2 py-0.5 m-2 rounded-3xl bg-white bg-opacity-20 backdrop-blur-lg right-0 font-mono">
|
||||||
progress: {progress.toFixed(2)}, nextUpdate: {nextUpdate}, scrolling: {scrolling}, current: {currentLyricIndex}
|
progress: {progress.toFixed(2)}, nextUpdate: {nextUpdate}, scrolling: {scrolling}, current: {currentLyricIndex}, uap: {$userAdjustingProgress}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user