Задать вопрос
@Now159

Как сделать плавное сворачивание и разворачивание списка?

<template>
  <div>
    <button @click="toggleShowMore">{{ showMoreText }}</button>
    <transition-group name="fade">
      <div v-for="(item, index) in visibleItems" :key="index" class="list-item" >
        {{ item.name }} - {{ item.description }}
      </div>
    </transition-group>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';

const items = ref([]);
const showMore = ref(false);
const visibleItems = ref([]);

const showMoreText = ref('Показать еще');

const toggleShowMore = () => {
  showMore.value = !showMore.value;
  updateVisibleItems();
};

const updateVisibleItems = () => {
  if (showMore.value) {
    visibleItems.value = items.value;
    showMoreText.value = 'Скрыть';
  } else {
    visibleItems.value = items.value.slice(0, 0);
    setTimeout(() =>  showMoreText.value = 'Показать еще', 900);
  }
};

onMounted(() => {
  items.value = [
    { name: 'Item 1', description: 'Description 1' },
    { name: 'Item 2', description: 'Description 2' },
    { name: 'Item 3', description: 'Description 3' },
    { name: 'Item 3', description: 'Description 4' },
    { name: 'Item 4', description: 'Description 5' },
    { name: 'Item 5', description: 'Description 6' },
    { name: 'Item 6', description: 'Description 7' },
    { name: 'Item 7', description: 'Description 8' },
    { name: 'Item 8', description: 'Description 9' },
    { name: 'Item 9', description: 'Description 10' }
  ];

  // Изначально показываем только первые 3 элемента
  visibleItems.value = items.value.slice(0, 0);
});
</script>

<style scoped>
.list-item {
  margin-top: 10px;
}
.fade-enter-active,
.fade-leave-active {
  animation: fadeAnimation 0.9s  forwards;
}
.fade-enter,
.fade-leave-to {
  animation: fadeRAnimation 0.9s  forwards;
}
@keyframes fadeAnimation {
  from {
    max-height: 0;
  }
  to {
    max-height: 1000px;
  }
}
@keyframes fadeRAnimation {
  from {
    max-height: 1000px;
  }
  to {
    max-height: 0;
  }
}
</style>

Есть такой код, открытие закрытие списка на vue3, нужно, чтобы список плавно разворачивался вниз и сворачивался без изменения расстояния между элементами в процессе свертывания (просто эффект задвигания).
Допустим, есть 3 элемента, я нажимаю, идет плавный разворот до максимума, нажимаю ещё раз, список плавно сворачивается до 3ех.
Сейчас у меня почти так, но список в процессе свертывания изменяет расстояние между элементами и перед закрытием появляется непонятная задержка, которую я попробовал вылечить setTimeout'ом, но это не решает основной проблемы.

Есть решение на vue 2.
<script>
export default {
  name: "smooth-deployment",
  render(h) {
    const items = this.$slots.default;
    const gridStyle = this.isGrid ? `display: grid; grid-template-columns: repeat(7, 1fr);
     row-gap: ${this.gridGapRow}px;  column-gap: ${this.gridGapColumn}px;  grid-column: 1 / span ${this.gridColumns};` : ''
    return h('div', [
      items.slice(0, this.minShow),
      h('div', {
        ref: 'dropdown',
        attrs: {
          style: `overflow: hidden; transition: height ${this.duration}ms; ${gridStyle}`,
        },
      }, items.slice(this.minShow)),
    ]);
  },
  props: {
    minShow: Number,
    showAll: Boolean,
    duration: {
      type: Number,
      default: 500,
    },
    isGrid: {
      type: Boolean,
      default: false,
    },
    gridColumns: {
      type: Number,

    },
    gridGapRow: {
      type: Number,
      default: 20,
    },
    gridGapColumn: {
      type: Number,
      default: 20,
    },
  },
  watch: {
    showAll(val) {
      this[val ? 'open' : 'close']();
    },
  },
  mounted() {
    if (!this.showAll) {
      this.$refs.dropdown.style.height = '0px';
    }
  },
  methods: {
    open() {
      const el = this.$refs.dropdown;

      if (this.timeout) {
        clearTimeout(this.timeout);
      }

      el.style.height = `${el.scrollHeight}px`;

      this.timeout = setTimeout(() => {
        el.style.height = 'auto';
        this.timeout = null;
      }, this.duration);
    },
    close() {
      const el = this.$refs.dropdown;

      if (this.timeout) {
        clearTimeout(this.timeout);
      }

      el.style.height = `${el.scrollHeight}px`;
      setTimeout(() => el.style.height = '0px', 25);

      this.timeout = setTimeout(() => {
        this.timeout = null;
      }, this.duration);
    },
  },
}
</script>

Но оно не работает на vue 3, пробовал переписать - не хватило мозгов, да и хочется какое-то решение попроще.
  • Вопрос задан
  • 362 просмотра
Подписаться 1 Простой Комментировать
Решения вопроса 1
0xD34F
@0xD34F Куратор тега Vue.js
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы