[Push-pull frame-accordion] Vue3 components to achieve accordion effect

Briefly

Components for the accordion effect are sometimes used while working.
Record the implementation code and implementation ideas here.

accordion implementation

structure building

The building structure mainly realizes the arrangement effect between boxes.

  • Use flex layout or other layout methods to arrange content in a row
  • Wrap the content and header of each item in a box, the content is the content to be displayed (content-box), and the header is the box (title-box) that can be clicked to display this item.
  • By default, the context-box of the first item is displayed, that is, it has a width, and the content-box width of other items is 0.
  • Content-box plus transition style, so that there will be a feeling of push and pull.
  • Since it is a vue component, we can add a slot to it so that the component can customize the display content

insert image description here

Functional construction

  • The clicked item header displays the content area of ​​the clicked item. Since we have set the undisplayed content-box width to 0, we only need to add a variable to assign the class style with width to the currently clicked item to display.
  • When the width changes, the content style inside may change, so we need to add a transparency transition effect to the box in the content to avoid the style change from being seen.
  • Add a slot, the slot point is random, just reasonable.
  • Add attributes, such as the current display item, the content of the current display item, etc.

the code

<template>
  <div class="accordion">
    <slot>
      <div class="item__box" v-for="(item, i) of list" :key="i">
        <div
          class="item__content"
          :class="[`item__content${i}`, { 'item__content-active': activeIndex === i }]"
        >
          <div class="item__content__detail">
            <slot :name="`item__content${i}`"> </slot>
          </div>
        </div>
        <div
          class="item__title"
          :class="[`item__title${i}`, { 'item__title-active': activeIndex === i }]"
        >
          <div class="item__title__detail" @click="() => tabChange(i)">
            <slot :name="`item__title${i}`">
              <img
                v-if="!!item.icon"
                :src="activeIndex === i ? getImageUrl(item.activeIcon  as string) : getImageUrl(item.icon)"
                alt=""
              />
              <div class="item__title__detail__text">{
   
   { item.title }}</div>
            </slot>
          </div>
        </div>
      </div>
    </slot>
  </div>
</template>
<script lang="ts" setup>
import {
      
       reactive, toRefs, onBeforeMount, onMounted, ref } from 'vue'
import {
      
       getImageUrl } from '@/untils/index'

interface Item {
      
      
  icon?: string
  activeIcon?: string
  title: string
}

defineProps({
      
      
  list: {
      
      
    type: Array<Item>,
    default: () => []
  }
})

const activeIndex = ref(0)

const tabChange = (index: number) => {
      
      
  activeIndex.value = index
}
</script>
<style lang="scss" scoped>
.accordion {
      
      
  position: relative;
  width: 1181px;
  height: 512px;
  display: flex;
  color: #fff;
  background: url('@/assets/images/bg_shoufq.png');
  overflow: hidden;
}
.item__box {
      
      
  position: relative;
  display: flex;
  overflow: hidden;
  margin-left: 4px;
}
.item__content {
      
      
  flex-shrink: 0;
  width: 0;
  left: 1000px;
  overflow: hidden;
  transition: all 0.5s ease;
}
.item__title {
      
      
  width: 110px;
  flex-shrink: 0;
  background: #00398e;
}
.item__content__detail {
      
      
  width: 842px;
  height: 512px;
  opacity: 0;
  transition: opacity 0.5s ease;
}
.item__content-active {
      
      
  width: 842px;
  left: 0;
  .item__content__detail {
      
      
    opacity: 1;
  }
}
.item__title__detail {
      
      
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 512px;
  cursor: pointer;

  &__text {
      
      
    writing-mode: vertical-lr;
    height: 160px;
    font-size: 28px;
    font-family: PingFangSC-Semibold, PingFang SC;
    font-weight: 600;
    color: #ffffff;
    line-height: 40px;
    letter-spacing: 8px;
  }
}
.item__title-active {
      
      
  background: linear-gradient(180deg, #ffbb1a 0%, #f57c00 100%);
}

@keyframes moveInRight {
      
      
  from {
      
      
    transform: translateX(100%);
  }
  to {
      
      
    transform: translateX(0);
  }
}
</style>

use

<template>
  <div class="container">
    <Accordion class="three__box__text" :list="dataOpenList">
      <template #item__content0>
        <div class="edu__gaikuang">11</div>
      </template>
      <template #item__content1>
        <div class="edu__gongbao">22</div>
      </template>
      <template #item__content2>
        <div class="edu_nianjian">33</div>
      </template>
    </Accordion>
  </div>
</template>
<script lang="ts" setup>
import {
      
       reactive, toRefs, onBeforeMount, onMounted } from 'vue'
import Accordion from '@/components/accordion.vue'

const dataOpenList = reactive([
  {
      
      
    // icon: 'icon/icon_edu_gk.png',
    // activeIcon: 'icon/icon_edu_gk_active.png',
    title: '教育概况'
  },
  {
      
      
    // icon: 'icon/icon_edu_nb.png',
    // activeIcon: 'icon/icon_edu_nb_active.png',

    title: '教育公报'
  },
  {
      
      
    // icon: 'icon/icon_edu_nj.png',
    // activeIcon: 'icon/icon_edu_nj_active.png',
    title: '教育年鉴'
  }
])
</script>
<style lang="scss" scoped></style>

insert image description here
Effect
insert image description here

epilogue

According to this idea, you can realize accordion effects in different directions and different arrangements.

Guess you like

Origin blog.csdn.net/qq_43231248/article/details/129320354