Restore JS scroll animations, move WASM AutoScroll to util/wasm/
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:
2026-04-14 16:53:32 +01:00
parent c684fcb858
commit 68dca953f2
3 changed files with 158 additions and 17 deletions

View File

@@ -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>

View File

@@ -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>

View 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>