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

1 背景

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

2 思路

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

3 实现

javascript 复制代码
<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>
相关推荐
qq_316837753 小时前
uni.chooseMedia 读取base64 或 二进制
开发语言·前端·javascript
Zoey的笔记本4 小时前
2026告别僵化工作流:支持自定义字段的看板工具选型与部署指南
大数据·前端·数据库
小二·4 小时前
Python Web 开发进阶实战:混沌工程初探 —— 主动注入故障,构建高韧性系统
开发语言·前端·python
gis开发4 小时前
【无标题】
java·前端·javascript
小二·4 小时前
Python Web 开发进阶实战:低代码平台集成 —— 可视化表单构建器 + 工作流引擎实战
前端·python·低代码
慧一居士4 小时前
Vite 中配置环境变量方法及完整示例
前端·vue.js
梦因you而美4 小时前
XPath 元素定位全方位技术文档
javascript·xpath·xpath定位
天意pt4 小时前
Idempotency 幂等性 - 点赞和投票功能
前端·javascript·express
weixin_427771615 小时前
cursor 智能commit
前端
努力的小陈^O^5 小时前
问题:Spring循环依赖问题排查与解决
java·开发语言·前端