vue3手风琴

该文章描述了一个使用Vue.js实现的折叠内容组件。组件基于flex布局,利用CSS过渡效果实现内容推拉动画。每个项目包含标题和内容两部分,内容区域默认隐藏,点击标题显示。组件支持动态内容,使用插槽进行自定义,并提供了当前显示项的属性和方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

结构搭建

搭建结构主要实现盒子间的排列效果。

用flex布局或者其他布局方式将内容在一行排列

把每一项的内容和项头用盒子包裹, 内容就是这一项要展示的内容(content-box),项头就是可以点击显示这一项的盒子(title-box)。

默认第一项的context-box显示,即有宽度,其他项的content-box宽度为0.

content-box加上过渡样式,这样就会有推拉的感觉。

由于是vue组件,那我们可以给它加上插槽,让组件可以自定义显示内容

功能搭建

点击项头显示 点击项的内容区域。由于我们已经将未显示的content-box宽度设为0,所以只需要添加一个变量将带有宽度的类样式赋给当前点击项即可显示。

宽度变化时,里面的内容样式可能会发生改动,所以我们需要给内容里的box加一个透明度的过渡效果,避免样式发生改变试被看到。

加插槽,插槽点随意,合理就行。

加属性,例如,当前显示项、当前显示项内容等。

<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>

使用

<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>
### Vue3 实现手风琴式弹窗组件 在构建基于 Vue3手风琴式弹窗组件时,可以利用 Vue3 提供的组合 API 和响应式特性来简化开发过程。下面是一个简单的实现方案。 #### 创建基础结构 首先定义一个名为 `Accordion` 的组件: ```html <template> <div class="accordion"> <div v-for="(item, index) in items" :key="index" @click="toggle(index)"> <header>{{ item.title }}</header> <transition name="fade"> <section v-if="activeIndex === index">{{ item.content }}</section> </transition> </div> </div> </template> <script setup> import { ref } from &#39;vue&#39;; const props = defineProps({ items: { type: Array, required: true } }); let activeIndex = ref(null); function toggle(index) { if (activeIndex.value === index) { activeIndex.value = null; } else { activeIndex.value = index; } } </script> <style scoped> .fade-enter-active, .fade-leave-active { transition: max-height 0.3s ease-out; } .fade-enter-from, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ { max-height: 0; } .fade-enter-to, .fade-leave-from { max-height: 50px; /* Adjust based on content */ } </style> ``` 此代码片段展示了如何创建一个基本的手风琴组件[^1]。通过点击标题部分展开或折叠对应的面板内容,并应用了一个简单的 CSS 过渡动画以增强用户体验。 为了使该组件更加实用,在实际项目中可能还需要考虑更多细节,比如支持多级嵌套、自定义样式以及与其他 UI 库集成等功能扩展。 #### 使用示例 要在页面上使用这个手风琴组件,只需将其导入并传递适当的数据即可: ```html <template> <div id="app"> <Accordion :items="data"/> </div> </template> <script setup> import Accordion from &#39;./components/Accordion.vue&#39;; import { reactive } from &#39;vue&#39;; // 示例数据 const data = reactive([ { title: &#39;Section One&#39;, content: &#39;Content of section one.&#39; }, { title: &#39;Section Two&#39;, content: &#39;Content of section two.&#39; }, { title: &#39;Section Three&#39;, content: &#39;Content of section three.&#39; } ]); </script> ``` 上述例子说明了如何配置和初始化手风琴组件及其所需的内容项数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值