Restore JS scroll animations, move WASM AutoScroll to util/wasm/
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 17s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 17s
Revert AutoScroll and Headline components to their original JS requestAnimationFrame implementations. Keep the WASM-based AutoScroll as an alternative at util/wasm/AutoScroll.vue. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,22 +1,55 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, useTemplateRef, onUnmounted } from "vue";
|
import { onMounted, useTemplateRef, onUnmounted } from "vue";
|
||||||
import { HeadlineScroller } from "@/wasm/stp_wasm.js";
|
|
||||||
|
|
||||||
const container = useTemplateRef("container");
|
const container = useTemplateRef("container");
|
||||||
const item1 = useTemplateRef("item1");
|
const item1 = useTemplateRef("item1");
|
||||||
|
|
||||||
let scroller = null;
|
let offset = 0;
|
||||||
|
let cachedWidth = 0;
|
||||||
|
|
||||||
|
let rafId;
|
||||||
|
|
||||||
|
const speed = 0.5; // pixels per frame
|
||||||
|
|
||||||
|
function measureWidth() {
|
||||||
|
const ctnr = container.value;
|
||||||
|
const it1 = item1.value;
|
||||||
|
if (ctnr && it1) {
|
||||||
|
cachedWidth = Math.max(ctnr.offsetWidth, it1.scrollWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
const ctnr = container.value;
|
||||||
|
if (!ctnr || cachedWidth === 0) {
|
||||||
|
rafId = requestAnimationFrame(animate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset -= speed;
|
||||||
|
|
||||||
|
if (offset <= -cachedWidth) {
|
||||||
|
offset += cachedWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctnr.style.transform = `translateX(${offset}px)`;
|
||||||
|
|
||||||
|
rafId = requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
let resizeObserver;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!container.value || !item1.value) return;
|
measureWidth();
|
||||||
scroller = new HeadlineScroller(container.value, item1.value);
|
rafId = requestAnimationFrame(animate);
|
||||||
scroller.start();
|
|
||||||
|
resizeObserver = new ResizeObserver(measureWidth);
|
||||||
|
resizeObserver.observe(container.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
scroller?.destroy();
|
cancelAnimationFrame(rafId);
|
||||||
scroller?.free();
|
resizeObserver?.disconnect();
|
||||||
scroller = null;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,108 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="container" class="overflow-y-auto">
|
<div ref="container" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" class="overflow-y-auto">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useTemplateRef, onMounted, onBeforeUnmount } from "vue";
|
import { useTemplateRef, onMounted, onBeforeUnmount } from "vue";
|
||||||
import { AutoScroller } from "@/wasm/stp_wasm.js";
|
|
||||||
|
|
||||||
const container = useTemplateRef("container");
|
const container = useTemplateRef("container");
|
||||||
|
|
||||||
let scroller = null;
|
const SPEED = 0.0005; // % per frame
|
||||||
|
const PAUSE = 2000; // ms at top/bottom
|
||||||
|
|
||||||
|
let pos = 0;
|
||||||
|
let direction = 1; // 1 = down, -1 = up
|
||||||
|
let hovered = false;
|
||||||
|
let rafId = null;
|
||||||
|
let pauseTimeoutId = null;
|
||||||
|
let cachedScrollHeight = 0;
|
||||||
|
|
||||||
|
function measureScrollHeight() {
|
||||||
|
const el = container.value;
|
||||||
|
if (el) cachedScrollHeight = el.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopLoop() {
|
||||||
|
if (rafId !== null) {
|
||||||
|
cancelAnimationFrame(rafId);
|
||||||
|
rafId = null;
|
||||||
|
}
|
||||||
|
if (pauseTimeoutId !== null) {
|
||||||
|
clearTimeout(pauseTimeoutId);
|
||||||
|
pauseTimeoutId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLoop() {
|
||||||
|
stopLoop();
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseEnter() {
|
||||||
|
hovered = true;
|
||||||
|
stopLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseLeave() {
|
||||||
|
hovered = false;
|
||||||
|
const el = container.value;
|
||||||
|
if (el && cachedScrollHeight > 0) {
|
||||||
|
pos = el.scrollTop / cachedScrollHeight;
|
||||||
|
}
|
||||||
|
startLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function schedulePause(callback) {
|
||||||
|
stopLoop();
|
||||||
|
pauseTimeoutId = setTimeout(callback, PAUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tick() {
|
||||||
|
rafId = null;
|
||||||
|
const el = container.value;
|
||||||
|
if (hovered) return;
|
||||||
|
|
||||||
|
if (!el || cachedScrollHeight === 0) {
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reachedBottom = pos >= 1;
|
||||||
|
const reachedTop = pos <= 0;
|
||||||
|
|
||||||
|
if (reachedBottom) {
|
||||||
|
pos = 0.999;
|
||||||
|
direction = -1;
|
||||||
|
schedulePause(startLoop);
|
||||||
|
return;
|
||||||
|
} else if (reachedTop && direction === -1) {
|
||||||
|
pos = 0.001;
|
||||||
|
direction = 1;
|
||||||
|
schedulePause(startLoop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += direction * SPEED;
|
||||||
|
|
||||||
|
el.scrollTop = pos * cachedScrollHeight;
|
||||||
|
|
||||||
|
rafId = requestAnimationFrame(tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
let resizeObserver;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!container.value) return;
|
measureScrollHeight();
|
||||||
scroller = new AutoScroller(container.value);
|
schedulePause(startLoop);
|
||||||
scroller.start();
|
|
||||||
|
resizeObserver = new ResizeObserver(measureScrollHeight);
|
||||||
|
resizeObserver.observe(container.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
scroller?.destroy();
|
stopLoop();
|
||||||
scroller?.free();
|
resizeObserver?.disconnect();
|
||||||
scroller = null;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
26
vue/src/components/util/wasm/AutoScroll.vue
Normal file
26
vue/src/components/util/wasm/AutoScroll.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="container" class="overflow-y-auto">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useTemplateRef, onMounted, onBeforeUnmount } from "vue";
|
||||||
|
import { AutoScroller } from "@/wasm/stp_wasm.js";
|
||||||
|
|
||||||
|
const container = useTemplateRef("container");
|
||||||
|
|
||||||
|
let scroller = null;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!container.value) return;
|
||||||
|
scroller = new AutoScroller(container.value);
|
||||||
|
scroller.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
scroller?.destroy();
|
||||||
|
scroller?.free();
|
||||||
|
scroller = null;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user