stickyScrollBar.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import { createVNode as _createVNode } from "vue";
  3. import { nextTick, onActivated, watchEffect, defineComponent, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
  4. import addEventListenerWrap from '../vc-util/Dom/addEventListener';
  5. import { getOffset } from '../vc-util/Dom/css';
  6. import classNames from '../_util/classNames';
  7. import getScrollBarSize from '../_util/getScrollBarSize';
  8. import { useInjectTable } from './context/TableContext';
  9. import { useLayoutState } from './hooks/useFrame';
  10. export default defineComponent({
  11. name: 'StickyScrollBar',
  12. inheritAttrs: false,
  13. props: ['offsetScroll', 'container', 'scrollBodyRef', 'scrollBodySizeInfo'],
  14. emits: ['scroll'],
  15. setup(props, _ref) {
  16. let {
  17. emit,
  18. expose
  19. } = _ref;
  20. const tableContext = useInjectTable();
  21. const bodyScrollWidth = shallowRef(0);
  22. const bodyWidth = shallowRef(0);
  23. const scrollBarWidth = shallowRef(0);
  24. watchEffect(() => {
  25. bodyScrollWidth.value = props.scrollBodySizeInfo.scrollWidth || 0;
  26. bodyWidth.value = props.scrollBodySizeInfo.clientWidth || 0;
  27. scrollBarWidth.value = bodyScrollWidth.value && bodyWidth.value * (bodyWidth.value / bodyScrollWidth.value);
  28. }, {
  29. flush: 'post'
  30. });
  31. const scrollBarRef = shallowRef();
  32. const [scrollState, setScrollState] = useLayoutState({
  33. scrollLeft: 0,
  34. isHiddenScrollBar: true
  35. });
  36. const refState = ref({
  37. delta: 0,
  38. x: 0
  39. });
  40. const isActive = shallowRef(false);
  41. const onMouseUp = () => {
  42. isActive.value = false;
  43. };
  44. const onMouseDown = event => {
  45. refState.value = {
  46. delta: event.pageX - scrollState.value.scrollLeft,
  47. x: 0
  48. };
  49. isActive.value = true;
  50. event.preventDefault();
  51. };
  52. const onMouseMove = event => {
  53. // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
  54. const {
  55. buttons
  56. } = event || (window === null || window === void 0 ? void 0 : window.event);
  57. if (!isActive.value || buttons === 0) {
  58. // If out body mouse up, we can set isActive false when mouse move
  59. if (isActive.value) {
  60. isActive.value = false;
  61. }
  62. return;
  63. }
  64. let left = refState.value.x + event.pageX - refState.value.x - refState.value.delta;
  65. if (left <= 0) {
  66. left = 0;
  67. }
  68. if (left + scrollBarWidth.value >= bodyWidth.value) {
  69. left = bodyWidth.value - scrollBarWidth.value;
  70. }
  71. emit('scroll', {
  72. scrollLeft: left / bodyWidth.value * (bodyScrollWidth.value + 2)
  73. });
  74. refState.value.x = event.pageX;
  75. };
  76. const onContainerScroll = () => {
  77. if (!props.scrollBodyRef.value) {
  78. return;
  79. }
  80. const tableOffsetTop = getOffset(props.scrollBodyRef.value).top;
  81. const tableBottomOffset = tableOffsetTop + props.scrollBodyRef.value.offsetHeight;
  82. const currentClientOffset = props.container === window ? document.documentElement.scrollTop + window.innerHeight : getOffset(props.container).top + props.container.clientHeight;
  83. if (tableBottomOffset - getScrollBarSize() <= currentClientOffset || tableOffsetTop >= currentClientOffset - props.offsetScroll) {
  84. setScrollState(state => _extends(_extends({}, state), {
  85. isHiddenScrollBar: true
  86. }));
  87. } else {
  88. setScrollState(state => _extends(_extends({}, state), {
  89. isHiddenScrollBar: false
  90. }));
  91. }
  92. };
  93. const setScrollLeft = left => {
  94. setScrollState(state => {
  95. return _extends(_extends({}, state), {
  96. scrollLeft: left / bodyScrollWidth.value * bodyWidth.value || 0
  97. });
  98. });
  99. };
  100. expose({
  101. setScrollLeft
  102. });
  103. let onMouseUpListener = null;
  104. let onMouseMoveListener = null;
  105. let onResizeListener = null;
  106. let onScrollListener = null;
  107. onMounted(() => {
  108. onMouseUpListener = addEventListenerWrap(document.body, 'mouseup', onMouseUp, false);
  109. onMouseMoveListener = addEventListenerWrap(document.body, 'mousemove', onMouseMove, false);
  110. onResizeListener = addEventListenerWrap(window, 'resize', onContainerScroll, false);
  111. });
  112. onActivated(() => {
  113. nextTick(() => {
  114. onContainerScroll();
  115. });
  116. });
  117. onMounted(() => {
  118. setTimeout(() => {
  119. watch([scrollBarWidth, isActive], () => {
  120. onContainerScroll();
  121. }, {
  122. immediate: true,
  123. flush: 'post'
  124. });
  125. });
  126. });
  127. watch(() => props.container, () => {
  128. onScrollListener === null || onScrollListener === void 0 ? void 0 : onScrollListener.remove();
  129. onScrollListener = addEventListenerWrap(props.container, 'scroll', onContainerScroll, false);
  130. }, {
  131. immediate: true,
  132. flush: 'post'
  133. });
  134. onBeforeUnmount(() => {
  135. onMouseUpListener === null || onMouseUpListener === void 0 ? void 0 : onMouseUpListener.remove();
  136. onMouseMoveListener === null || onMouseMoveListener === void 0 ? void 0 : onMouseMoveListener.remove();
  137. onScrollListener === null || onScrollListener === void 0 ? void 0 : onScrollListener.remove();
  138. onResizeListener === null || onResizeListener === void 0 ? void 0 : onResizeListener.remove();
  139. });
  140. watch(() => _extends({}, scrollState.value), (newState, preState) => {
  141. if (newState.isHiddenScrollBar !== (preState === null || preState === void 0 ? void 0 : preState.isHiddenScrollBar) && !newState.isHiddenScrollBar) {
  142. setScrollState(state => {
  143. const bodyNode = props.scrollBodyRef.value;
  144. if (!bodyNode) {
  145. return state;
  146. }
  147. return _extends(_extends({}, state), {
  148. scrollLeft: bodyNode.scrollLeft / bodyNode.scrollWidth * bodyNode.clientWidth
  149. });
  150. });
  151. }
  152. }, {
  153. immediate: true
  154. });
  155. const scrollbarSize = getScrollBarSize();
  156. return () => {
  157. if (bodyScrollWidth.value <= bodyWidth.value || !scrollBarWidth.value || scrollState.value.isHiddenScrollBar) {
  158. return null;
  159. }
  160. const {
  161. prefixCls
  162. } = tableContext;
  163. return _createVNode("div", {
  164. "style": {
  165. height: `${scrollbarSize}px`,
  166. width: `${bodyWidth.value}px`,
  167. bottom: `${props.offsetScroll}px`
  168. },
  169. "class": `${prefixCls}-sticky-scroll`
  170. }, [_createVNode("div", {
  171. "onMousedown": onMouseDown,
  172. "ref": scrollBarRef,
  173. "class": classNames(`${prefixCls}-sticky-scroll-bar`, {
  174. [`${prefixCls}-sticky-scroll-bar-active`]: isActive.value
  175. }),
  176. "style": {
  177. width: `${scrollBarWidth.value}px`,
  178. transform: `translate3d(${scrollState.value.scrollLeft}px, 0, 0)`
  179. }
  180. }, null)]);
  181. };
  182. }
  183. });