正文

Vue实现电梯样式锚点导航效果流程详解(vue单向数据流)学到了吗

文章摘要

这篇文章介绍了使用Vue.js和Vite框架构建的一个咖啡产品展示页面。页面左边分为11个不同的咖啡类别,包括"人气Top"、"爆款套餐"、"大师咖啡"等,每个类别下都有详细的产品信息。右边的区域展示了不同类型的咖啡产品,每个产品包含图片、名称、类型(如咖啡)、描述和购买按钮。页面通过滚动监听和锁机制来切换展示的不同类别,用户可以通过滑动屏幕来查看不同类别的咖啡产品。文章整体展示了如何利用前端技术实现多分类、多商品的展示功能。

<template> <div class=”classify”> <div class=”left”> <div class=”item” :class=”{ active: currentIndex==index }” v-for=”(item, index) in types” @click=”changeType(index)”>{{ item }} </div> </div> <div class=”right” @scroll=”handleScroll”> <div v-for=”(item, index) in shops” :key=”index” ref=”shop”> <div class=”title”>{{ item.category }}</div> <div class=”item” v-for=”(i, j) in item.data” :key=”j”> <div class=”photo”> <img src=”https://www.jb51.net/vite.svg” class=”logo” alt=”Vite logo” /> </div> <div class=”info”> <div class=”name”>{{ i.name }}</div> <div class=”type”>{{ i.type }}</div> <div class=”desc”>{{ i.desc }}</div> <div class=”buy”>购买</div> </div> </div> </div> </div> </div> </template> <script setup> import { ref, onMounted, onBeforeUnmount, nextTick } from ‘vue’ let isLock=ref(false) // 分类 let types=ref([ ‘人气Top’, ‘爆款套餐’, ‘大师咖啡’, ‘小黑杯’, ‘中国茶咖’, ‘生椰家族’, ‘厚乳拿铁’, ‘丝绒拿铁’, ‘生酪拿铁’, ‘经典拿铁’, ]) // 商品 let shops=ref([ { category: ‘人气Top’, data: [ { name: ‘冰吸生椰拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘生椰拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘摸鱼生椰拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘茉莉花香拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘丝绒拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘小甘橘美式’, type: ‘咖啡’, desc: ‘咖啡’ }, ] }, { category: ‘爆款套餐’, data: [ { name: ‘2杯套餐’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘3杯套餐’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘4杯套餐’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘5杯套餐’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘不喝咖啡套餐’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘必喝套餐’, type: ‘咖啡’, desc: ‘咖啡’ }, ] }, { category: ‘大师咖啡’, data: [ { name: ‘美式’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘加浓美式’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘橙C美式’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘澳瑞白’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘卡布奇诺’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘玛奇朵’, type: ‘咖啡’, desc: ‘咖啡’ }, ] }, { category: ‘小黑杯’, data: [ { name: ‘云南小柑橘’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘广东小柑橘’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘广西小柑橘’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘福建小柑橘’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘湖南小柑橘’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘江西小柑橘’, type: ‘咖啡’, desc: ‘咖啡’ }, ] }, { category: ‘中国茶咖’, data: [ { name: ‘碧螺知春拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘茉莉花香拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘菊花香拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘梅花香拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘兰花香拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘玫瑰花香拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, ] }, { category: ‘生椰家族’, data: [ { name: ‘冰吸生椰拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘生椰拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘摸鱼生椰拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘椰云拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘丝绒拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘陨石拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, ] }, { category: ‘厚乳拿铁’, data: [ { name: ‘厚乳拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘生椰拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘茉莉花香拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘椰云拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘丝绒拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘海盐拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, ] }, { category: ‘丝绒拿铁’, data: [ { name: ‘丝绒拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘生椰丝绒拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘黑糖丝绒拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘椰云丝绒拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘香草丝绒拿铁’, type: ‘咖啡’, desc: ‘咖啡’ } ] }, { category: ‘生酪拿铁’, data: [ { name: ‘生酪拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘绿豆拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘红豆拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘黑豆拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘黄豆拿铁’, type: ‘咖啡’, desc: ‘咖啡’ } ] }, { category: ‘经典拿铁’, data: [ { name: ‘拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘陨石拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘焦糖拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘生椰拿铁’, type: ‘咖啡’, desc: ‘咖啡’ }, { name: ‘美式’, type: ‘咖啡’, desc: ‘咖啡’ } ] }, ]) // 获取右侧商品的ref实例 let shop=ref(null) // 用来表示当前选中处于激活状态的分类的索引 let currentIndex=ref(0) // 切换类型 const changeType=val=> { currentIndex.value=val // 获取选中的dom元素 const typeItemDom=shop.value[val] // 开锁 isLock.value=true // 第一个参数为水平方向,第二个参数为纵轴方向 window.scrollTo(0, typeItemDom.offsetTop) setTimeout(()=> { //关锁 isLock.value=false }, 0) } // 监听页面滚动 const handleScroll=()=> { // 锁关了滚动事件才有效 if (!isLock.value) { types.value.forEach((item, index)=> { // console.dir(shop.value[index]); const shopItemDom=shop.value[index] // 每个模块距离顶部的距离 const offsetTop=shopItemDom.offsetTop // 页面滚动的距离 const scrollTop=document.documentElement.scrollTop if (scrollTop >=offsetTop) { // 给左边分类设置激活的效果 currentIndex.value=index } }) } } onMounted(()=> { window.addEventListener(‘scroll’, handleScroll); }) onBeforeUnmount(()=> { window.removeEventListener(‘scroll’, handleScroll); }) </script> <style scoped lang=”less”> .classify { display: flex; position: relative; .left { display: flex; flex-direction: column; align-items: center; position: fixed; left: 0; top: 0; bottom: 0; width: 92px; overflow-y: scroll; border-right: 1px solid #C1C2C4; .item { display: flex; justify-content: center; align-items: center; width: 67px; height: 29px; font-size: 15px; font-family: PingFang SC-Semibold, PingFang SC; font-weight: 600; color: #333333; } .active { color: #00B1FF; } .item:not(:last-child) { margin-bottom: 25px; } } .right { flex: 1; position: absolute; top: 0; right: 17px; overflow-y: scroll; .title { font-size: 18px; margin-bottom: 5px; } .item { display: flex; justify-content: space-between; margin-bottom: 17px; width: 246px; height: 73px; .photo { width: 58px; height: 58px; img { width: 100%; height: 100%; border-radius: 12px; border: 1px solid gray; } } .info { display: flex; flex-direction: column; position: relative; width: 171px; height: 73px; box-shadow: 0px 1px 0px 0px rgba(221, 221, 221, 1); .name { padding-left: 0; font-size: 17px; font-weight: 600; color: #333333; } .type, .desc { font-size: 14px; font-weight: 400; color: #999999; } .buy { display: flex; align-items: center; justify-content: center; position: absolute; right: 0; top: 17px; width: 67px; height: 29px; background: #E7E8EA; border-radius: 21px; font-size: 15px; font-family: PingFang SC-Semibold, PingFang SC; font-weight: 600; color: #05AFFA; } } } } } </style>