JavaScript 实现滚动条效果

方文锋  2021-05-20 16:09:03  1606  首页学习JavaScript

最近发现项目可能需要使用到滚动条,浏览器自动的滚动条不美观,所以就想到了使用js实现。

js代码如下:

 function box_scroll() {
    var args = arguments;
    var parent = this;
    /*左右滚动*/
    this.lr = {
        $scrollContainer: null,
        $scrollContent: null,
        $scrollbar: null,
        containerWidth: null,
        scrollWidth: null,
        scrollbarWidth: null,
        scroll: 0,
        /**
         * 鼠标滚轮事件
         * @param e
         * @returns {boolean}
         */
        scrollFunction: function (e) {
            var e = e || window.event;
            var $scrollContainer = this.$scrollContainer,
                $scrollContent = this.$scrollContent,
                $scrollbar = this.$scrollbar,
                containerWidth = this.containerWidth,
                scrollWidth = this.scrollWidth,
                scrollbarWidth = this.scrollbarWidth,
                scroll = this.scroll;
            //判断滚轮方向
            var wheelValue = e.wheelDelta / 120 || -e.detail / 3;
            var dir = wheelValue > 0 ? "up" : "down";
            if (dir === "up") {
                //内容容器右移
                scroll += 80;
            } else {
                //内容容器左移
                scroll -= 80;
            }
            //大于最大滚动的长度(scrollWidth - containerWidth)
            if (Math.abs(scroll) > scrollWidth - containerWidth) {
                //边界判定
                scroll = -scrollWidth + containerWidth;
            }
            if (scroll > 0) {
                //scroll 不能大于0,因为使用的是css的left
                scroll = 0;
            }
            this.scroll = scroll;

            $scrollContent.style.left = scroll + "px";
            //距离比例
            var proportion = Math.abs(scroll) / (scrollWidth - containerWidth);
            //设置滚动条滑块移动的距离
            if ($scrollbar) {
                $scrollbar.style.left = proportion * (containerWidth - scrollbarWidth) + "px";
            }
            e.preventDefault();
            return false;
        },
        /**
         * 左右滚动
         * @param {string|object} scrollContainer 最外层容器
         * @param {string|object} scrollContent 内容容器部分
         * @param {string|object} scrollbar 滚动条盒子
         */
        run: function (scrollContainer, scrollContent, scrollbar) {
            var args = arguments;
            var $scrollContainer, $scrollContent, $scrollbar, reg = {isEle: /^\[object HTML\S+Element\]$/i};
            var self = this;
            //滚动的距离(内容容器 left)
            var scroll = 0;
            if (scrollContainer) {
                (typeof scrollContainer === "string") && ($scrollContainer = document.getElementById(scrollContainer));
                (reg.isEle.test(Object.prototype.toString.call(scrollContainer))) && ($scrollContainer = scrollContainer);
                this.$scrollContainer = $scrollContainer;
            }
            if (scrollContent) {
                (typeof scrollContent === "string") && ($scrollContent = document.getElementById(scrollContent));
                (reg.isEle.test(Object.prototype.toString.call(scrollContent))) && ($scrollContent = scrollContent);
                this.$scrollContent = $scrollContent;
            }
            if (scrollbar) {
                (typeof scrollbar === "string") && ($scrollbar = document.getElementById(scrollbar));
                (reg.isEle.test(Object.prototype.toString.call(scrollbar))) && ($scrollbar = scrollbar);
                this.$scrollbar = $scrollbar;
            }
            //设置最外层容器的样式
            $scrollContainer.style.position = "relative";
            $scrollContainer.style.overflow = "hidden";
            //设置内容容器的样式
            $scrollContent.style.position = "absolute";

            //内容容器的宽
            var scrollWidth = $scrollContent.offsetWidth;
            this.scrollWidth = scrollWidth;
            //最外层容器的宽
            var containerWidth = $scrollContainer.offsetWidth;
            this.containerWidth = containerWidth;
            if ($scrollbar) {
                $scrollbar.style.width = (containerWidth * containerWidth) / scrollWidth + "px";
                //滚动条的宽
                var scrollbarWidth = $scrollbar.offsetWidth;
                this.scrollbarWidth = scrollbarWidth;
                if (scrollbarWidth >= containerWidth) {
                    $scrollbar.style.display = "none";
                }
            }

            //滑块滚动部分
            if ($scrollbar) {
                $scrollbar.style.opacity = 0.7;
                $scrollContainer.onmouseover = function () {
                    $scrollbar.style.opacity = 1;
                };
                $scrollContainer.onmouseout = function () {
                    $scrollbar.style.opacity = 0.7;
                };
                parent.is_mobile() && ($scrollbar.style.opacity = 0);
                $scrollbar.onmousedown = function (e) {
                    var e = e || window.event;
                    var objX = this.offsetLeft;
                    var x = e.clientX;
                    var realX = x - objX;
                    var _this = this;
                    var scrollWidth = $scrollContent.offsetWidth;
                    document.onmousemove = function (e) {
                        var e = e || window.event;
                        var x1 = e.clientX;
                        var realX2 = x1 - realX;
                        if (realX2 < 0) {
                            realX2 = 0;
                        } else if (realX2 > containerWidth - scrollbarWidth) {
                            realX2 = containerWidth - scrollbarWidth;
                        }
                        _this.style.left = realX2 + "px";
                        $scrollContent.style.left = (self.scroll = -realX2 / (containerWidth - scrollbarWidth) * (scrollWidth - containerWidth)) + "px";
                        e.preventDefault();
                        return false;
                    };

                    document.onmouseup = function () {
                        document.onmousemove = null;
                        document.onmouseup = null;
                    };
                };
            }

            this.mousewheel = function (e) {
                self.scrollFunction(e);
            };
            if (document.addEventListener) {
                $scrollContainer.addEventListener('DOMMouseScroll', this.mousewheel, false);
            }
            $scrollContainer.onmousewheel = this.mousewheel;
            //移动端触摸滚动
            (function () {
                var x, y, begin_left;
                $scrollContainer.addEventListener("touchstart", function (e) {
                    var e = e || window.event;
                    var touch = e.touches[0];
                    y = parseInt(touch.clientY);
                    x = parseInt(touch.clientX);
                    begin_left = $scrollContent.offsetLeft;
                    scrollWidth = $scrollContent.offsetWidth;
                }, false);
                $scrollContainer.addEventListener("touchmove", function (e) {
                    var e = e || window.event;
                    e.preventDefault();
                    var touch = e.touches[0];
                    y1 = parseInt(touch.clientY);
                    x1 = parseInt(touch.clientX);
                    $scrollContent.style.left = (self.scroll = begin_left + (x1 - x)) + "px";
                    if ($scrollContent.offsetLeft > 0) {
                        $scrollContent.style.left = 0;
                        self.scroll = 0;
                    } else if ($scrollContent.offsetLeft < -(scrollWidth - containerWidth)) {
                        $scrollContent.style.left = (self.scroll = -(scrollWidth - containerWidth)) + "px";
                    }

                    //距离比例
                    var proportion = Math.abs(self.scroll) / (scrollWidth - containerWidth);
                    //设置滚动条滑块移动的距离
                    if ($scrollbar) {
                        $scrollbar.style.left = proportion * (containerWidth - scrollbarWidth) + "px";
                    }
                    //触摸滑屏的时候显示滚动条
                    $scrollbar.style.opacity = 1;
                }, false);
                $scrollContainer.addEventListener("touchend", function (e) {
                    clearTimeout(self.IDD);
                    self.IDD = setTimeout(function () {
                        //手指离开屏幕的时候隐藏滚动条
                        $scrollbar.style.opacity = 0;
                    }, 800);
                });
            })();

        }

    };
    /*上下滚动*/
    this.tb = {
        $scrollContainer: null,
        $scrollContent: null,
        $scrollbar: null,
        containerHeight: null,
        scrollHeight: null,
        scrollbarHeight: null,
        scroll: 0,
        /**
         * 鼠标滚轮事件
         * @param e
         * @returns {boolean}
         */
        scrollFunction: function (e) {
            var e = e || window.event;
            //判断滚轮方向
            var wheelValue = e.wheelDelta / 120 || -e.detail / 3;
            var dir = wheelValue > 0 ? "up" : "down";
            if (dir === "up") {
                //内容容器下移
                this.scroll += 80;
            } else {
                //内容容器上移
                this.scroll -= 80;
            }
            //大于最大滚动的长度(scrollHeight - containerHeight)
            if (Math.abs(this.scroll) > this.scrollHeight - this.containerHeight) {
                //边界判定
                this.scroll = -this.scrollHeight + this.containerHeight;
            }
            if (this.scroll > 0) {
                //scroll 不能大于0,因为使用的是css的top
                this.scroll = 0;
            }
            this.$scrollContent.style.top = this.scroll + "px";
            //距离比例(滚动的距离与最大滚动距离的比例)
            var proportion = Math.abs(this.scroll) / (this.scrollHeight - this.containerHeight);
            //设置滚动条滑块移动的距离
            if (this.$scrollbar) {
                this.$scrollbar.style.top = proportion * (this.containerHeight - this.scrollbarHeight) + "px";
            }
            e.preventDefault();
            return false;
        },
        /**
         * 上下滚动
         * @param {string|object} scrollContainer 最外层容器
         * @param {string|object} scrollContent 内容容器
         * @param {string|object} scrollbar 滚动条盒子
         */
        run: function (scrollContainer, scrollContent, scrollbar) {
            var args = arguments, reg = {isEle: /^\[object HTML\S+Element\]$/i}, self = this;
            if (scrollContainer) {
                (typeof scrollContainer === "string") && (this.$scrollContainer = document.getElementById(scrollContainer));
                (reg.isEle.test(Object.prototype.toString.call(scrollContainer))) && (this.$scrollContainer = scrollContainer);
            }
            if (scrollContent) {
                (typeof scrollContent === "string") && (this.$scrollContent = document.getElementById(scrollContent));
                (reg.isEle.test(Object.prototype.toString.call(scrollContent))) && (this.$scrollContent = scrollContent);
            }
            if (scrollbar) {
                (typeof scrollbar === "string") && (this.$scrollbar = document.getElementById(scrollbar));
                (reg.isEle.test(Object.prototype.toString.call(scrollbar))) && (this.$scrollbar = scrollbar);
            }
            //设置最外层容器的样式
            this.$scrollContainer.style.position = "relative";
            this.$scrollContainer.style.overflow = "hidden";
            //设置内容容器的样式
            this.$scrollContent.style.position = "absolute";

            //内容容器的高
            this.scrollHeight = this.$scrollContent.offsetHeight;
            //最外层容器的高
            this.containerHeight = this.$scrollContainer.offsetHeight;
            if (this.$scrollbar) {
                this.$scrollbar.style.height = (this.containerHeight * this.containerHeight) / this.scrollHeight + "px";
                //滚动条的高
                this.scrollbarHeight = this.$scrollbar.offsetHeight;
                if (this.scrollbarHeight >= this.containerHeight) {
                    this.$scrollbar.style.display = "none";
                }
            }

            //滑块滚动部分
            if (this.$scrollbar) {
                this.$scrollbar.style.opacity = 0.7;
                this.$scrollContainer.onmouseover = function () {
                    self.$scrollbar.style.opacity = 1;
                };
                this.$scrollContainer.onmouseout = function () {
                    self.$scrollbar.style.opacity = 0.7;
                };
                parent.is_mobile() && (this.$scrollbar.style.opacity = 0);
                this.$scrollbar.onmousedown = function (e) {
                    var objY = this.offsetTop;
                    var e = e || window.event;
                    var y = e.clientY;
                    var realY = y - objY;
                    var _this = this;
                    self.scrollHeight = self.$scrollContent.offsetHeight;
                    document.onmousemove = function (e) {
                        var e = e || window.event;
                        var y1 = e.clientY;
                        var realY2 = y1 - realY;
                        if (realY2 < 0) {
                            realY2 = 0;
                        } else if (realY2 > self.containerHeight - self.scrollbarHeight) {
                            realY2 = self.containerHeight - self.scrollbarHeight;
                        }
                        _this.style.top = realY2 + "px";
                        self.$scrollContent.style.top = (self.scroll = -realY2 / (self.containerHeight - self.scrollbarHeight) * (self.scrollHeight - self.containerHeight)) + "px";
                        e.preventDefault();
                        return false;
                    };
                    document.onmouseup = function () {
                        document.onmousemove = null;
                        document.onmouseup = null;
                    };
                };
            }
            this.mousewheel = function (e) {
                self.scrollFunction(e);
            };
            if (document.addEventListener) {
                this.$scrollContainer.addEventListener('DOMMouseScroll', this.mousewheel, false);
            }
            this.$scrollContainer.onmousewheel = this.mousewheel;
            //移动端触摸滚动
            (function () {
                var x, y, begin_top;
                self.$scrollContainer.addEventListener("touchstart", function (e) {
                    var e = e || window.event;
                    var touch = e.touches[0];
                    y = parseInt(touch.clientY);
                    begin_top = self.$scrollContent.offsetTop;
                    $scrollHeight = self.$scrollContent.offsetHeight;
                }, false);
                self.$scrollContainer.addEventListener("touchmove", function (e) {
                    var e = e || window.event;
                    e.preventDefault();
                    var touch = e.touches[0];
                    y1 = parseInt(touch.clientY);
                    self.$scrollContent.style.top = (self.scroll = begin_top + (y1 - y)) + "px";
                    if (self.$scrollContent.offsetTop > 0) {
                        self.$scrollContent.style.top = 0;
                        self.scroll = 0;
                    } else if (self.$scrollContent.offsetTop < -(self.scrollHeight - self.containerHeight)) {
                        self.$scrollContent.style.top = (self.scroll = -(self.scrollHeight - self.containerHeight)) + "px";
                    }

                    //距离比例
                    var proportion = Math.abs(self.scroll) / (self.scrollHeight - self.containerHeight);
                    //设置滚动条滑块移动的距离
                    if (self.$scrollbar) {
                        self.$scrollbar.style.top = proportion * (self.containerHeight - self.scrollbarHeight) + "px";
                    }
                    //触摸滑屏的时候显示滚动条
                    self.$scrollbar.style.opacity = 1;
                }, false);
                self.$scrollContainer.addEventListener("touchend", function (e) {
                    clearTimeout(self.IDD);
                    self.IDD = setTimeout(function () {
                        //手指离开屏幕的时候隐藏滚动条
                        self.$scrollbar.style.opacity = 0;
                    }, 500);
                });
            })();
        }
    };
}

box_scroll.prototype = {
    getEle: function (select, parentEle, num) {
        var reg = {
            "is_ele": /^\[object HTML\S+Element\]$/i,
            "id": /^#[A-Za-z_\-][A-Za-z_\-1-9]*$/,
            "class": /^(\.[A-Za-z_\-][A-Za-z_\-1-9]*\s*)+$/,
            "tagName": /^[A-Za-z_\-][A-Za-z_\-1-9]*$/,
        };
        if (!reg.is_ele.test(Object.prototype.toString.call(parentEle))) {
            parentEle = document;
        }

        if (select) {
            if (typeof select === "string") {
                if (reg.id.test(select)) {
                    select = this.trim(this.trim(select), "#");
                    return document.getElementById(select);
                }
                if (reg.class.test(select)) {
                    var ele_list = parentEle.getElementsByTagName("*");
                    select = this.trim(select, "\\.");
                    var select_ele_list = [];
                    for (var i = 0, len = ele_list.length; i < len; i++) {
                        var class_attr_str = ele_list[i].getAttribute("class");
                        if (class_attr_str) {
                            var class_list = class_attr_str.split(/\s+/i);
                            if (this.in_array(select, class_list)) {
                                select_ele_list.push(ele_list[i]);
                            }
                        }
                    }
                    return typeof num === "number" && num > -1 ? select_ele_list[num] : select_ele_list;
                }
                if (reg.tagName.test(select)) {
                    var ele_list = parentEle.getElementsByTagName(this.trim(select));
                    return typeof num === "number" && num > -1 ? ele_list[num] : ele_list;
                }
            }
        }
        return false;
    },
    trim: function (str, preg) {
        if (preg) {
            preg = new RegExp("(^" + preg + "*|" + preg + "*$)", "ig");
        }
        (preg) || (preg = /(^[\s\t\r\n]*|[\s\t\r\n]*$)/ig);
        return str.replace(preg, "");
    },
    ltrim: function (str, preg) {
        if (preg) {
            preg = new RegExp("(^" + preg + "*)", "ig");
        }
        (preg) || (preg = /(^[\s\t\r\n]*)/i);
        return str.replace(preg, "");
    },
    rtrim: function (str, preg) {
        if (preg) {
            preg = new RegExp("(" + preg + "*$)", "ig");
        }
        (preg) || (preg = /([\s\t\r\n]*$)/i);
        return str.replace(preg, "");
    },
    in_array: function (needle, haystack, argStrict) {
        var key = "", strict = !!argStrict;
        if (strict) {
            for (key in haystack) {
                if (haystack[key] === needle) {
                    return true
                }
            }
        } else {
            for (key in haystack) {
                if (haystack[key] == needle) {
                    return true
                }
            }
        }
        return false;
    },
    is_mobile: function () {
        if (window.navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) {
            /* 移动端*/
            return true;
        } else {
            /* PC端*/
            return false;
        }
    },
};

/**
 * 自定义滚动条
 * @param {string|object} scrollContainer 最外层容器
 * @param {string|object} scrollContent 内容容器部分
 * @param {string|object} scrollbar 滚动条盒子
 * @param {number|string|null|boolean} opt 选项
 * @returns {box_scroll}
 */
function my_scroll(scrollContainer, scrollContent, scrollbar, opt) {
    var sc = new box_scroll();
    opt || (opt = 1);
    //上下滚动
    if (sc.in_array(opt, [1, "tb", "top_bottom"])) {
        sc.tb.run(scrollContainer, scrollContent, scrollbar);
    }
    //左右滚动
    if (sc.in_array(opt, [2, "lr", "left_right"])) {
        sc.lr.run(scrollContainer, scrollContent, scrollbar);
    }
    return sc;
} 

css 样式表代码如下:

 @charset "UTF-8";
body .scroll_relative {
    position: relative;
}

body .scroll_absolute {
    position: absolute;
}

/*底部滚动条*/
body .scrollbar_bottom {
    position: absolute;
    left: 0;
    bottom: 0;
    display: block;
    width: 40px;
    height: 5px;
    background: gray;
    border-radius: 2.5px 2.5px 2.5px 2.5px;
    transition-property: background-color, opacity;
    transition-duration: 0.3s;
    transition-timing-function: ease-out;
    transition-delay: 0s;
}

/*右边滚动条*/
body .scrollbar_right {
    position: absolute;
    top: 0;
    right: 0;
    display: block;
    width: 5px;
    height: 40px;
    background: gray;
    border-radius: 2.5px 2.5px 2.5px 2.5px;
    transition-property: background-color, opacity;
    transition-duration: 0.3s;
    transition-timing-function: ease-out;
    transition-delay: 0s;
}

/*最外层容器测试样式*/
body .container_scroll {
    width: 200px;
    height: 300px;
    background: #f7f7f7;
    overflow: hidden;
} 


HTML代码如下:

 <!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link type="text/css" rel="stylesheet" href="./css/scroll.css">
    <title>自定义滚动条展示</title>
</head>
<body>
<!--滚动条测试-->
<div id="container_tb" class="container_scroll">
    <div id="content_tb" style="min-height: 700px;">
        这个事件在标准下和IE下是有区别的。
        firefox是按标准实现的,事件名为"DOMMouseScroll ",IE下采用的则是"mousewheel "。
        当然一行代码就解决了兼容问题.
        <br>
        <br>
        <br>
    </div>
    <i id="scrollbar_tb" class="scrollbar_right"></i>
</div>

<div id="container_lr_01" class="container_scroll" style="margin: 20px auto 0 auto;">
    <div id="content_lr_01">
        <p style="width: 1000px;">
            CSS 动画属性
            @keyframes  规定动画模式。
            animation  设置所有动画属性的简写属性
            animation-delay  规定动画开始的延迟。
            animation-direction  规定动画是向前播放、向后播放还是交替播放。
            animation-duration  规定动画完成一个周期应花费的时间。
            animation-fill-mode  规定元素在不播放动画时的样式(在开始前、结束后,或两者同时)。
            animation-iteration-count  规定动画应播放的次数。
            animation-name  规定 @keyframes 动画的名称。
            animation-play-state  规定动画是运行还是暂停。
            animation-timing-function  规定动画的速度曲线。
            <br>
            -----------------------------------------------
            <br>
            animation 属性是一个简写属性,用于设置六个动画属性:
            animation-name  规定需要绑定到选择器的 keyframe 名称
            animation-duration  规定完成动画所花费的时间,以秒或毫秒计
            animation-timing-function  规定动画的速度曲线。
            animation-delay  规定在动画开始之前的延迟。
            animation-iteration-count  规定动画应该播放的次数。
            animation-direction  规定是否应该轮流反向播放动画。
        </p>
    </div>
    <i id="scrollbar_lr_01" class="scrollbar_bottom"></i>
</div>

<div id="container_lr_02" class="container_scroll" style="margin: 20px 0 0 0;">
    <div id="content_lr_02">
        <p style="width: 1000px;">
            transition是CSS3新增的动画属性,可以实现属性的平滑过渡,大大提高用户体验,对于多个属性进行过渡的话很多人会这样写 .tr{ transition:all 1s}
        </p>
    </div>
    <i id="scrollbar_lr_02" class="scrollbar_bottom"></i>
</div>

<script type="text/javascript" src="./js/scroll.js"></script>
<script type="text/javascript">
    window.onload = function (e) {
        my_scroll("container_tb", "content_tb", "scrollbar_tb", 1);
        my_scroll("container_lr_01", "content_lr_01", "scrollbar_lr_01", 2);
        my_scroll("container_lr_02", "content_lr_02", "scrollbar_lr_02", 2);
    }
</script>
</body>
</html> 


如图:

纵向

 

横向

 


演示地址