vue 插件tab选项卡

基本用法

<template>
    <tab :options="tabOpt" :state.sync="stateIndex">tab>
template>
<script type="text/babel">
    import tab from 'components/tab_touch';
    export default {
        data(){
            tabOpt:undefined,
            stateIndex:0
        },
        components:{
        "tab":tab
        },
        ready(){
            this.tabOpt={
                count: 2.3,
                pin:true,
                htmls:[
                    "白日登山",
                    "望烽火",
                    "黄昏饮马",
                    "傍交河",
                    "行人刁斗",
                    "风沙暗",
                    "公主琵琶",
                    "幽怨多",
                    "野营万里",
                    "无城郭",
                    "雨雪纷纷",
                    "连大漠"
                ],
                slideCallback:function (dex) {
                    console.log(dex);
                },
                tabClassName:"tab",
                tabActiveClassName: "active"
            }
        }
    }
script>

options参数释义

参数名 简称 是否必填 类型 含义 默认值 其它
count 展示tab数 必填项 数值|一屏中露出tab的个数| 无
pin 是否固定到顶部 可选项 布尔 当垂直滚动时,该组件即将超出屏幕时是否固定到头部 false
htmls tab内容 必填项 数组[字符串] 在tab中显示的html
tabClassName tabClassName 可选项 字符串 单个tab的class名 一般用于自定义样式
tabActiveClassName tabActiveClassName 可选项 字符串 激活的tab的class名 一般用于自定义样式

代码

tab.vue

<template>
    <div class="fixWrap">
        <div class="component-tabsWrap" :id="tabsWrapID"
             v-touch:pan="onPan">
            <div class="component-tabs" :style="wrapStyle">
                <div class="component-tab" v-for="item in options.htmls" track-by="$index" :style="'width:'+tWidth+'px'"
                     @click.stop="this.state=$index"
                     :class="[options.tabClassName,$index==state?options.tabActiveClassName:'']">
                    {{{item}}}
                div>
            div>
        div>
    div>
template>
<style lang="sass" rel="stylesheet/sass">
    @import "tab.sass"
style>
<script lang="babel" type="text/babel">
    var VueTouch = require ('vue-touch');
    Vue.use (VueTouch);
    import requestAnimFrame  from "utils/requestAnimFrame"
    const sign = (num)=> {
        return num >= 0 ? 1 : -1
    }
    export default {
        props: ["options", "state"],
        data(){
            return {
                tabsWrapID: undefined,//外容器ID
                wrapWidth: "", //外容器宽度
                tWidth: 0, //每一个选项卡应该有多宽
                width: 0, //宽度。
                startTransX: 0,
                transX: 0, //元素样式偏移。
                cssX: 0 //动作中css实际完成的偏移。
            }
        },
        methods: {
            init(){
                this.wrapWidth = document.getElementById (this.tabsWrapID).offsetWidth;
                this.tWidth = this.wrapWidth / this.options.count;
                this.width = this.tWidth * this.options.htmls.length;
                setTimeout(function(){
                    this.$el.style["height"]= this.$el.children[0].clientHeight+"px";
                }.bind(this),0)
                if (this.options.pin) {
                    var elemRect = this.$el.getBoundingClientRect ();
                    var windowOffset = this.getWindowOffset ();
                    var winOffsetY = windowOffset.offsetY;
                    this.elemOffsetY = elemRect.top + winOffsetY;
                    document.addEventListener ('scroll', this.isTop);
                }
            },
            onPan(event){
                if (this.options.disPan) return;
                this.transX = event.deltaX + this.startTransX;
                this.cssX = this.transX;
                if (event.eventType == 4) {
                    this.transX = -Math.round (-this.transX / this.tWidth) * this.tWidth; //整格滑动
                    var start = null;
                    if (this.transX > 0) {
                        // 头部回弹
                        this.transX = 0;
                        var speed = 24;
                        requestAnimFrame (step.bind (this));
                        function step (timestamp) {
                            this.cssX -= speed;
                            if (this.cssX > this.transX) requestAnimFrame (step.bind (this));
                            else this.startTransX = this.cssX = this.transX;
                        };
                    }
                    else if (this.transX < this.wrapWidth - this.width) {
                        // 尾部回弹
                        this.transX = this.wrapWidth - this.width;
                        var speed = 24;
                        requestAnimFrame (step.bind (this));
                        function step (timestamp) {
                            this.cssX += speed;
                            if (this.cssX < this.transX) requestAnimFrame (step.bind (this));
                            else this.startTransX = this.cssX = this.transX;
                        };
                    }
                    else {
                        //整格落位
                        let speed = 6;
                        let _sign = sign (this.cssX - this.transX);
                        if (_sign * (this.cssX - this.transX) > 1) requestAnimFrame (step.bind (this));
                        else this.cssX = this.transX;
                        function step (timestamp) {
                            if (start === null) start = timestamp;
                            let progress = timestamp - start;
                            if (progress < 3000) speed *= 1 - progress / 3000;
                            this.cssX -= _sign * speed;
                            if (_sign * (this.cssX - this.transX) > 1) requestAnimFrame (step.bind (this));
                            else this.cssX = this.transX;
                        };
                    }
                    this.startTransX = this.transX; //滑动结束设置下次滑动起始值。
                }

            },
            slideTo(dex){
                this.state = dex;
                let speed = 10;
                // 开头几个
                if (dex + 1 <= this.options.count) {
                    this.transX = 0; // 设置应到位置。
                    if (this.startTransX < this.transX) {
                        let _sign = sign (this.transX - this.startTransX);
                        this.cssX = this.transX - _sign * this.tWidth;
                        requestAnimFrame (step.bind (this));
                        function step () {
                            this.cssX += _sign * speed;
                            if (_sign * (this.cssX - this.transX) < 0) requestAnimFrame (step.bind (this));
                            else {
                                this.cssX = this.startTransX = this.transX;
                                if (this.options.slideCallback) this.options.slideCallback (dex);
                            }
                            ;
                        };
                    }
                    //无需动画
                    else {
                        this.cssX = this.startTransX = this.transX;
                        if (this.options.slideCallback) this.options.slideCallback (dex);
                    }
                }
                // 结尾几个
                else if (this.options.htmls.length - dex <= this.options.count) {
                    this.transX = this.wrapWidth - this.width;// 设置应到位置。
                    if (this.startTransX > this.transX) {
                        let _sign = sign (this.transX - this.startTransX);
                        this.cssX = this.transX - _sign * this.tWidth;
                        requestAnimFrame (step.bind (this));
                        function step () {
                            this.cssX += _sign * speed;
                            if (_sign * (this.cssX - this.transX) < 0) requestAnimFrame (step.bind (this));
                            else {
                                this.cssX = this.startTransX = this.transX;
                                if (typeof this.options.slideCallback == "function") this.options.slideCallback (dex);
                            }

                        };
                    }
                    else {
                        this.cssX = this.startTransX = this.transX; //无需动画
                        if (typeof this.options.slideCallback == "function") this.options.slideCallback (dex);
                    }
                }
                //中
                else {
                    this.transX = -this.tWidth * dex;// 设置应到位置。
                    let _sign = sign (this.transX - this.startTransX);
                    if (this.tWidth * (dex + 0.5 * (1 - _sign)) + this.startTransX >= 0 && this.tWidth * (dex + 0.5 * (1 - _sign)) + this.startTransX <= this.wrapWidth) {
                        //无需动画
                        this.cssX = this.transX = this.startTransX;
                        if (typeof this.options.slideCallback == "function") this.options.slideCallback (dex);
                    }
                    else {
                        //需要动画
                        this.cssX = this.transX - _sign * this.tWidth;
                        requestAnimFrame (step.bind (this));
                        function step () {
                            this.cssX += _sign * speed;
                            if (_sign * (this.cssX - this.transX) < 0) requestAnimFrame (step.bind (this));
                            else {
                                this.cssX = this.startTransX = this.transX;
                                if (typeof this.options.slideCallback == "function") this.options.slideCallback (dex);
                            }
                        };
                    }
                }
            },
            isTop(){
                var windowOffset = this.getWindowOffset (),
                        winOffsetY = windowOffset.offsetY,
                        isTop;
                isTop = this.elemOffsetY <= winOffsetY;
                if (isTop) {
                    this.$el.children[0].style['position'] = 'fixed';
                    this.$el.children[0].style['top'] = '0';
                    this.$el.children[0].style['left'] = '0';
                }
                else {
                    this.$el.children[0].style['position']='relative';
                }
                return isTop;
            },
            getWindowOffset(){
                var t;
                var win = window;
                var pageXOffset = (typeof win.pageXOffset == 'number') ? win.pageXOffset : (((t = doc.documentElement) || (t = body.parentNode)) && typeof t.ScrollLeft == 'number' ? t : body).ScrollLeft;
                var pageYOffset = (typeof win.pageYOffset == 'number') ? win.pageYOffset : (((t = doc.documentElement) || (t = body.parentNode)) && typeof t.ScrollTop == 'number' ? t : body).ScrollTop;
                return {
                    offsetX: pageXOffset,
                    offsetY: pageYOffset
                };
            },
        },
        watch: {
            options(){
                this.tabsWrapID = parseInt (Math.random () * 1e10);
                setTimeout (this.init.bind (this), 10);
            },
            state(val){
                this.slideTo(val)
            }
        },
        computed: {
            wrapStyle(){
                var _str = "";
                if (this.width) _str += `width:${this.width}px;`;//宽度
                _str += `-webkit-transform:translateX(${this.cssX}px);` //位移。
                _str += `transform:translateX(${this.cssX}px);` //位移。
                return _str
            }
        }
    }
script>
<style lang="sass" rel="stylesheet/sass" type="sass">
.fixWrap
  z-index: 999
  position: relative
.component-tabsWrap
  width: 100%
  overflow: hidden
  min-height: .1rem
  position: relative
  background: #fff
  .component-tabs
    height: 100%
    display: table
    .component-tab
      display: table-cell
      box-sizing: border-box
      text-align: center
      vertical-align: middle
style>

你可能感兴趣的:(前端)