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();
  },
});