vue实现商品分类左右联动

vue中better-scroll实现商品分类左右联动

结合better-scroll插件实现商品分类左右联动交互方式。

前提准备

  • 安装: npm i better-scroll -S
  • 组件中引入: import BScroll from 'better-scroll'
  • better-scroll官网

相关文章:

better-scroll监听元素吸顶

better-scroll商品详情联动


步骤:

  • 先实现左联右
  • 再实现右联左

效果图

在这里插入图片描述


1. 初始化数据

data() {
    return {
        currentIndex: 0, //当前选中index
        scrollY: 0,
        heightList: [0], //存储某个分类下的商品高度列表
        flag: false, //标识是否选中
        fixedTitle: '', //当前分类标题
        tagList: [{"type": 1, "name": "全部"}, {"type": 2, "name": "热销"}], //分类列表
        tagIndex: 0, //分类tag当前index
        type: 1, // 1:全部 2:热销
        categoryList: [ // 数据仅供测试,请自行copy多些来看效果
            {   
                categoryId: 1,
                name: "健康蔬菜",
                goods: [
                    {
                        goodsName: '大个小番茄',
                        goodsSummary: '绿色小番茄',
                        goodsStock: 231,
                        goodsRetailPrice: 5.4,
                        goodsMarketPrice: 6.8,
                        // goodsMainImage: require('../../assets/images/icon/icon_1.png')
                        goodsMainImage: 'xxxxx'
                    },
                    {
                        goodsName: '新鲜小奶白菜',
                        goodsSummary: '清脆、鲜嫩蔬菜',
                        goodsStock: 134,
                        goodsRetailPrice: 3.8,
                        goodsMarketPrice: 5.5,
                        goodsMainImage: 'xxxxx'
                    },
                ]
            },
            {   
                categoryId: 2,
                name: "时令蔬菜",
                goods: [
                    {
                        goodsName: '大又甜地瓜',
                        goodsSummary: '自家种植放心品尝',
                        goodsStock: 344,
                        goodsRetailPrice: 6.5,
                        goodsMarketPrice: 8.4,
                        goodsMainImage: 'xxxxx'
                    },
                    {
                        goodsName: '精美套餐',
                        goodsSummary: '健康搭配新鲜蔬菜',
                        goodsStock: 1099,
                        goodsRetailPrice: 45.9,
                        goodsMarketPrice: 65.8,
                        goodsMainImage: 'xxxxx'
                    },
                    {
                        goodsName: '水果黄瓜',
                        goodsSummary: '可口脆嫩水果黄瓜',
                        goodsStock: 785,
                        goodsRetailPrice: 3.5,
                        goodsMarketPrice: 4.8,
                        goodsMainImage: 'xxxxx'
                    },
                ]
            },
        ]
    }
},

2. 实现左联动右

使用better-scroll中的scrollToElement方法。

说明:

  1. scrollToElement(el, time, offsetX, offsetY, easing)

    滚动到某个元素,el(必填)表示 dom 元素,time 表示动画时间,offsetXoffsetY 表示坐标偏移量,easing 表示缓动函数

  2. scroll: 滚动时触发

  3. scrollEnd:滚动结束时触发

  4. destroy():销毁 better-scroll,解绑事件


  • 目标元素区域上添加ref属性标识。【右侧商品列表中标识ref="good"
<!-- 右侧商品列表 -->
<section class="right_list fixeds" ref="r_list">
    <div>
        <div class='listsWrap'>
            <ul class="goods-list" v-for="(outerItem, outerIdx) in categoryList" :key="outerIdx" ref="good">
                <li class="goods-li bd" v-for="(item, index) in outerItem.goods" :key="index">
                    <!-- 自定义内容 -->
                </li>
            </ul>
        </div>
    </div>
</section>
  • 在左边菜单中添加点击事件,滑动到右侧对应的位置。【这里定义changeMenu事件】
<aside class="fixeds" ref="l_list">
    <ul class="left-menu">
        <li ref="l_item" :class="{'active': index === currentIndex}" 
            @click="changeMenu(index, item.name)" v-for="(item, index) in categoryList" :key="index">
            <div class="cname" ref="cname">{{item.name}}</div>
        </li>
    </ul>
</aside>
changeMenu (index, name) {
    this.currentIndex = index; //当前选中index
    this.fixedTitle = name; //当前选中标题
    
	//【-32 标题高度】 根据自身调整,如果不需要写0即可。
    this.rightList.scrollToElement(this.$refs.good[index], 1000, 0, -32);
},
  • 初始化better-scroll中获取左菜单定义的ref属性,并开启点击事件
 this.left = new BScroll(this.$refs.l_list, {
     click: true, //是否开启点击事件
     probeType: 3, //是否会截流scroll事件
 })

3. 实现右联动左

  • 监听右侧滚动时触发的距离,可通过scroll事件来监听当前值。

这里右侧数据格式: 每个分类下有n个商品【即嵌套列表】

this.rightList = new BScroll(this.$refs.r_list, {
    probeType: 3, // 是否会截流scroll事件
    scrollY: true,  // 是否开启Y轴滚动方向
    click: true, // 是否开启点击事件
    useTransition: false, // 防止iphone微信滑动卡顿
    bounce: true, // 是否启用回弹动画效果
    momentumLimitDistance: 5 // 符合惯性拖动的最小拖动距离
})

this.rightList.on('scroll', (res) => {
    // 获取当前滚动距离
    this.scrollY = Math.abs(Math.round(res.y));
})

  • 定义数组,用来存储每个分类下的商品列表离顶部的距离
this.$refs.good.forEach((el, index) => {
    this.heightList.push(el.offsetHeight + this.heightList[index]);
})
  • 拿到content滚动距离和每个子商品列表离顶部距离比较。更新左菜单index选中状态

遍历每个分类下的商品列表距离列表

for (let i = 0; i < this.heightList.length; i++) {
    if (this.scrollY > this.heightList[i] && this.scrollY < this.heightList[i + 1]) {
        if (!this.flag) {
            this.currentIndex = i;
        }
    }
}

完整demo

请下载

完整js

<script>
import BScroll from "better-scroll";

export default {
    data() {
        return {
            currentIndex: 0, // 当前选中index
            scrollY: 0,
            heightList: [0], // 存储某个分类下的商品高度列表
            flag: false, // 解决是否选中当前分类index
            fixedTitle: '', // 当前分类标题
            tagList: [{"type": 1, "name": "全部"}, {"type": 2, "name": "热销"}], //分类列表
            tagIndex: 0, //分类tag当前index
            type: 1, // 1:全部 2:热销
            categoryList: [ // 数据仅供测试,请自行copy多些来看效果
                {   
                    categoryId: 1,
                    name: "健康蔬菜",
                    goods: [
                        {
                            goodsName: '大个小番茄',
                            goodsSummary: '绿色小番茄',
                            goodsStock: 231,
                            goodsRetailPrice: 5.4,
                            goodsMarketPrice: 6.8,
                            goodsMainImage: 'xxxxx'
                        },
                        {
                            goodsName: '新鲜小奶白菜',
                            goodsSummary: '清脆、鲜嫩蔬菜',
                            goodsStock: 134,
                            goodsRetailPrice: 3.8,
                            goodsMarketPrice: 5.5,
                            goodsMainImage: 'xxxxx'
                        },
                    ]
                },
				{   
                    categoryId: 2,
                    name: "时令蔬菜",
                    goods: [
                        {
                            goodsName: '大又甜地瓜',
                            goodsSummary: '自家种植放心品尝',
                            goodsStock: 344,
                            goodsRetailPrice: 6.5,
                            goodsMarketPrice: 8.4,
                            goodsMainImage: 'xxxxx'
                        },
                        {
                            goodsName: '精美套餐',
                            goodsSummary: '健康搭配新鲜蔬菜',
                            goodsStock: 1099,
                            goodsRetailPrice: 45.9,
                            goodsMarketPrice: 65.8,
                            goodsMainImage: 'xxxxx'
                        },
                        {
                            goodsName: '水果黄瓜',
                            goodsSummary: '可口脆嫩水果黄瓜',
                            goodsStock: 785,
                            goodsRetailPrice: 3.5,
                            goodsMarketPrice: 4.8,
                            goodsMainImage: 'xxxxx'
                        },
                    ]
                },
            ]
        }
    },
    mounted() {
        this.fixedTitle = this.categoryList[0].name;
		
		
        this.$nextTick(() => {
			// 初始化better-scroll
            this.scrollInit(); 
			
			// 获取某个分类下商品列表离顶部距离
            this.getCategoryListHeight();
        })
    },
    methods: {
        toCommTap(url) {
            this.$router.push(url);
        },
        changeMenu (index, name) {
            this.flag = true;
            this.currentIndex = index;
            this.fixedTitle = name;
			
			//【-32 标题高度】 根据自身调整,如果不需要写0即可。
            this.rightList.scrollToElement(this.$refs.good[index], 1000, 0, -32);
        },
        chageTag(index, type) {
            this.tagIndex = index;
            this.type = type;
            console.log('切换标签');
        },
        /** 
         * 初始化
        */
        scrollInit() {
            this.left = new BScroll(this.$refs.l_list, {
                click: true,
                probeType: 3,
            })
            
            this.rightList = new BScroll(this.$refs.r_list, {
                probeType: 3, // 是否会截流scroll事件
                scrollY: true,  // 是否开启Y轴滚动方向
                click: true, // 是否开启点击事件
                useTransition: false, // 防止iphone微信滑动卡顿
                bounce: true, // 是否启用回弹动画效果
                momentumLimitDistance: 5 // 符合惯性拖动的最小拖动距离
            })

            this.rightList.on('scroll', (res) => {
                this.scrollY = Math.abs(Math.round(res.y));
                for (let i = 0; i < this.heightList.length; i++) {
                    if (this.scrollY > this.heightList[i] && this.scrollY < this.heightList[i + 1]) {
                        if (!this.flag) {
                            this.currentIndex = i;
                        }
                        // 当滚动到倒数第2个位置时左侧列表向上滚动一个距离
                        if (i === this.$refs.l_item.length - 2) {
                            this.left.scrollToElement(this.$refs.l_item[1], 1000, 0, 0)
                        }
                        // 当滚动到倒数第3个位置时左侧列表向上下滚动一个距离
                        if (i === 2) {
                            this.left.scrollToElement(this.$refs.l_item[0], 1000, 0, 0)
                        }
						
                        // 获取当前分类标题
                        this.fixedTitle = this.$refs.cname[i].innerText;
                    }
                }
            })

            this.rightList.on("scrollEnd", pos => {
                //结束时触发事件获取一次位置,因为使用了模式2,惯性滚动不触发事件
                this.scrollY = Math.abs(Math.round(pos.y));
                this.flag = false;
            })
        },
        /**
         * 获取商品列表高度
         */
        getCategoryListHeight() {
            this.$refs.good.forEach((el, index) => {
                this.heightList.push(el.offsetHeight + this.heightList[index]);
            })
        },
        addToShopCart(item) {
            // 添加购物车事件
        },
    }
}
</script>

  • 8
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
Vue实现列表商品双向联动效果,可以考虑如下步骤: 1. 定义商品列表组件和商品详情组件 首先需要定义左侧商品列表组件和右侧商品详情组件,可以分别使用 `<template>`、`<script>` 和 `<style>` 标签定义组件模板、脚本和样式。 2. 在商品列表组件中绑定 click 事件 在商品列表组件中,需要为每个商品元素绑定 click 事件,当用户点击某个商品时触发事件。事件处理函数中可以通过 `this.$emit` 方法发送事件通知,将当前商品信息传递给父组件。 3. 在商品详情组件中监听事件 在商品详情组件中,需要使用 `props` 属性定义父组件传递过来的商品信息,并监听父组件发送的事件。当事件发生时,可以根据事件参数更新商品详情区域的内容。 4. 在父组件中使用子组件 在父组件中,需要引入商品列表组件和商品详情组件,并在模板中使用它们。同时需要定义一个 `selectedItem` 数据,用于存储当前选中的商品信息。当商品列表组件发送事件通知时,更新 `selectedItem` 数据;当商品详情组件接收到事件时,根据事件参数更新 `selectedItem` 数据和右侧商品详情区域的内容。 下面是一个简单的示例代码,用于演示 Vue实现列表商品双向联动效果的方法: ```html <!-- 商品列表组件 --> <template> <div> <div v-for="item in items" :key="item.id" @click="handleClick(item)"> {{ item.name }} </div> </div> </template> <script> export default { props: { items: Array }, methods: { handleClick(item) { this.$emit('item-click', item) } } } </script> <!-- 商品详情组件 --> <template> <div> <div v-if="selectedItem"> <h2>{{ selectedItem.name }}</h2> <p>{{ selectedItem.description }}</p> </div> </div> </template> <script> export default { props: { selectedItem: Object }, mounted() { this.$parent.$on('item-click', this.handleItemClick) }, methods: { handleItemClick(item) { this.$emit('item-selected', item) } } } </script> <!-- 父组件 --> <template> <div> <div> <item-list :items="items" @item-click="handleItemClick"></item-list> <item-detail :selected-item="selectedItem" @item-selected="handleItemSelected"></item-detail> </div> </div> </template> <script> import ItemList from './ItemList.vue' import ItemDetail from './ItemDetail.vue' export default { components: { ItemList, ItemDetail }, data() { return { items: [ { id: 1, name: '商品1', description: '这是商品1的描述信息' }, { id: 2, name: '商品2', description: '这是商品2的描述信息' }, { id: 3, name: '商品3', description: '这是商品3的描述信息' } ], selectedItem: null } }, methods: { handleItemClick(item) { this.selectedItem = item }, handleItemSelected(item) { this.selectedItem = item } } } </script> ``` 在上面的示例代码中,我们定义了 `ItemList` 和 `ItemDetail` 两个组件,分别用于展示商品列表商品详情。在父组件中,我们将这两个组件引入,并定义了 `items` 和 `selectedItem` 两个数据,用于存储所有商品和当前选中的商品。在 `ItemList` 组件中,我们为每个商品元素绑定了 click 事件,并通过 `this.$emit` 方法发送了一个 `item-click` 事件,将当前商品信息传递给父组件。在 `ItemDetail` 组件中,我们通过 `props` 属性定义了 `selectedItem` 数据,并在 `mounted` 钩子函数中监听了 `item-click` 事件,当事件发生时,通过 `this.$emit` 方法发送了一个 `item-selected` 事件,将当前商品信息传递给父组件。在父组件中,我们通过 `handleItemClick` 和 `handleItemSelected` 两个方法来更新 `selectedItem` 数据,并将其传递给 `ItemList` 和 `ItemDetail` 两个子组件,实现列表商品双向联动效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值