marqueeCom.vue 10.7 KB
<template>
    <div class="marquee-com" v-if="lists && lists.length">
        <div class="box" ref="marqueeBox" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">

            <!-- 判断是否有Clone数组 -->
            <template v-if="listsClone && listsClone.length">

                <!-- 循环输出列表 -->
                <div class="item" ref="items" v-for="(item, index) in listsClone" :key="index">

                    <!--
                        com=1:goodsItem_1组件,pid:1、3,固定不滚动;2、4,滚动
                            事件:
                                @jump:整个点击跳转事件
                                @addCart:右下角加入购物车按钮事件,已阻止事件冒泡

                        com=2:goodsItem_2组件,pid: 1,带标题,不显示附加信息;2、不带标题,显示附加信息
                            事件:
                                @jump:整个点击跳转事件

                        com=3:goodsItem_3组件,无pid参数
                            事件:
                                @jump:整个点击跳转事件
                                @primary:右下角红色主按钮点击事件,已阻止事件冒泡

                        com=10:goodsItem_10组件,pid: 1,价格,原价;2、积分信息
                            事件:
                                @jump:整个点击跳转事件
                        
                        -->

                    <!-- 商品展示样式 1 -->
                    <goods-item-1 v-if="com == '1'" @jump="jump" @addCart="addCart"
                        :item="item"
                        :pid="pid">
                    </goods-item-1>

                    <!-- 商品展示样式 2 -->
                    <goods-item-2 v-if="com == '2'" @jump="jump"
                        :item="item"
                        :pid="pid">
                    </goods-item-2>

                    <!-- 商品展示样式 3 -->
                    <goods-item-3 v-if="com == '3'" @jump="jump" @primary="primary"
                        :item="item">
                    </goods-item-3>

                    <!-- 商品展示样式 10 -->
                    <goods-item-10 v-if="com == '10'" @jump="jump"
                        :item="item"
                        :pid="pid">
                    </goods-item-10>
                </div>
            </template>
        </div>
    </div>
</template>

<script>

    // 引入组件
	import goodsItem1 from "@/components/goodsItem_1/goodsItem_1";
	import goodsItem2 from "@/components/goodsItem_2/goodsItem_2";
	import goodsItem3 from "@/components/goodsItem_3/goodsItem_3";
	import goodsItem10 from '@/components/goodsItem_10/goodsItem_10';

    export default {
        name : 'marqueeCom',
        props: ['lists', 'com', 'pid', 'options'],
        data () {
            return {
                control: {
                    distance: 0,    // 位移量
                    sub: null,      // 追加第几个数组数据下标
                    timer: 30,      // 检查配置选项,设置轮训时间
                    interval: null, // 轮训函数对象id
                    itemWidth: 0    // 滚动项目item宽度
                },
                targetTouches: {
                    ox: null,       // 上一次的鼠标X位置
                    nx: null        // 当前的鼠标X位置
                },
                listsClone: []      // lists深度拷贝对象
            }
        },
        components: {
            goodsItem1,
			goodsItem2,
			goodsItem3,
			goodsItem10
        },
        methods: {

            /**
             * 跑马灯无限循环移动函数
             * @param {Object} itemWidth [元素宽度,用户处理添加/删除元素逻辑]
             * @param {Object} marqueeBox [列表元素DOM对象]
             */
            move(itemWidth, marqueeBox) {

                // 检查配置选项,设置轮训时间
                this.$set(this.control, 'timer', (this.options && this.options.timer && this.options.timer > 0) ? this.options.timer : 30);

                // 设置位移
                this.control.interval = setInterval(() => {
                
                    // 计算位移量
                    this.$set(this.control, 'distance', this.control.distance - 1);

                    // 设置移动
                    marqueeBox.style.transform = 'translateX(' + this.control.distance + 'px)';

                    // 判断添加/删除元素
                    if (!(this.control.distance % itemWidth)) {

                        // 判断应该追加第几个数组数据下标
                        if (!this.control.sub) {
                            this.$set(this.control, 'sub', 0);
                        }

                        // 追加数据到listsClone数组
                        this.$set(this, 'listsClone', [...this.listsClone, ...[this.lists[this.control.sub]]]);

                        // 判断数组下标是否越界
                        if (this.control.sub < this.lists.length - 1) {
                            this.$set(this.control, 'sub', this.control.sub + 1);
                        } else {
                            this.$set(this.control, 'sub', 0);
                        }
                    }
                }, this.control.timer);
            },

			/**
			 * 点击跳转
			 * @param {object} item [数据对象]
			 */
			jump(item) {
				this.$emit("jump", item);
			},
			
			/**
			 * 加入购物车
			 * @param {object} item [数据对象]
			 */
			addCart(item) {
				this.$emit("addCart", item);
			},

			/**
			 * 主按钮点击事件
			 * @param {object} item [数据对象]
			 */
			primary(item) {
				this.$emit("primary", item);
			},

            /**
             * touchstart事件
             */
            touchstart(e) {

                // 判断事件类型
                if (e.type === 'touchstart') {
                    
                    // 暂停列表轮训
                    clearInterval(this.control.interval);
                }
            },

            /**
             * touchmove事件
             */
            touchmove(e) {
                
                // 判断事件类型
                if (e.type === 'touchmove') {

                    // 判断是不是第一次touchmove
                    if (this.targetTouches.ox === null && this.targetTouches.nx === null) {

                        // 初始化touchmove事件的X坐标
                        this.$set(this.targetTouches, 'ox', parseInt(e.targetTouches[0].clientX));
                        this.$set(this.targetTouches, 'nx', parseInt(e.targetTouches[0].clientX));
                    } else {

                        // 设置新的nx坐标
                        this.$set(this.targetTouches, 'nx', parseInt(e.targetTouches[0].clientX));

                        // 判断distance要 <= 0
                        if (this.control.distance <= 0) {

                            // 判断列表长度,不能超长
                            if (Math.abs(this.control.distance) < (this.$refs.items[0].clientWidth * (this.$refs.items.length - this.options.minCount))) {

                                // 坐标移动差值
                                let diff = this.targetTouches.nx - this.targetTouches.ox;
                                
                                // 执行向左移动
                                this.$set(this.control, 'distance', this.control.distance + diff);
                            } else {

                                // 修正this.control.distance的位置值
                                this.$set(this.control, 'distance', -(this.$refs.items[0].clientWidth * (this.$refs.items.length - this.options.minCount)) + 1);
                            }

                            // 设置移动
                            this.$refs.marqueeBox.style.transform = 'translateX(' + this.control.distance + 'px)';
                        }

                        // 更新ox坐标,将当前nx坐标赋值给ox坐标,并且清除nx坐标值
                        this.$set(this.targetTouches, 'ox', parseInt(e.targetTouches[0].clientX));
                        this.$set(this.targetTouches, 'nx', null);
                    }
                }
            },

            /**
             * touchend事件
             */
            touchend(e) {

                // 判断事件类型
                if (e.type === 'touchend') {

                    // 设置touchstart初始坐标点
                    this.$set(this.targetTouches, 'ox', null);
                    this.$set(this.targetTouches, 'nx', null);

                    // 暂停列表轮训
                    clearInterval(this.control.interval);

                    // 延时两秒
                    // setTimeout(() => {
                        
                        // 跑马灯无限循环移动函数
                        this.move(this.control.itemWidth, this.$refs.marqueeBox);
                    // }, 2000);
                }
            }
        },
        mounted() {
            
            // 下一周期执行
            this.$nextTick(() => {

                // 深度拷贝数组
                this.$set(this, 'listsClone', this.lists.slice(0)); 

                // 检查配置选项
                let minCount = (this.options && this.options.minCount && this.options.minCount > 0) ? this.options.minCount : 5;

                // 判断列表项目是否 >= 5项,如果不足,不全列表项
                while(true) {

                    if (this.listsClone && this.listsClone.length >= minCount) {
                        break;
                    }

                    // 向尾部追加数据(克隆数据,prop数据不能追加数组)
                    this.$set(this, 'listsClone', [...this.listsClone, ...this.listsClone]);
                }

                // 下一周期执行
                this.$nextTick(() => {

                    // 获取item宽度
                    this.$set(this.control, 'itemWidth', this.$refs.items[0].clientWidth);

                    // 判断是否获取到item宽度
                    if (this.control.itemWidth) {

                        // 延时2秒再开始滚动
                        setTimeout(() => {
                            
                            // 跑马灯无限循环移动函数
                            this.move(this.control.itemWidth, this.$refs.marqueeBox);
                        }, 2000);
                    }
                });
            });
        }
    }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style rel="stylesheet/less" lang='less' scoped>
    @import "marqueeCom";
</style>