奖品列表组件【仿swiper】

  最近lz在做项目的一些优化,发现我的项目里有个奖品列表的功能;我们之前是引入swiper这个库去做的;swiper库的滑动效果确实比较好看,但是js文件以及css文件相对是比较大的;考虑到这个小小的需求而去引入如此大的库,感觉太不值得了;所以自己去封装了一下;

  项目原需求如下图:

  

       封装思路:

  1.我想的是swiper在布局是不是隐藏掉了滚动条?因为在进行左右滑动时候首先想到的是css属性“overflow-x:scroll或者overflow-x:auto”;但是我审查了下元素发现swiper并没有使用这个css属性;经过查看原来使用了translate3d去让其滑动的;于是解决了滑动问题;

  2.思考可视展示问题;我的做法是将整个大的容器除以页面可视容器所要展示的个数得到每个子元素的宽度;然后进行排列;这样布局问题就搞定了;

  3.就是滑动问题,监听容器的touchStart、touchMove、touchEnd事件;先要计算出两边的临界值;左边很好理解就是0,右边稍微计算下把整个子容器宽度和-父级容器宽度=最大滑动距离;这样再做下滑动临界值的处理就好了;

  4.联动问题,滚动条的滑动跟容器内容滑动原理是一样的,稍微动脑下的是联动的一个逻辑。这里有个等式就是容器滑动距离/容器最大滑动距离 = 滚动条滑动距离/滚动条最大滑动距离;这样就能做好相应的联动效果;对于边界值,我们再稍微的优化下;整个功能就差不多实现了;

  封装代码如下:

/**
 * 我们在各个活动工具中要用到swiper去做奖品列表的渲染,由于swiper.js和swiper.css文件较大;所以仿照swiper封装了此组件
 * @param {Object} config 
 * @param {String} containerId:选择器
 * @param {Number} slidesPerView:一屏要展示数
 * @param {Number} spaceBetween:奖品间距 
 * @param {Boolean} scrollbarHide:是否显示滚动条         
 */
import './index.scss';
    class CSlide {
        init(config) {
            let { containerId, slidesPerView, spaceBetween = 20, scrollbarHide } = config;
            this.container = document.querySelector(containerId);
            this.wrapper = this.container.children[0];
            this.store = {
                containerId,
                containerWidth: this.wrapper.offsetWidth,
                children: this.wrapper.children,
                slidesPerView,
                spaceBetween,
                calculateSlideX: 0,
                scrollbarHide
            };
            this.state = {
                startX: 0,
                diffX: 0,
                touchStart: false,
                touchEnd: false,
                touchMove: false,
                translateX: 0,
                transitionDuring: 300,
                ifanimateEnd: false
            };
            this.buildSwiper();
        }
        bind() {
            let events = ['touchstart', 'touchmove', 'touchend'];
            this.addEvent(this.container, events[0], this.touchStart);
            this.addEvent(this.container, events[1], this.touchMove);
            this.addEvent(this.container, events[2], this.touchEnd);
        }
        //初始化内容
        buildSwiper() {
            let {
                containerWidth,
                children,
                slidesPerView,
                spaceBetween
            } = this.store;
            let slideWidth = Math.round(containerWidth / slidesPerView);
            for (let i = 0; i < children.length; i++) {
                children[i].style.width = slideWidth + 'px';
                children[i].style.marginRight = spaceBetween + 'px';
            }
            //是否有滚动条
            if (this.store.scrollbarHide) {
                $(this.store.containerId).append(` <div class="swiper-scrollbar">
                <div class="swiper-scrollbar-drag"></div>
            </div>`);
                this.srollbarContainer = document.querySelector('.swiper-scrollbar-drag');
                this.scrollbar = document.querySelector('.swiper-scrollbar');
                this.store.scrollbarWidth = this.scrollbar.offsetWidth;
                this.store.scrollbarDragWidth = this.srollbarContainer.offsetWidth;
                setTimeout(() => {
                    this.store.ableslideX = Number(this.scrollbar.offsetWidth - this.srollbarContainer.offsetWidth);
                }, 10);
            }
            this.bind();
        }
        //监听touchStart事件
        touchStart(e, that) {
            if (that.state.touchStart) return;
            that.state.startX = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
            that.state.touchEnd = that.state.touchMove = false;
            that.state.touchStart = true;
            that.state.diffX = 0;
        }
        //监听touchMove事件
        touchMove(e, that) {
            let { startX } = that.state;
            if (!that.state.touchStart) return;
            that.state.touchMove = true;
            let currentX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
            if (!that.state.ifanimateEnd) {
                that.state.diffX = Math.round(currentX - startX);
                that.targetMaxmove();
                if(that.store.scrollbarHide){
                    that.scrollbarMove();
                }
                
            }
        }
        //监听touchEnd事件
        touchEnd(e, that) {
            let {
                touchStart,
                touchMove,
                touchEnd,
            } = that.state;
            if (!touchStart || !touchMove || touchEnd) return;
            that.state.touchEnd = true;
            that.state.touchStart = false;
            that.state.ifanimateEnd = false;
            that.ifdeadLine();
            if(that.store.scrollbarHide){
                that.scrollbarInit();
            }
            setTimeout(() => {
                that.transitionDurationEndFn();
            }, that.state.transitionDuring);
        }
        //返回滑动区域的最大值
        calculateSlide() {
            let {
                children,
                spaceBetween,
                containerWidth
            } = this.store;
            let slide_max = 0,
                totalWidth = 0;
            for (let i = 0; i < children.length; i++) {
                totalWidth += children[0].offsetWidth + spaceBetween;
            }
            slide_max = containerWidth + spaceBetween - totalWidth;
            return slide_max;
        }
        //滑动的临界值判断
        targetMaxmove() {
            let {
                diffX
            } = this.state;
            let currentSlide = this.state.diffX + this.getTranslate(this.wrapper).x;
            let rightLine = this.calculateSlide();
            if (diffX > 0 && currentSlide > this.store.containerWidth / 2) {
                this.state.ifanimateEnd = true;
                this.state.translateX = this.store.containerWidth / 2;
            } else if (diffX < 0 && currentSlide <= rightLine - this.store.containerWidth / 2) {
                this.state.ifanimateEnd = true;
                this.state.translateX = rightLine - this.store.containerWidth / 2;
            } else {
                this.state.translateX = currentSlide;
            }
            this.recover(this.wrapper, Number(this.state.translateX), 0, 0);
        }
        //结束时临界值处理
        ifdeadLine() {
            let {
                diffX,
                translateX
            } = this.state;
            let rightLine = this.calculateSlide();
            if (diffX > 0) {
                this.recover(this.wrapper, 0, 0, 0);
            }
            if (diffX < 0 && translateX <= rightLine) {
                this.recover(this.wrapper, rightLine, 0, 0);
            }
        }
        //滚动条滑动处理
        scrollbarMove() {
            let { diffX } = this.state;
            let { ableslideX } = this.store;
            let rightLine = this.calculateSlide();
            let radio = Math.abs(diffX) / Math.abs(rightLine);
            let scrollX = diffX > 0 ? -radio * ableslideX : radio * ableslideX;
            scrollX += this.getTranslate(this.srollbarContainer).x;
            this.recover(this.srollbarContainer, scrollX, 0, 0);
        }
        //结束滚动条处理
        scrollbarInit() {
            let { ableslideX } = this.store;
            let { diffX } = this.state;
            let scrollX = this.getTranslate(this.srollbarContainer).x;
            if (diffX > 0) {
                this.recover(this.srollbarContainer, 0, 0, 0);
            }
            if (diffX < 0 && scrollX > ableslideX) {
                this.recover(this.srollbarContainer, ableslideX, 0, 0);
            }
        }
        recover(container, x, y, z) {
            this.transitionDuration(container, this.state.transitionDuring);
            this.translate(container, x, y, z);
        }
        translate(ele, x, y, z) {
            this.transform(ele, 'translate3d(' + x + 'px, ' + y + 'px, ' + z + 'px)');
        }

        transform(ele, transform) {
            let elStyle = ele.style;
            elStyle.webkitTransform = elStyle.MsTransform = elStyle.msTransform = elStyle.MozTransform = elStyle.OTransform = elStyle.transform = transform;
        }

        transitionDuration(ele, time) {
            let elStyle = ele.style;
            elStyle.webkitTransitionDuration = elStyle.MsTransitionDuration = elStyle.msTransitionDuration = elStyle.MozTransitionDuration = elStyle.OTransitionDuration = elStyle.transitionDuration = time + 'ms';
        }

        transitionDurationEndFn() {
            this.transitionDuration(this.wrapper, 0);
        }
        getTranslate(el) {
            let curStyle = window.getComputedStyle(el);
            let curTransform = curStyle.transform || curStyle.webkitTransform;
            let x, y;
            x = y = 0;
            curTransform = curTransform.split(', ');
            if (curTransform.length === 6) {
                x = parseInt(curTransform[4], 10);
                y = parseInt(curTransform[5], 10);
            }
            return {
                x,
                y
            };
        }
        addEvent(target, type, fn) {
            $(document).on(type, target, event => {
                if (typeof fn !== 'function') return;
                fn(event, this);
            }).bind(this);
        }
    };

  css代码如下:

.bxm-container {
    width: 100%;
    height: auto;
    position: relative;
    overflow: hidden;
    list-style: none;
    padding: 0;
    .bxm-wrapper {
        position: relative;
        width: 100%;
        height: 100%;
        z-index: 1;
        display: flex;
        transition-property: transform;
        box-sizing: content-box;
        .bxm-slide {
            text-align: center;
            font-size: 18px;
            background: #fff;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-shrink: 0;
            width: 100%;
            height: 100%;
            box-sizing: border-box;
            transition-property: transform;
            -webkit-flex-shrink: 0;
            -ms-flex-negative: 0;
            flex-shrink: 0;
        }
    }
    .swiper-scrollbar {
        position: relative;
        left: 1%;
        bottom: 0px;
        z-index: 50;
        height: 5px;
        width: 98%;
        border-radius: 10px;
        -ms-touch-action: none;
        background: rgba(0, 0, 0, .1);
        margin-top: 10px;
        overflow: hidden;
        .swiper-scrollbar-drag {
            height: 100%;
            width: 60%;
            position: relative;
            background: rgba(0, 0, 0, .5);
            border-radius: 10px;
            left: 0;
            top: 0
        }
    }
}

  具体调用该组件类似于

<div class="bxm-container">
    <div class="bxm-wrapper">
        <div class="bxm-slide">
            <div class="awardImg"><img src="https://buyimg.bianxianmao.com/dist/ACTIVITY/certificate/2018/10/24/04c32df6-13f2-464d-b298-b712e14daa2e"
                    alt=""></div>
        </div>
        <div class="bxm-slide">
            <div class="awardImg"><img src="https://buyimg.bianxianmao.com/dist/ACTIVITY/certificate/2018/10/24/04c32df6-13f2-464d-b298-b712e14daa2e"
                    alt=""></div>
        </div>
        <div class="bxm-slide">
            <div class="awardImg"><img src="https://buyimg.bianxianmao.com/dist/ACTIVITY/certificate/2018/10/24/04c32df6-13f2-464d-b298-b712e14daa2e"
                    alt=""></div>
        </div>
        <div class="bxm-slide">
            <div class="awardImg"><img src="https://buyimg.bianxianmao.com/dist/ACTIVITY/certificate/2018/10/24/04c32df6-13f2-464d-b298-b712e14daa2e"
                    alt=""></div>
        </div>
        <div class="bxm-slide">
            <div class="awardImg"><img src="https://buyimg.bianxianmao.com/dist/ACTIVITY/certificate/2018/10/24/04c32df6-13f2-464d-b298-b712e14daa2e"
                    alt=""></div>
        </div>
        <div class="bxm-slide">
            <div class="awardImg"><img src="https://buyimg.bianxianmao.com/dist/ACTIVITY/certificate/2018/10/24/04c32df6-13f2-464d-b298-b712e14daa2e"
                    alt=""></div>
        </div>
    </div>
<script>

  var config = {
    containerId: ‘.bxm-container’,
    slidesPerView: 2.4,
    spaceBetween: 20,
    scrollbarHide: true,
  };

  new CSlide().init(config, temp);
</script>  

  LZ刚写到快结尾时候被叫到公司去做一个紧急需求,心里一万匹草泥马在奔腾。

  如有不妥,请大佬指正;