应用开发过程中当web页面的内容过多时则会出现滚动条,而原生的滚动条的样式除了谷歌浏览器外其他的浏览器都不好修改,于是打算自己写一个容器组件,当内容过多时隐藏默认的滚动条显示自定义滚动条(只做了垂直滚动条,懒~)
先来看看如何引用这个滚动盒子(hd-scroll,注:"hd"是与我相关某个名字的简称)组件,先在app里面填充100个div:
1 <template>
2 <div class="container">
3 <div v-for="i in 100" :key="i">{{ i }}</div>
4 </div>
5 </template>
然后在把container容器的大小限制一下:
1 <style lang="scss" scoped>
2 .container {
3 background-color: whitesmoke;
4 width: 200px;
5 height: 400px;
6 }
7 </style>
打开页面,可以看到浏览器右边出现了默认的滚动条,而且我们添加的div元素也超出了container范围。
解决这个问题的一般方式是在样式里面添加“overflow:auto”属性,再来看一下效果:
改善了许多,但是滚动条的样式却不好改变,于是现在引入hd-scroll组件:
1 <template>
2 <div class="container">
3 <hd-scroll>
4 <div v-for="i in 100" :key="i">{{ i }}</div>
5 </hd-scroll>
6 </div>
7 </template>
8
9 <script>
10 import hdScroll from './components/hdScroll'
11
12 export default {
13 components: {
14 hdScroll
15 }
16 }
17 </script>
18
19 <style lang="scss" scoped>
20 .container {
21 background-color: whitesmoke;
22 width: 200px;
23 height: 400px;
24 }
25 </style>
在这里需要注意的是用<hd-scroll>标签来包裹住大量的要渲染的元素,同时删除overflow属性,添加了滚动盒子组件后的页面看起来或许是这个样子的:
ps:鼠标的小黄点是录频工具的,不是页面自带的。。。
滚动盒子(hd-scroll)的实现方式如下:
1 <template>
2 <div class="hd-scroll scrollbox" ref="box"
3 @mousewheel.stop.prevent="handleMouseWheel"
4 @mouseenter="handleMouseEnter"
5 @mouseleave="handleMouseLeave">
6 <transition name="fade">
7 <div :class="['scrollbar', { force: force }]" ref="bar"
8 v-show="show" :style="{ 'height': barHeight + 'px'}"
9 @mousedown="handleMouseDown"></div>
10 </transition>
11 <slot></slot>
12 </div>
13 </template>
14
15 <script>
16 export default {
17 name: 'hdScroll',
18 data() {
19 return {
20 box: undefined, // 自定义滚动条盒子
21 bar: undefined, // 滚动条
22 barHeight: 100, // 滚动条高度
23 ratio: 1, // 滚动条偏移率
24 force: false, // 滚动条是否被鼠标光标按住
25 hover: false, // 鼠标光标是否悬停在盒子上
26 show: false // 是否显示滚动条
27 }
28 },
29 mounted() {
30 this.box = this.$refs.box
31 this.bar = this.$refs.bar
32 // 滚动条全局可拖动
33 document.addEventListener('mouseup', this.handleMouseUp)
34 document.addEventListener('mousemove', this.handleMouseMove)
35 },
36 methods: {
37 /**
38 * 鼠标滚轮事件
39 * @param {object} e 事件
40 */
41 handleMouseWheel(e) {
42 this.box.scrollTop -= e.wheelDelta / 4
43 this.bar.style.transform = 'translateY(' + (this.box.scrollTop + this.box.scrollTop / this.ratio) + 'px)'
44 },
45 /**
46 * 鼠标按下
47 * @param {object} e 事件
48 */
49 handleMouseDown(e) {
50 if (e.target === this.bar) {
51 this.box.prevY = e.pageY
52 this.force = true
53 }
54 },
55 /**
56 * 鼠标按键释放
57 */
58 handleMouseUp() {
59 this.force = false
60 this.box.prevY = null
61 if (!this.hover) {
62 this.show = false
63 }
64 },
65 /**
66 * 鼠标移动
67 * @param {object} e 事件
68 */
69 handleMouseMove(e) {
70 if (this.force) {
71 // 阻止默认选中事件(IE下无效)
72 e.preventDefault()
73 this.box.scrollTop += (e.pageY - this.box.prevY) * this.ratio
74 this.bar.style.transform = 'translateY(' + (this.box.scrollTop + this.box.scrollTop / this.ratio) + 'px)'
75 this.box.prevY = e.pageY
76 }
77 },
78 /**
79 * 鼠标光标进入盒子范围
80 */
81 handleMouseEnter() {
82 this.hover = true
83 if (this.box.scrollHeight > this.box.offsetHeight) {
84 // 修正进度条高度和位置(建议通过事件触发)
85 this.barHeight = this.box.offsetHeight ** 2 / this.box.scrollHeight
86 this.ratio = (this.box.scrollHeight - this.box.offsetHeight) / (this.box.offsetHeight - this.barHeight)
87 this.bar.style.transform = 'translateY(' + (this.box.scrollTop + this.box.scrollTop / this.ratio) + 'px)'
88 // 显示滚动条
89 this.$nextTick(() => this.show = true)
90 }
91 },
92 /**
93 * 鼠标光标离开盒子范围
94 */
95 handleMouseLeave() {
96 this.hover = false
97 if (!this.force) {
98 this.show = false
99 }
100 }
101 }
102 }
103 </script>
104
105 <style lang="scss" scoped>
106 // 滚动条宽度
107 $scrollbar-width: 8px;
108
109 .scrollbox {
110 width: 100%;
111 height: 100%;
112 position: relative;
113 padding-right: $scrollbar-width;
114 overflow-y: hidden;
115 }
116 .scrollbar {
117 width: $scrollbar-width;
118 height: 100%;
119 background-color: darkgray;
120 position: absolute;
121 right: 0;
122 border-radius: $scrollbar-width / 2;
123 &:hover {
124 background-color: gray;
125 }
126 &.force {
127 background-color: gray;
128 }
129 }
130
131 // Vue进入离开动画
132 .fade-enter-active, .fade-leave-active {
133 transition: opacity .5s;
134 }
135 .fade-enter, .fade-leave-to {
136 opacity: 0;
137 }
138 </style>
在谷歌里鼠标滚轮事件可使用deltaY来控制滚动,不过为了兼容ie,选择了使用wheelDelta来代替,它们之间的关系大约是wheelDelta == -4 * deltaY。在设置滚动条移动的过程中是通过CSS3属性transform,在低版本ie浏览器中可能无法运行,可以考虑使用style.top来代替,不过看网上大神们都是通过两个div容器来隐藏主默认的滚动条,实现方法如下:
1 <!DOCTYPE html>
2 <html lang="zh-CN">
3 <head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <meta http-equiv="X-UA-Compatible" content="ie=edge">
7 <title>Document</title>
8 <style>
9 #app {
10 width: 200px;
11 height: 400px;
12 }
13 </style>
14 </head>
15 <body>
16 <div id="app">
17 <div class="out-box">
18 <div class="inner-box">
19 <div class="container">
20 <div v-for="i in 100" :key="i">{{ i }}</div>
21 </div>
22 </div>
23 </div>
24 </div>
25 <script src="./vue.min.js"></script>
26 <script>
27 new Vue({
28 el: '#app'
29 })
30 </script>
31 </body>
32 </html>
先设置好页面结构,在这里#app是宽度200px, 高度400px的容器,我们需要使内容不溢出的同时隐藏滚动条:
1 .out-box {
2 width: 100%;
3 height: 100%;
4 position: relative;
5 overflow: hidden;
6 }
7
8 .inner-box {
9 width: 100%;
10 height: 100%;
11 position: absolute;
12 padding-right: 17px;
13 padding-bottom: 17px;
14 overflow: auto;
15 }
只需要添加两个盒子属性就完成了,很简单吧
源码下载(需要自己装包):