resource-tfgoods-list.vue 7.89 KB
<template>
    <resource class="no-padding-right product-list-more" ref="resource">
        <ul class="resource-tf-goods"
            @touchstart="touchStart"
            @touchmove="touchMove"
            @touchend="touchEnd"
            ref="goodsList"
            :style="moreSlider"
            :class="{'less-slider': value.data.list.length <= 3}"
        >
            <li class="product-item" v-for="(item, pi) in value.data.list" :key="pi">
                <product-link :value="item" class="link" :yas="value" :yas-f="index" :yas-i="pi"></product-link>
                <img-format :lazy="lazy" :src="item.default_images" :w="188" :h="250"></img-format>
                <div class="ellipsis">
                    <p class="title" v-if="item.product_name">{{item.product_name}}</p>
                    <p class="price" v-if="item.sales_price">¥{{item.sales_price}}</p>
                </div>
            </li>
        </ul>
        <div class="more" :class="{'show-more': value.data.list.length <= 3}" :style="moreMove" ref="more" v-show="value.data.url" @click="moreAction"></div>
        <a-link ref="linkA" :href="value.data.url"></a-link>
    </resource>
</template>

<script>
import _ from 'lodash';
import Resource from './resource';

export default {
    name: 'ResourceTfGoodsList',
    data() {
        return {
            startX: 0,
            endX: 0,
            moveX: 0,
            disX: 0,
            moreSlider: '',
            moreMove: '',
            itemVisibleStatus: {},
            checkTimer: ''
        };
    },
    props: {
        value: Object,
        lazy: Boolean,
        index: Number
    },
    computed: {
    },
    methods: {
        touchStart(ev) {
            ev = ev || event;

            // tounches类数组,等于1时表示此时有只有一只手指在触摸屏幕
            if (ev.touches.length === 1) {

                // 记录开始位置
                this.startX = ev.touches[0].clientX;
            }
        },
        touchMove(ev) {
            let wd = this.$refs.goodsList.scrollWidth - this.$refs.goodsList.offsetWidth;
            let moreWd = this.$refs.more.offsetWidth;
            let scrollLeft = this.$refs.goodsList.scrollLeft;

            if (this.value.data.list.length <= 3 || !this.value.data.url) {
                return;
            }

            if (ev.touches.length === 1 && scrollLeft >= wd) {

                // 滑动时距离浏览器左侧实时距离
                this.moveX = ev.touches[0].clientX;

                // 起始位置减去 实时的滑动的距离,得到手指实时偏移距离
                this.disX = this.startX - this.moveX;

                // 如果是向右滑动或者不滑动,不改变滑块的位置
                if (this.disX < 0 || this.disX === 0) {
                    this.moreSlider = 'transform:translateX(0px)';
                    this.moreMove = 'transform:translateX(' + moreWd + 'px)';

                // 大于0,表示左滑了,此时滑块开始滑动
                } else if (this.disX > 0) {

                    // 具体滑动距离我取的是 手指偏移距离*5。
                    this.moreSlider = 'transform:translateX(-' + this.disX * 5 + 'px)';
                    this.moreMove = 'transform:translateX(' + moreWd - this.disX + 'px)';

                    // 最大也只能等于按钮宽度
                    if (this.disX * 5 >= moreWd) {
                        this.moreSlider = 'transform:translateX(-' + moreWd + 'px)';
                        this.moreMove = 'transform:translateX(0px)';
                    }
                }
            } else {
                this.moreSlider = 'transform:translateX(0px)';
                this.moreMove = 'transform:translateX(' + moreWd + 'px)';
            }
        },
        touchEnd(ev) {

            // 手动触发父组件曝光检查, touchEnd事件触发后延迟500ms检查
            clearTimeout(this.checkTimer);
            this.checkTimer = setTimeout(() => {
                this.$parent.checkReqFromChild(this.index);
            }, 500);

            let wd = this.$refs.goodsList.scrollWidth - this.$refs.goodsList.offsetWidth;
            let moreWd = this.$refs.more.offsetWidth;
            let scrollLeft = this.$refs.goodsList.scrollLeft;

            if (this.value.data.list.length <= 3 || !this.value.data.url) {
                return;
            }

            if (ev.changedTouches.length === 1 && scrollLeft >= wd) {
                let endX = ev.changedTouches[0].clientX;

                this.disX = this.startX - endX;

                // 如果距离小于按钮一半,强行回到起点
                if ((this.disX * 5) < (moreWd / 2)) {
                    this.moreSlider = 'transform:translateX(0px)';
                    this.moreMove = 'transform:translateX(' + moreWd + 'px)';
                } else {
                    // 大于一半 滑动到最大值
                    this.$refs.linkA.click();
                }
            }
        },
        moreAction() {
            if (this.value.data.list.length > 3) {
                return;
            }

            this.$refs.linkA.click();
        },

        // 用于检测楼层内部item的曝光状态, 暴露给父组件调用
        checkReqFromParent() {
            let rect;
            let isVisible;
            let visible = [];
            let items = this.$el.querySelectorAll('.product-item');

            _.each(items, (item, idx) => {
                rect = item.getBoundingClientRect();
                isVisible = (rect.left > 0 && rect.left < window.screen.width) ||
                    ((rect.left + rect.width) > 0 && (rect.left + rect.width) < window.screen.width);

                if (isVisible && !this.itemVisibleStatus[idx]) {
                    visible.push({
                        I_INDEX: idx + 1,
                        PRD_SKN: this.value.data && this.value.data.list[idx].product_skn
                    });
                }
                this.itemVisibleStatus[idx] = isVisible;
            });

            return visible;
        }
    },
    components: {Resource}
};
</script>

<style lang="scss">
.resource-tf-goods {
    width: 100%;
    overflow-x: scroll;
    -webkit-overflow-scrolling: touch;
    white-space: nowrap;
    overflow-y: hidden;
    display: inline-block;
    transition: 0.5s;
    position: absolute;
    left: 0;
    right: 0;
    z-index: 200;
    background: #fff;
    padding-left: 20px;

    li.product-item {
        position: relative;
        display: inline-block;
        padding-right: 20px;
        text-align: center;
        line-height: 40px;

        .link {
            position: absolute;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
        }

        img {
            width: 188px;
            height: 250px;
            display: block;
        }

        .ellipsis > p {
            text-overflow: ellipsis;
            white-space: nowrap;
            overflow: hidden;
            font-size: 24px;
            max-width: 188px;
            line-height: 28px;
            margin-top: 24px;
            font-family: "SanFranciscoText-Regular";

            &.price {
                font-size: 28px;
                margin-top: 20px;
            }
        }
    }
}

.product-list-more {
    position: relative;
    height: 318px;

    .more {
        width: 113px;
        height: 250px;
        background: url("~statics/img/channel/more@2x.png") no-repeat;
        background-size: contain;
        position: absolute;
        right: 0;
        top: 20px;
        transform: translateX(113px);
        transition: 0.5s;
    }

    .show-more {
        width: 98px;
        position: relative;
        vertical-align: top;
        display: inline-block;
        top: 0;
        right: 0;
        transform: translateX(0);
    }

    .less-slider {
        width: auto;
        position: relative;
        right: auto;
        padding-left: 0;
    }
}

.scroller-box {
    overflow-x: hidden;
}
</style>