vue 纵向滚动菜单, 点击滚动到选中菜单

15 篇文章 0 订阅
13 篇文章 0 订阅

1 背景

需要设计一个纵向滚动菜单,要求丝滑点,默认显示选中菜单

在这里插入图片描述

2 思路

  • 给定一个容器,样式包含overflow:hidden,默认高宽足够显示一个菜单(以下用图标代替菜单),鼠标悬浮时增大容器高度,显示更多图标
  • 设置两个div用于触发上下滚动(本意直接用每页第一和最后图标进行触发,但是这样会导致鼠标悬停时直接滚动,体验不好)
  • 鼠标点击时将点击图标滚动到当前页的第一个图标,鼠标没有点击,移出后菜单还原

3 实现

<template>
  <div class="container" @mouseleave="handleMouseLeave">
    <div
      class="action up"
      v-if="scrollTop !== -1 * (totalHeight - pageHeight)"
      @mouseover="handleMouseOver('up')"
    ></div>
    <ul :style="{ transform: `translateY(${scrollTop}px)` }">
      <li
        v-for="(item, index) in imgs"
        :key="index"
        :style="{ padding: itemPadding + 'px' }"
      >
        <img
          :src="item"
          alt=""
          :style="{ width: iconSize[0] + 'px', height: iconSize[1] + 'px' }"
          @click="handleClick(index)"
        />
      </li>
    </ul>
    <div
      class="action dowm"
      v-if="scrollTop !== 0"
      @mouseover="handleMouseOver('down')"
    ></div>
  </div>
</template>

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

const props = defineProps({
  iconSize: {
    type: Array,
    default: () => [60, 60],
  },
  pageSize: {
    type: Number,
    default: 6,
  },
  itemPadding: {
    type: Number,
    default: 20,
  },
});
const list = [
  "vue.svg",
  "back.svg",
  "behance.svg",
  "down.svg",
  "hands.svg",
  "hdd.svg",
  "next.svg", //
  "one.svg",
  "snow.svg",
  "three.svg",
  "up.svg",
  "upload.svg",
  "vip.svg", //
  "dvi.svg",
  "bone.svg",
  "bird.svg",
  "ipad.svg",
  "duck.svg",
  "deer.svg", //
  "fish.svg",
  "clap.svg",
  "eagle.svg",
];
const imgs = ref(
  list.map((item) => new URL(`./assets/${item}`, import.meta.url).href)
);

const scrollTop = ref(0);
const baseIndex = ref(0);

const actionHeight = computed(() => {
  return props.itemPadding+ "px";
});

const itemHeight = computed(() => {
  return props.iconSize[1] + 2 * props.itemPadding;
});
const containerBaseSize = computed(() => {
  return itemHeight.value + "px";
});
const containerHeight = computed(() => {
  return itemHeight.value * props.pageSize + "px";
});
const pageHeight = computed(() => {
  return props.pageSize * itemHeight.value;
});
const totalHeight = computed(() => {
  return imgs.value.length * itemHeight.value;
});

const handleMouseOver = async (direction: string) => {
  if (direction === "up") {
    if (
      scrollTop.value + (totalHeight.value - pageHeight.value) >
      pageHeight.value
    ) {
      scrollTop.value += -1 * pageHeight.value;
    } else {
      scrollTop.value = -1 * (totalHeight.value - pageHeight.value);
    }
  } else {
    if (scrollTop.value + pageHeight.value >= 0) {
      scrollTop.value = 0;
    } else {
      scrollTop.value += pageHeight.value;
    }
  }
};

const handleClick = (index: number) => {
  scrollTop.value = -1 * index * itemHeight.value;
  baseIndex.value = index;
};
const handleMouseLeave = () => {
  handleClick(baseIndex.value);
};
</script>

<style scoped lang="scss">
.container {
  overflow: hidden;
  transition: all 0.5s;
  position: relative;
  width: v-bind(containerBaseSize);
  height: v-bind(containerBaseSize);
  &:hover {
    height: v-bind(containerHeight);
  }
  .action {
    cursor: pointer;
    position: absolute;
    width: 100%;
    height: v-bind(actionHeight);
    z-index: 10;
    &.up {
      top: 0;
    }
    &.dowm {
      bottom: 0;
    }
  }
  ul {
    box-sizing: content-box;
    margin: 0;
    padding: 0;
    height: 100%;
    transition: all ease-in-out 1s;
    list-style: none;
    li {
      line-height: 0;
      position: relative;

      img {
        cursor: pointer;
        transition: all 0.5s;
        &:hover {
          scale: 1.3;
        }
      }
    }
  }
}
</style>

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以通过以下几个步骤实现: 1. 在菜单中添加点事件,获取对应的锚点id。 2. 使用`ref`属性获取到内容容器的元素。 3. 在内容容器上绑定`scroll`事件,监听滚动位置。 4. 根据当前滚动位置,判断哪个锚点处于可视范围内,并高亮对应的菜单项。 具体代码如下: 菜单部分: ``` <template> <div> <ul> <li v-for="item in menu" :key="item.id" @click="scrollToAnchor(item.id)" :class="{active: activeAnchor === item.id}"> {{ item.title }} </li> </ul> </div> </template> <script> export default { data() { return { menu: [ { id: 'a1', title: '锚点1' }, { id: 'a2', title: '锚点2' }, { id: 'a3', title: '锚点3' } ], activeAnchor: '' // 当前激活的锚点id } }, methods: { scrollToAnchor(anchorId) { // 获取对应的锚点元素 const anchorElement = document.getElementById(anchorId); if (anchorElement) { // 滚动到锚点位置 anchorElement.scrollIntoView({ behavior: 'smooth' }); // 设置当前激活的锚点id this.activeAnchor = anchorId; } } } } </script> ``` 内容部分: ``` <template> <div ref="content" @scroll="onScroll"> <div id="a1">锚点1内容</div> <div id="a2">锚点2内容</div> <<div id="a3">锚点3内容</div> </div> </template> <script> export default { methods: { onScroll() { // 获取内容容器的高度和滚动位置 const contentHeight = this.$refs.content.offsetHeight; const scrollTop = this.$refs.content.scrollTop; // 遍历所有锚点,判断哪个处于可视范围内 for (let i = 0; i < this.menu.length; i++) { const anchorId = this.menu[i].id; const anchorElement = document.getElementById(anchorId); if (anchorElement) { const anchorTop = anchorElement.offsetTop; const anchorBottom = anchorTop + anchorElement.offsetHeight; if (scrollTop >= anchorTop && scrollTop < anchorBottom) { // 设置当前激活的锚点id this.$emit('active-anchor', anchorId); break; } } } } } } </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值