JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.min.js"></script>
<div id="app">
<button @click="toggle">{{ timeoutId ? "stop" : "start" }}</button>
<input type="range" v-model.number="transitionDuration" min="100" max="1500" step="100">
{{ transitionDuration }} ms
<transition-group
name="item"
tag="div"
class="items-wrapper"
:style="{ '--transition-duration': transitionDuration + 'ms' }"
>
<div v-for="n in visibleItems" :key="n.id" class="item">{{ n.text }}</div>
</transition-group>
</div>
CSS
* {
font-family: monospace;
}
.items-wrapper {
height: 500px;
overflow: hidden;
border: 2px solid black;
position: relative;
}
.item {
border: 1px solid silver;
background: white;
padding: 10px;
width: 100%;
transition: var(--transition-duration) linear;
}
.item-enter-active,
.item-leave-active {
position: absolute;
}
.item-leave-to {
transform: translateY(-100%);
}
.item-enter {
transform: translateY(100%);
}
JavaScript
new Vue({
el: '#app',
data: () => ({
items: Array.from(
{ length: 100 },
(n, i) => ({ id: i, text: `${(-~i) ** 2}` })
),
visibleItemsCount: 20,
firstVisibleItemIndex: 0,
timeoutId: null,
transitionDuration: 700,
}),
computed: {
visibleItems() {
const { items, firstVisibleItemIndex, visibleItemsCount } = this;
const visibleItems = [];
for (
let i = firstVisibleItemIndex;
visibleItems.length < Math.min(visibleItemsCount, items.length);
i = (i + 1) % items.length
) {
visibleItems.push(items[i]);
}
return visibleItems;
},
},
methods: {
step() {
this.stop();
this.firstVisibleItemIndex =
(this.firstVisibleItemIndex + 1) % this.items.length;
this.timeoutId = setTimeout(this.step, this.transitionDuration);
},
stop() {
clearTimeout(this.timeoutId);
this.timeoutId = null;
},
toggle() {
this[this.timeoutId ? 'stop' : 'step']();
},
},
mounted() {
this.step();
},
});