69 lines
1.2 KiB
Vue
69 lines
1.2 KiB
Vue
<script setup>
|
|
import { onMounted, useTemplateRef, onUnmounted } from "vue";
|
|
|
|
const container = useTemplateRef("container");
|
|
const item1 = useTemplateRef("item1");
|
|
|
|
let offset = 0;
|
|
|
|
let rafId;
|
|
|
|
const speed = 0.5; // pixels per frame
|
|
|
|
function animate() {
|
|
const ctnr = container.value;
|
|
const it1 = item1.value;
|
|
|
|
const width = Math.max(ctnr.offsetWidth, it1.scrollWidth);
|
|
|
|
offset -= speed;
|
|
|
|
if (offset <= -width) {
|
|
offset += width;
|
|
}
|
|
|
|
ctnr.style.transform = `translateX(${offset}px)`;
|
|
|
|
rafId = requestAnimationFrame(animate);
|
|
}
|
|
|
|
onMounted(() => {
|
|
rafId = requestAnimationFrame(animate);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
cancelAnimationFrame(rafId);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="root">
|
|
<div class="container" ref="container">
|
|
<div ref="item1">
|
|
<slot />
|
|
</div>
|
|
<div>
|
|
<slot />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.root {
|
|
overflow: hidden;
|
|
}
|
|
|
|
.container {
|
|
width: fit-content;
|
|
height: fit-content;
|
|
display: grid;
|
|
grid-auto-flow: column;
|
|
grid-auto-columns: max-content;
|
|
/* Each column fits its content */
|
|
overflow-x: visible;
|
|
will-change: transform;
|
|
gap: 10em;
|
|
}
|
|
</style>
|