ScrollBar.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import { createVNode as _createVNode } from "vue";
  3. import { defineComponent, reactive } from 'vue';
  4. import classNames from '../_util/classNames';
  5. import createRef from '../_util/createRef';
  6. import raf from '../_util/raf';
  7. import supportsPassive from '../_util/supportsPassive';
  8. const MIN_SIZE = 20;
  9. function getPageY(e) {
  10. return 'touches' in e ? e.touches[0].pageY : e.pageY;
  11. }
  12. export default defineComponent({
  13. compatConfig: {
  14. MODE: 3
  15. },
  16. name: 'ScrollBar',
  17. inheritAttrs: false,
  18. props: {
  19. prefixCls: String,
  20. scrollTop: Number,
  21. scrollHeight: Number,
  22. height: Number,
  23. count: Number,
  24. onScroll: {
  25. type: Function
  26. },
  27. onStartMove: {
  28. type: Function
  29. },
  30. onStopMove: {
  31. type: Function
  32. }
  33. },
  34. setup() {
  35. return {
  36. moveRaf: null,
  37. scrollbarRef: createRef(),
  38. thumbRef: createRef(),
  39. visibleTimeout: null,
  40. state: reactive({
  41. dragging: false,
  42. pageY: null,
  43. startTop: null,
  44. visible: false
  45. })
  46. };
  47. },
  48. watch: {
  49. scrollTop: {
  50. handler() {
  51. this.delayHidden();
  52. },
  53. flush: 'post'
  54. }
  55. },
  56. mounted() {
  57. var _a, _b;
  58. (_a = this.scrollbarRef.current) === null || _a === void 0 ? void 0 : _a.addEventListener('touchstart', this.onScrollbarTouchStart, supportsPassive ? {
  59. passive: false
  60. } : false);
  61. (_b = this.thumbRef.current) === null || _b === void 0 ? void 0 : _b.addEventListener('touchstart', this.onMouseDown, supportsPassive ? {
  62. passive: false
  63. } : false);
  64. },
  65. beforeUnmount() {
  66. this.removeEvents();
  67. clearTimeout(this.visibleTimeout);
  68. },
  69. methods: {
  70. delayHidden() {
  71. clearTimeout(this.visibleTimeout);
  72. this.state.visible = true;
  73. this.visibleTimeout = setTimeout(() => {
  74. this.state.visible = false;
  75. }, 2000);
  76. },
  77. onScrollbarTouchStart(e) {
  78. e.preventDefault();
  79. },
  80. onContainerMouseDown(e) {
  81. e.stopPropagation();
  82. e.preventDefault();
  83. },
  84. // ======================= Clean =======================
  85. patchEvents() {
  86. window.addEventListener('mousemove', this.onMouseMove);
  87. window.addEventListener('mouseup', this.onMouseUp);
  88. this.thumbRef.current.addEventListener('touchmove', this.onMouseMove, supportsPassive ? {
  89. passive: false
  90. } : false);
  91. this.thumbRef.current.addEventListener('touchend', this.onMouseUp);
  92. },
  93. removeEvents() {
  94. window.removeEventListener('mousemove', this.onMouseMove);
  95. window.removeEventListener('mouseup', this.onMouseUp);
  96. this.scrollbarRef.current.removeEventListener('touchstart', this.onScrollbarTouchStart, supportsPassive ? {
  97. passive: false
  98. } : false);
  99. if (this.thumbRef.current) {
  100. this.thumbRef.current.removeEventListener('touchstart', this.onMouseDown, supportsPassive ? {
  101. passive: false
  102. } : false);
  103. this.thumbRef.current.removeEventListener('touchmove', this.onMouseMove, supportsPassive ? {
  104. passive: false
  105. } : false);
  106. this.thumbRef.current.removeEventListener('touchend', this.onMouseUp);
  107. }
  108. raf.cancel(this.moveRaf);
  109. },
  110. // ======================= Thumb =======================
  111. onMouseDown(e) {
  112. const {
  113. onStartMove
  114. } = this.$props;
  115. _extends(this.state, {
  116. dragging: true,
  117. pageY: getPageY(e),
  118. startTop: this.getTop()
  119. });
  120. onStartMove();
  121. this.patchEvents();
  122. e.stopPropagation();
  123. e.preventDefault();
  124. },
  125. onMouseMove(e) {
  126. const {
  127. dragging,
  128. pageY,
  129. startTop
  130. } = this.state;
  131. const {
  132. onScroll
  133. } = this.$props;
  134. raf.cancel(this.moveRaf);
  135. if (dragging) {
  136. const offsetY = getPageY(e) - pageY;
  137. const newTop = startTop + offsetY;
  138. const enableScrollRange = this.getEnableScrollRange();
  139. const enableHeightRange = this.getEnableHeightRange();
  140. const ptg = enableHeightRange ? newTop / enableHeightRange : 0;
  141. const newScrollTop = Math.ceil(ptg * enableScrollRange);
  142. this.moveRaf = raf(() => {
  143. onScroll(newScrollTop);
  144. });
  145. }
  146. },
  147. onMouseUp() {
  148. const {
  149. onStopMove
  150. } = this.$props;
  151. this.state.dragging = false;
  152. onStopMove();
  153. this.removeEvents();
  154. },
  155. // ===================== Calculate =====================
  156. getSpinHeight() {
  157. const {
  158. height,
  159. scrollHeight
  160. } = this.$props;
  161. let baseHeight = height / scrollHeight * 100;
  162. baseHeight = Math.max(baseHeight, MIN_SIZE);
  163. baseHeight = Math.min(baseHeight, height / 2);
  164. return Math.floor(baseHeight);
  165. },
  166. getEnableScrollRange() {
  167. const {
  168. scrollHeight,
  169. height
  170. } = this.$props;
  171. return scrollHeight - height || 0;
  172. },
  173. getEnableHeightRange() {
  174. const {
  175. height
  176. } = this.$props;
  177. const spinHeight = this.getSpinHeight();
  178. return height - spinHeight || 0;
  179. },
  180. getTop() {
  181. const {
  182. scrollTop
  183. } = this.$props;
  184. const enableScrollRange = this.getEnableScrollRange();
  185. const enableHeightRange = this.getEnableHeightRange();
  186. if (scrollTop === 0 || enableScrollRange === 0) {
  187. return 0;
  188. }
  189. const ptg = scrollTop / enableScrollRange;
  190. return ptg * enableHeightRange;
  191. },
  192. // Not show scrollbar when height is large than scrollHeight
  193. showScroll() {
  194. const {
  195. height,
  196. scrollHeight
  197. } = this.$props;
  198. return scrollHeight > height;
  199. }
  200. },
  201. render() {
  202. // eslint-disable-next-line no-unused-vars
  203. const {
  204. dragging,
  205. visible
  206. } = this.state;
  207. const {
  208. prefixCls
  209. } = this.$props;
  210. const spinHeight = this.getSpinHeight() + 'px';
  211. const top = this.getTop() + 'px';
  212. const canScroll = this.showScroll();
  213. const mergedVisible = canScroll && visible;
  214. return _createVNode("div", {
  215. "ref": this.scrollbarRef,
  216. "class": classNames(`${prefixCls}-scrollbar`, {
  217. [`${prefixCls}-scrollbar-show`]: canScroll
  218. }),
  219. "style": {
  220. width: '8px',
  221. top: 0,
  222. bottom: 0,
  223. right: 0,
  224. position: 'absolute',
  225. display: mergedVisible ? undefined : 'none'
  226. },
  227. "onMousedown": this.onContainerMouseDown,
  228. "onMousemove": this.delayHidden
  229. }, [_createVNode("div", {
  230. "ref": this.thumbRef,
  231. "class": classNames(`${prefixCls}-scrollbar-thumb`, {
  232. [`${prefixCls}-scrollbar-thumb-moving`]: dragging
  233. }),
  234. "style": {
  235. width: '100%',
  236. height: spinHeight,
  237. top,
  238. left: 0,
  239. position: 'absolute',
  240. background: 'rgba(0, 0, 0, 0.5)',
  241. borderRadius: '99px',
  242. cursor: 'pointer',
  243. userSelect: 'none'
  244. },
  245. "onMousedown": this.onMouseDown
  246. }, null)]);
  247. }
  248. });