美文网首页
vue-滚动条

vue-滚动条

作者: 青青草原灰太狼 | 来源:发表于2019-08-15 20:37 被阅读0次

    功能简介

    功能比较简陋,仅实现了主要功能,水平滚动条,垂直滚动条,容器高宽改变时动态更改滚动条的大小,自定义颜色,方位等功能可通过添加配置参数实现

    一、需要安装 “element-resize-detector”

    npm install element-resize-detector
    

    用于监听div宽高变化

    二、创建工具类 util.js ,方便事件的绑定以及移除

    /**
     * 绑定事件
     *
     * @export
     * @param {any} dom
     * @param {any} eventType
     * @param {any} callback
     */
    export function on (dom, eventType, callback) {
        if (document.addEventListener) {
          dom.addEventListener(eventType, callback)
        } else {
          dom.attachEvent('on' + eventType, callback)
        }
      }
      
      /**
      * 解绑事件
      *
      * @export
      * @param {any} dom
      * @param {any} eventType
      * @param {any} callback
      */
      export function off (dom, eventType, callback) {
        if (document.removeEventListener) {
          dom.removeEventListener(eventType, callback)
        } else {
          dom.detachEvent('on' + eventType, callback)
        }
      }
    

    三、创建容器 scroll.vue

    <template>
        <div class="scroll" ref="scroll">
            <div class="scroll-container" ref="container">
                <div
                        class="scroll-content"
                        ref="content"
                        :style="{'width':'calc(100% + '+this.vSize+'px','height':'calc(100% + '+this.hSize+'px'}"
                        @scroll.stop="onScroll"
                >
                    <slot></slot>
                </div>
            </div>
            <!--垂直滚动条-->
            <scroll-slider
                    v-if="!hideV"
                    :scrollTop="scrollTop"
                    ref="sliderV"
                    @change="silderVChange"
            ></scroll-slider>
            <!--水平滚动条-->
            <scroll-slider
                    v-if="!hideH"
                    sliderType="h"
                    :scrollLeft="scrollLeft"
                    ref="sliderH"
                    @change="silderHChange"
            ></scroll-slider>
        </div>
    </template>
    <script>
        import slider from "./slider.vue";
        import ElementResizeDetectorMaker from "element-resize-detector";
    
        export default {
            name: "scroll",
            props: {
                //隐藏垂直滚动条
                hideV: {
                    type: Boolean,
                    default: false
                },
                //隐藏水平滚动条
                hideH: {
                    type: Boolean,
                    default: false
                },
                resize: Boolean
            },
            components: {
                "scroll-slider": slider
            },
            data() {
                return {
                    //垂直滚动条宽度
                    vSize: 17,
                    //水平滚动条高度
                    hSize: 17,
                    scrollTop: 0,
                    scrollLeft: 0,
                };
            },
            mounted() {
                this.computeSliderV();
                this.computeSliderH();
                // 监听slot视图变化, 方法内部会判断是否设置了开启监听resize
                this.resizeListener()
            },
            methods: {
                onScroll(event) {
                    this.scrollTop = this.$refs["content"].scrollTop;
                    this.scrollLeft = this.$refs["content"].scrollLeft;
                },
                silderVChange(newval) {
                    this.$refs["content"].scrollTop = newval;
                },
                silderHChange(newval) {
                    this.$refs["content"].scrollLeft = newval;
                },
                //计算垂直滚动条的长度
                computeSliderV() {
                    if (this.hideV) {
                        return;
                    }
                    let clientEle = this.$refs["scroll"];
                    let slotEle = this.$slots.default[0]["elm"];
                    this.$refs.sliderV.computeSlider(slotEle,clientEle);
                },
                computeSliderH() {
                    if (this.hideH) {
                        return;
                    }
                    let clientEle = this.$refs["scroll"];
                    let slotEle = this.$slots.default[0]["elm"];
                    this.$refs.sliderH.computeSlider(slotEle,clientEle);
                },
    
                // slot视图大小变化时的监听
                resizeListener() {
                    // 没开启监听reszie方法
                    if (!this.resize) return
    
                    // 监听slot视图元素resize
                    let elementResizeDetector = ElementResizeDetectorMaker({strategy: 'scroll',callOnAdd: false})
    
                    // 记录视图上次宽高的变化
                    const ele = this.$refs.scroll;
                    elementResizeDetector.listenTo(ele, (element) => {
                        // 初始化百分比
                        this.computeSliderV();
                        this.computeSliderH();
                        this.scrollTop = this.$refs["content"].scrollTop;
                        this.scrollLeft = this.$refs["content"].scrollLeft;
                    })
                },
            }
        };
    </script>
    <style>
        .scroll {
            position: relative;
            width: 100px;
            height: 100px;
            overflow: hidden;
        }
    
        .scroll-container {
            width: 100%;
            height: 100%;
        }
    
        .scroll-content {
            width: calc(100% + 17px);
            height: calc(100% + 17px);
            overflow: scroll;
        }
    
        .sliderContainer {
            position: absolute;
            border-radius: 5px;
        }
    
        .scroll-v {
            top: 0;
            bottom: 0;
            right: auto;
            width: 8px;
            height: 100%;
        }
    
        .scroll-h {
            left: 0;
            bottom: auto;
            right: 0;
            height: 8px;
            width: 100%;
        }
    
        .scroll-slider {
            position: absolute;
            background-color: #000000;
            opacity: 0.3;
            border-radius: 5px;
            cursor: pointer;
            transition: opacity .3s;;
        }
    
        .scroll-slider:hover {
            opacity: 0.5;
        }
    
        .scroll-v .scroll-slider {
            top: 0;
            right: 0;
            left: auto;
            bottom: auto;
        }
    
        .scroll-h .scroll-slider {
            top: auto;
            right: auto;
            left: 0;
            bottom: 0;
        }
    </style>
    
    

    四、创建滑块 slider.vue

    <template>
        <div
                :style="[isShow]"
                class="sliderContainer"
                :class="sliderType=='h'?'scroll-h':'scroll-v'"
                ref="sliderContainer"
                @wheel.capture.stop="handlewheel"
        >
            <div
                    class="scroll-slider"
                    :style="[initSize,initLength,loc]"
                    @mousedown.stop="sliderMousedown"
            ></div>
        </div>
    </template>
    <script>
        import {on, off} from "./util";
    
        export default {
            props: {
                //滚动条类型:h:水平滚动条,v:垂直滚动条
                sliderType: {
                    type: String,
                    default: "v"
                },
                //滚动条最小长度
                minLength: {
                    type: Number,
                    default: 20
                },
                size: {
                    type: Number,
                    default: 8
                },
                scrollTop: {
                    type: Number,
                    default: 0
                },
                scrollLeft: {
                    type: Number,
                    default: 0
                },
                color: {
                    type: String,
                    default: "#000000"
                },
                opacity: {
                    type: Number,
                    default: 0.3
                }
            },
            created() {
                //水平滚动条配置
                let h = {
                    clientSize: "clientWidth",
                    loc: "top",
                    scrollSize: "scrollWidth",
                    locsSize: "scrollHeight",
                    loccSize: "clientHeight"
                };
                let v = {
                    clientSize: "clientHeight",
                    loc: "left",
                    scrollSize: "scrollHeight",
                    locsSize: "scrollWidth",
                    loccSize: "clientWidth"
                };
                this.config = this.sliderType === "h" ? h : v;
            },
            data() {
                return {
                    isBind: false,//标识是否已经绑定拖动事件,避免重复绑定
                    percentage: 0,
                    length: 0,
                    startMove: false,
                    offset: 0,
                    position: {},
                    config: {},
                    isShowSilider: true,
                    locValue: 0
                };
            },
            computed: {
                initSize() {
                    return {
                        [this.sliderType === "h" ? "height" : "width"]: this.size + "px"
                    };
                },
                //初始长度
                initLength() {
                    return {
                        [this.sliderType === "h" ? "width" : "height"]: this.length + "px"
                    };
                },
                //初始位置
                loc() {
                    return {
                        [this.sliderType === "h" ? "left" : "top"]: this.offset + "px"
                    };
                },
                //是否显示,隐藏即放到视野外
                isShow() {
                    return {
                        [this.config.loc]: this.isShowSilider ? (this.locValue - this.size) + 'px' : '-' + this.siz + 'px'
                    }
                }
            },
            methods: {
                sliderMousedown(event) {
                    // 只有鼠标左键可以拖动
                    if (event.button !== 0) {
                        return;
                    }
                    event.preventDefault();
                    event.stopPropagation();
                    event.stopImmediatePropagation();
    
                    this.startMove = true;
                    this.position.x = event.clientX;
                    this.position.y = event.clientY;
                    this.bindEvent();
                },
                bindEvent() {
                    if (this.isBind) {
                        return;
                    }
                    on(document, "mouseup", this.mouseup);
                    on(document, "mousemove", this.mousemove);
                    this.isBind = true;
                    this.startMove = true;
                },
                mouseup() {
                    this.startMove = false;
                    this.isBind = false;
                    off(document, "mouseup", this.mouseup);
                    off(document, "mousemove", this.mousemove);
                },
                mousemove(event) {
                    if (!this.startMove) return;
                    let x = event.clientX;
                    let y = event.clientY;
                    let moveX = x - this.position.x;
                    let moveY = y - this.position.y;
                    this.position.x = x;
                    this.position.y = y;
                    let move = this.sliderType === "h" ? moveX : moveY;
                    this.computePosition(move);
                },
                computePosition(move) {
                    let newloc = this.offset + move;
                    if (newloc > this.maxOffset) {
                        newloc = this.maxOffset;
                    }
                    if (newloc < 0) newloc = 0;
                    this.offset = newloc;
                    this.$emit("change", newloc / this.percentage);
                },
                //计算滑块的一些属性
                computeSlider(scroll, client) {
                    //内容真实长度或宽度
                    let scrollSize = scroll[this.config.scrollSize];
                    //容器的长度或宽度
                    let clientSize = client[this.config.clientSize];
                    let containerSize = this.$refs.sliderContainer[this.config.clientSize];
                    this.length = containerSize * (clientSize / scrollSize);
                    if (this.length < this.minLength) {
                        this.length = this.minLength;
                    }
                    if (clientSize >= scrollSize) {
                        this.length = 0;
                    }
                    //最大偏移距离
                    this.maxOffset = containerSize - this.length;
                    if(scrollSize - clientSize==0){
                        this.percentage=0;
                    }else{
                        this.percentage = this.maxOffset / (scrollSize - clientSize);
                    }
                    this.isShowSilider = this.percentage > 0 && this.percentage < 1;
                    let locsSize = scroll[this.config.locsSize];
                    let loccSize = client[this.config.loccSize];
                    //防止内容宽度比容器小的时候垂直滚动条和内容有距离
                    this.locValue = locsSize < loccSize ? locsSize : loccSize;
                },
                handlewheel(event) {
                    console.log(event);
                }
            },
            watch: {
                scrollTop() {
                    this.offset = this.scrollTop * this.percentage;
                },
                scrollLeft() {
                    this.offset = this.scrollLeft * this.percentage;
                }
            },
            destroyed() {
                off(document, "mouseup", this.mouseup);
                off(document, "mousemove", this.mousemove);
            }
        };
    </script>
    

    五、demo

    <template>
        <div>
            <scroll :style="{width:width+'px',height:height+'px'}" resize>
                <div style="width: 100%;height: 100%;">
                    <div>水龙吟·次韵章质夫杨花词</div>
                    <div>似花还似非花,也无人惜从教坠。</div>
                    <div>抛家傍路,思量却是,无情有思。</div>
                    <div>萦损柔肠,困酣娇眼,欲开还闭。</div>
                    <div>梦随风万里,寻郎去处,又还被、莺呼起。</div>
                    <div>不恨此花飞尽,恨西园、落红难缀。晓来雨过,遗踪何在?</div>
                    <div>一池萍碎。</div>
                    <div>春色三分,二分尘土,一分流水。细看来,不是杨花,点点是离人泪。</div>
                </div>
            </scroll>
            <div>
                <span>scroll宽度:</span><input type="text" v-model="width"/>
                <span>scroll高度:</span><input type="text" v-model="height"/>
            </div>
        </div>
    </template>
    <script>
        import scroll from "./component/scroll.vue";
    
        export default {
            components: {
                scroll
            },
            data() {
                return {
                    width: 300,
                    height: 200
                }
            }
        };
    </script>
    

    相关文章

      网友评论

          本文标题:vue-滚动条

          本文链接:https://www.haomeiwen.com/subject/kdptsctx.html