ScrollBar(滚动条)的实现尝试

谁践踏了优雅 2023-01-12 01:55 233阅读 0赞

【写在前面】

最近项目中需要用到滚动条,然后尝试了不少方法:

比如使用 overflow: scroll,但感觉不好控制,效果也不怎么满意。

然后,自己就想着进行简单的实现,这里就讲解一下当时的实现思路。


【正文开始】

首先,我们思考一下,滚动条是什么:

在一个页面中,当某个元素不足以显示内容时,可以滚动的,用于显示剩下内容的条。

因此,对于一个滚动条,我认为它应该具有以下属性:

  • pressed: bool :是否被按下 ( 用于判断用户是否按住滚动条 )。
  • attach: any :附着元素 ( 滚动条附着的元素 )。
  • attachWidth & attachHeight: number :附着元素宽高。
  • target: any :目标元素 ( 被滚动的元素 )。
  • targetWidth & targetHeight: number :目标元素宽高 ( 用于计算滑块宽度以及和后续滚动时的位置计算 )。
  • scrollRatio: number : 滚动比例 [ 0.0 ~ 1.0 ] ( 因为我这里打算是通过改变比例来控制内容的滚动 )。
  • scrollBar: any : 滚动条 ( 仅为条部分,这里我将滚动条分为两部分【滚动条】和【滑块】)。
  • scrollBarWidth: number : 滚动条宽度 ( 用于后续计算 )。
  • scrollThumb: any : 滚动滑块 ( 仅为滑块部分 )。
  • scrollThumbWidth: number : 滚动滑块宽度 ( 用于后续计算 )。
  • scrollThumbPosition: number : 滑块位置 ( 用于后续计算 )。
  • orientation: 方向 ( 水平 / 垂直滚动条 )。

接下来进行初步实现,当然,我只实现了水平滚动条,垂直方向的仿照即可。

先是构造函数,它主要是完成一些赋值和添加事件响应等工作:

注意:初始的 scrollThumb 的宽度需要根据 attach 和 target 的宽度来计算,并且需要给 window 添加事件响应 ( 否则会出现鼠标离开滑块就不能滚动 )。

  1. constructor(attach, target, scrollBar, scrollThumb) {
  2. this.attach = attach;
  3. this.target = target;
  4. this.scrollBar = scrollBar;
  5. this.scrollThumb = scrollThumb;
  6. this.attachWidth = attach.outerWidth();
  7. this.targetWidth = target.outerWidth();
  8. this.scrollBarWidth = scrollBar.outerWidth();
  9. this.scrollThumb.css('width', this.attachWidth / this.targetWidth * this.scrollBarWidth + 'px');
  10. this.scrollThumbWidth = this.scrollThumb.outerWidth();
  11. this.scrollThumb.hover(this.enter.bind(this), this.leave.bind(this));
  12. this.scrollThumb.on('mousemove', this.move.bind(this));
  13. this.scrollThumb.on('mouseup', this.up.bind(this));
  14. this.scrollThumb.on('mousedown', this.down.bind(this));
  15. window.addEventListener('mouseup', this.up.bind(this));
  16. window.addEventListener('mousemove', this.move.bind(this));
  17. }

这里:

enter:为鼠标移入处理函数。

leave:为鼠标移出处理函数。

move:为滑块移动处理函数,它通过计算移动的距离来改变 ratio ( 比率 ) 进行滚动。

up:为鼠标按键弹起处理函数。

down:为鼠标按键按下处理函数。

具体实现如下:

  1. /**
  2. * @param {MouseEvent} event
  3. */
  4. move(event) {
  5. if (this.pressed) {
  6. let x = event.pageX - this.startX;
  7. let right = this.scrollBarWidth - this.scrollThumbWidth;
  8. this.ratio += x / right;
  9. this.startX += x;
  10. }
  11. }
  12. up(event) {
  13. this.pressed = false;
  14. this.leave();
  15. }
  16. down(event) {
  17. this.pressed = true;
  18. this.startX = event.pageX;
  19. }
  20. enter() {
  21. this.scrollThumb.addClass('hover');
  22. }
  23. leave() {
  24. if (!this.pressed)
  25. this.scrollThumb.removeClass('hover');
  26. }

然后是关键的 ratio,由它来控制滚动,这里给出简单的实现:

  1. /**
  2. * @brief 改变滑块位置的比率并控制 target 滚动
  3. * @param {number} ratio 比率 [0.0 ~ 1.0]
  4. */
  5. set ratio(ratio) {
  6. ratio = ratio < 0.0 ? 0.0 : ratio > 1.0 ? 1.0 : ratio;
  7. this.scrollRatio = ratio;
  8. let left = (this.scrollBarWidth - this.scrollThumbWidth) * ratio + 'px';
  9. this.scrollThumb.css('left', left);
  10. this.target.css('left', (this.attachWidth - this.targetWidth) * ratio + 'px');
  11. }
  12. get ratio() {
  13. return this.scrollRatio;
  14. }

到这儿,我们基本已经实现了 ScrollBar,现在来试试效果:

2021013021113342.gif


【结语】

虽然整个 ScrollBar 用起来体验还行,但是仍然有一点小问题,一些情况也没有考虑周全,因此,如果自用的话还需要改进。

其中,代码在:https://github.com/mengps/Web-jx 可以找到,话说这个项目有相当多的小技巧( css 和 js ),请多多 star 呀..⭐_⭐

最后,这里也给出全部代码,可以直接看到效果 ( 注意:使用了 jquery,并且需要把图片路径改一下,还有要支持ES6 ):

  1. <!DOCTYPE html>
  2. <html lang="cn">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Document</title>
  7. <style>
  8. .outer {
  9. position: relative;
  10. margin: auto;
  11. width: 500px;
  12. max-width: 1000px;
  13. height: 1000px;
  14. max-height: 1000px;
  15. overflow: hidden;
  16. border: 5px solid black;
  17. resize: both;
  18. }
  19. .inner {
  20. position: absolute;
  21. width: 1000px;
  22. height: 1000px;
  23. background-image: url(./images/0.jpg);
  24. background-size: contain;
  25. background-position: center;
  26. background-repeat: no-repeat;
  27. }
  28. .outer .scroll_bar {
  29. position: absolute;
  30. margin: 0px 5%;
  31. width: 90%;
  32. height: 2px;
  33. bottom: 10px;
  34. background-color: blue;
  35. border-radius: 5px;
  36. }
  37. .outer .scroll_bar .thumb {
  38. position: absolute;
  39. top: -5px;
  40. height: 10px;
  41. width: 120px;
  42. border-radius: 5px;
  43. background-color: #88cfff;
  44. }
  45. .outer .scroll_bar .thumb.hover {
  46. background-color: #049aff;
  47. cursor: pointer;
  48. }
  49. .unselectable {
  50. -moz-user-select: none;
  51. -webkit-user-select: none;
  52. -ms-user-select: none;
  53. user-select: none;
  54. }
  55. </style>
  56. <script type="text/javascript" src="lib/jquery-3.5.1.min.js"></script>
  57. </head>
  58. <body>
  59. <div class="outer">
  60. <div class="inner unselectable"></div>
  61. <div class="scroll_bar">
  62. <div class="thumb"></div>
  63. </div>
  64. </div>
  65. <script>
  66. class ScrollBar {
  67. constructor(attach, target, scrollBar, scrollThumb) {
  68. this.attach = attach;
  69. this.target = target;
  70. this.scrollBar = scrollBar;
  71. this.scrollThumb = scrollThumb;
  72. this.attachWidth = attach.outerWidth();
  73. this.targetWidth = target.outerWidth();
  74. this.scrollBarWidth = scrollBar.outerWidth();
  75. this.scrollThumb.css('width', this.attachWidth / this.targetWidth * this.scrollBarWidth + 'px');
  76. this.scrollThumbWidth = this.scrollThumb.outerWidth();
  77. this.scrollThumb.hover(this.enter.bind(this), this.leave.bind(this));
  78. this.scrollThumb.on('mousemove', this.move.bind(this));
  79. this.scrollThumb.on('mouseup', this.up.bind(this));
  80. this.scrollThumb.on('mousedown', this.down.bind(this));
  81. window.addEventListener('mouseup', this.up.bind(this));
  82. window.addEventListener('mousemove', this.move.bind(this));
  83. }
  84. show() {
  85. this.scrollBar.show();
  86. }
  87. hide() {
  88. this.scrollBar.hide();
  89. }
  90. /**
  91. * @param {MouseEvent} event
  92. */
  93. move(event) {
  94. if (this.pressed) {
  95. let x = event.pageX - this.startX;
  96. let right = this.scrollBarWidth - this.scrollThumbWidth;
  97. this.ratio += x / right;
  98. this.startX += x;
  99. }
  100. }
  101. up(event) {
  102. this.pressed = false;
  103. this.leave();
  104. }
  105. down(event) {
  106. this.pressed = true;
  107. this.startX = event.pageX;
  108. }
  109. enter() {
  110. this.scrollThumb.addClass('hover');
  111. }
  112. leave() {
  113. if (!this.pressed)
  114. this.scrollThumb.removeClass('hover');
  115. }
  116. /**
  117. * @brief 改变滑块位置的比率并控制 target 滚动
  118. * @param {number} ratio 比率 [0.0 ~ 1.0]
  119. */
  120. set ratio(ratio) {
  121. ratio = ratio < 0.0 ? 0.0 : ratio > 1.0 ? 1.0 : ratio;
  122. this.scrollRatio = ratio;
  123. let left = (this.scrollBarWidth - this.scrollThumbWidth) * ratio + 'px';
  124. this.scrollThumb.css('left', left);
  125. this.target.css('left', (this.attachWidth - this.targetWidth) * ratio + 'px');
  126. }
  127. get ratio() {
  128. return this.scrollRatio;
  129. }
  130. pressed = false;
  131. startX = 0;
  132. scrollRatio = 0.0;
  133. scrollBar;
  134. scrollBarWidth;
  135. scrollThumb;
  136. scrollThumbWidth;
  137. target;
  138. targetWidth;
  139. attach;
  140. attachWidth;
  141. };
  142. (() => {
  143. let scrollBar = new ScrollBar($('.outer'), $('.inner')
  144. , $('.outer .scroll_bar'), $('.outer .scroll_bar .thumb'));
  145. })();
  146. </script>
  147. </body>
  148. </html>

发表评论

表情:
评论列表 (有 0 条评论,233人围观)

还没有评论,来说两句吧...

相关阅读

    相关 滚动scrollbar

    页面滚动条 对于IE浏览器,可以单独设置滚动条样式,从而使其更加符合网站的整体设计。 滚动条组成 : 3dlight、highlight、face、arrow、

    相关 界面控件 - 滚动ScrollBar

    界面是人机交互的门户,对产品至关重要。在界面开发中只有想不到没有做不到的,有好的想法,当然要尝试着做出来。对滚动条的扩展,现在有很多类是的例子。 VS2015的代码编辑是非常