refactor: backtop
							parent
							
								
									5bcca46fff
								
							
						
					
					
						commit
						b96fc440e9
					
				|  | @ -1,17 +0,0 @@ | |||
| export default function getScroll(target, top) { | ||||
|   if (typeof window === 'undefined') { | ||||
|     return 0; | ||||
|   } | ||||
| 
 | ||||
|   const prop = top ? 'pageYOffset' : 'pageXOffset'; | ||||
|   const method = top ? 'scrollTop' : 'scrollLeft'; | ||||
|   const isWindow = target === window; | ||||
| 
 | ||||
|   let ret = isWindow ? target[prop] : target[method]; | ||||
|   // ie6,7,8 standard mode
 | ||||
|   if (isWindow && typeof ret !== 'number') { | ||||
|     ret = window.document.documentElement[method]; | ||||
|   } | ||||
| 
 | ||||
|   return ret; | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| export function isWindow(obj: any) { | ||||
|   return obj !== null && obj !== undefined && obj === obj.window; | ||||
| } | ||||
| 
 | ||||
| export default function getScroll( | ||||
|   target: HTMLElement | Window | Document | null, | ||||
|   top: boolean, | ||||
| ): number { | ||||
|   if (typeof window === 'undefined') { | ||||
|     return 0; | ||||
|   } | ||||
|   const method = top ? 'scrollTop' : 'scrollLeft'; | ||||
|   let result = 0; | ||||
|   if (isWindow(target)) { | ||||
|     result = (target as Window)[top ? 'pageYOffset' : 'pageXOffset']; | ||||
|   } else if (target instanceof Document) { | ||||
|     result = target.documentElement[method]; | ||||
|   } else if (target) { | ||||
|     result = (target as HTMLElement)[method]; | ||||
|   } | ||||
|   if (target && !isWindow(target) && typeof result !== 'number') { | ||||
|     result = ((target as HTMLElement).ownerDocument || (target as Document)).documentElement?.[ | ||||
|       method | ||||
|     ]; | ||||
|   } | ||||
|   return result; | ||||
| } | ||||
|  | @ -1,9 +1,10 @@ | |||
| import getScroll from './getScroll'; | ||||
| import raf from './raf'; | ||||
| import getScroll, { isWindow } from './getScroll'; | ||||
| import { easeInOutCubic } from './easings'; | ||||
| 
 | ||||
| interface ScrollToOptions { | ||||
|   /** Scroll container, default as window */ | ||||
|   getContainer?: () => HTMLElement | Window; | ||||
|   getContainer?: () => HTMLElement | Window | Document; | ||||
|   /** Scroll end callback */ | ||||
|   callback?: () => any; | ||||
|   /** Animation duration, default as 450 */ | ||||
|  | @ -12,7 +13,6 @@ interface ScrollToOptions { | |||
| 
 | ||||
| export default function scrollTo(y: number, options: ScrollToOptions = {}) { | ||||
|   const { getContainer = () => window, callback, duration = 450 } = options; | ||||
| 
 | ||||
|   const container = getContainer(); | ||||
|   const scrollTop = getScroll(container, true); | ||||
|   const startTime = Date.now(); | ||||
|  | @ -21,16 +21,18 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) { | |||
|     const timestamp = Date.now(); | ||||
|     const time = timestamp - startTime; | ||||
|     const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration); | ||||
|     if (container === window) { | ||||
|       window.scrollTo(window.pageXOffset, nextScrollTop); | ||||
|     if (isWindow(container)) { | ||||
|       (container as Window).scrollTo(window.pageXOffset, nextScrollTop); | ||||
|     } else if (container instanceof HTMLDocument || container.constructor.name === 'HTMLDocument') { | ||||
|       (container as HTMLDocument).documentElement.scrollTop = nextScrollTop; | ||||
|     } else { | ||||
|       (container as HTMLElement).scrollTop = nextScrollTop; | ||||
|     } | ||||
|     if (time < duration) { | ||||
|       requestAnimationFrame(frameFunc); | ||||
|       raf(frameFunc); | ||||
|     } else if (typeof callback === 'function') { | ||||
|       callback(); | ||||
|     } | ||||
|   }; | ||||
|   requestAnimationFrame(frameFunc); | ||||
|   raf(frameFunc); | ||||
| } | ||||
|  |  | |||
|  | @ -7,8 +7,13 @@ import { | |||
|   onBeforeUnmount, | ||||
|   onMounted, | ||||
|   reactive, | ||||
|   PropType, | ||||
|   ref, | ||||
|   watch, | ||||
|   onDeactivated, | ||||
|   computed, | ||||
| } from 'vue'; | ||||
| import classNames from '../_util/classNames'; | ||||
| import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined'; | ||||
| import PropTypes from '../_util/vue-types'; | ||||
| import addEventListener from '../vc-util/Dom/addEventListener'; | ||||
| import getScroll from '../_util/getScroll'; | ||||
|  | @ -16,18 +21,12 @@ import { getTransitionProps, Transition } from '../_util/transition'; | |||
| import { defaultConfigProvider } from '../config-provider'; | ||||
| import scrollTo from '../_util/scrollTo'; | ||||
| import { withInstall } from '../_util/type'; | ||||
| 
 | ||||
| function getDefaultTarget() { | ||||
|   return window; | ||||
| } | ||||
| import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'; | ||||
| 
 | ||||
| export const backTopProps = { | ||||
|   // 滚动高度达到此参数值才出现 BackTop | ||||
|   visibilityHeight: PropTypes.number.def(400), | ||||
|   // 回到顶部所需时间(ms) @4.4.0 | ||||
|   duration: PropTypes.number.def(450), | ||||
|   // 设置需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | ||||
|   target: PropTypes.func, | ||||
|   target: Function as PropType<() => HTMLElement | Window | Document>, | ||||
|   prefixCls: PropTypes.string, | ||||
|   onClick: PropTypes.func, | ||||
|   // visible: PropTypes.looseBool, // Only for test. Don't use it. | ||||
|  | @ -42,12 +41,15 @@ const BackTop = defineComponent({ | |||
|   emits: ['click'], | ||||
|   setup(props, { slots, attrs, emit }) { | ||||
|     const configProvider = inject('configProvider', defaultConfigProvider); | ||||
| 
 | ||||
|     const domRef = ref(); | ||||
|     const state = reactive({ | ||||
|       visible: false, | ||||
|       scrollEvent: null, | ||||
|     }); | ||||
| 
 | ||||
|     const getDefaultTarget = () => | ||||
|       domRef.value && domRef.value.ownerDocument ? domRef.value.ownerDocument : window; | ||||
| 
 | ||||
|     const scrollToTop = (e: Event) => { | ||||
|       const { target = getDefaultTarget, duration } = props; | ||||
|       scrollTo(0, { | ||||
|  | @ -57,51 +59,85 @@ const BackTop = defineComponent({ | |||
|       emit('click', e); | ||||
|     }; | ||||
| 
 | ||||
|     const handleScroll = () => { | ||||
|       const { visibilityHeight, target = getDefaultTarget } = props; | ||||
|       const scrollTop = getScroll(target(), true); | ||||
|     const handleScroll = throttleByAnimationFrame((e: Event | { target: any }) => { | ||||
|       const { visibilityHeight } = props; | ||||
|       const scrollTop = getScroll(e.target, true); | ||||
|       state.visible = scrollTop > visibilityHeight; | ||||
|     }); | ||||
| 
 | ||||
|     const bindScrollEvent = () => { | ||||
|       const { target } = props; | ||||
|       const getTarget = target || getDefaultTarget; | ||||
|       const container = getTarget(); | ||||
|       state.scrollEvent = addEventListener(container, 'scroll', (e: Event) => { | ||||
|         handleScroll(e); | ||||
|       }); | ||||
|       handleScroll({ | ||||
|         target: container, | ||||
|       }); | ||||
|     }; | ||||
| 
 | ||||
|     const scrollRemove = () => { | ||||
|       if (state.scrollEvent) { | ||||
|         state.scrollEvent.remove(); | ||||
|       } | ||||
|       (handleScroll as any).cancel(); | ||||
|     }; | ||||
| 
 | ||||
|     watch( | ||||
|       () => props.target, | ||||
|       () => { | ||||
|         scrollRemove(); | ||||
|         nextTick(() => { | ||||
|           bindScrollEvent(); | ||||
|         }); | ||||
|       }, | ||||
|     ); | ||||
| 
 | ||||
|     onMounted(() => { | ||||
|       nextTick(() => { | ||||
|         const getTarget = props.target || getDefaultTarget; | ||||
|         state.scrollEvent = addEventListener(getTarget(), 'scroll', handleScroll); | ||||
|         handleScroll(); | ||||
|         bindScrollEvent(); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     onActivated(() => { | ||||
|       nextTick(() => { | ||||
|         handleScroll(); | ||||
|         bindScrollEvent(); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     onBeforeUnmount(() => { | ||||
|       if (state.scrollEvent) { | ||||
|         state.scrollEvent.remove(); | ||||
|       } | ||||
|     onDeactivated(() => { | ||||
|       scrollRemove(); | ||||
|     }); | ||||
| 
 | ||||
|     return () => { | ||||
|       const { prefixCls: customizePrefixCls } = props; | ||||
|     onBeforeUnmount(() => { | ||||
|       scrollRemove(); | ||||
|     }); | ||||
| 
 | ||||
|       const getPrefixCls = configProvider.getPrefixCls; | ||||
|       const prefixCls = getPrefixCls('back-top', customizePrefixCls); | ||||
|       const classString = classNames(prefixCls, attrs.class); | ||||
|     const prefixCls = computed(() => configProvider.getPrefixCls('back-top', props.prefixCls)); | ||||
| 
 | ||||
|     return () => { | ||||
|       const defaultElement = ( | ||||
|         <div class={`${prefixCls}-content`}> | ||||
|           <div class={`${prefixCls}-icon`} /> | ||||
|         <div class={`${prefixCls.value}-content`}> | ||||
|           <div class={`${prefixCls.value}-icon`}> | ||||
|             <VerticalAlignTopOutlined /> | ||||
|           </div> | ||||
|         </div> | ||||
|       ); | ||||
|       const divProps = { | ||||
|         ...attrs, | ||||
|         onClick: scrollToTop, | ||||
|         class: classString, | ||||
|         class: { | ||||
|           [`${prefixCls.value}`]: true, | ||||
|           [`${attrs.class}`]: attrs.class, | ||||
|           [`${prefixCls}-rtl`]: configProvider.direction === 'rtl', | ||||
|         }, | ||||
|       }; | ||||
| 
 | ||||
|       const backTopBtn = state.visible ? ( | ||||
|         <div {...divProps}>{slots.default?.() || defaultElement}</div> | ||||
|         <div {...divProps} ref={domRef}> | ||||
|           {slots.default?.() || defaultElement} | ||||
|         </div> | ||||
|       ) : null; | ||||
|       const transitionProps = getTransitionProps('fade'); | ||||
|       return <Transition {...transitionProps}>{backTopBtn}</Transition>; | ||||
|  |  | |||
|  | @ -18,6 +18,12 @@ | |||
|     display: none; | ||||
|   } | ||||
| 
 | ||||
|   &-rtl { | ||||
|     right: auto; | ||||
|     left: 100px; | ||||
|     direction: rtl; | ||||
|   } | ||||
| 
 | ||||
|   &-content { | ||||
|     width: 40px; | ||||
|     height: 40px; | ||||
|  | @ -26,20 +32,17 @@ | |||
|     text-align: center; | ||||
|     background-color: @back-top-bg; | ||||
|     border-radius: 20px; | ||||
|     transition: all 0.3s @ease-in-out; | ||||
|     transition: all 0.3s; | ||||
| 
 | ||||
|     &:hover { | ||||
|       background-color: @back-top-hover-bg; | ||||
|       transition: all 0.3s @ease-in-out; | ||||
|       transition: all 0.3s; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &-icon { | ||||
|     width: 14px; | ||||
|     height: 16px; | ||||
|     margin: 12px auto; | ||||
|     background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAoCAYAAACWwljjAAAABGdBTUEAALGPC/xhBQAAAbtJREFUWAntmMtKw0AUhhMvS5cuxILgQlRUpIggIoKIIoigG1eC+AA+jo+i6FIXBfeuXIgoeKVeitVWJX5HWhhDksnUpp3FDPyZk3Nm5nycmZKkXhAEOXSA3lG7muTeRzmfy6HneUvIhnYkQK+Q9NhAA0Opg0vBEhjBKHiyb8iGMyQMOYuK41BcBSypAL+MYXSKjtFAW7EAGEO3qN4uMQbbAkXiSfRQJ1H6a+yhlkKRcAoVFYiweYNjtCVQJJpBz2GCiPt7fBOZQpFgDpUikse5HgnkM4Fi4QX0Fpc5wf9EbLqpUCy4jMoJSXWhFwbMNgWKhVbRhy5jirhs9fy/oFhgHVVTJEs7RLZ8sSEoJm6iz7SZDMbJ+/OKERQTttCXQRLToRUmrKWCYuA2+jbN0MB4OQobYShfdTCgn/sL1K36M7TLrN3n+758aPy2rrpR6+/od5E8tf/A1uLS9aId5T7J3CNYihkQ4D9PiMdMC7mp4rjB9kjFjZp8BlnVHJBuO1yFXIV0FdDF3RlyFdJVQBdv5AxVdIsq8apiZ2PyYO1EVykesGfZEESsCkweyR8MUW+V8uJ1gkYipmpdP1pm2aJVPEGzAAAAAElFTkSuQmCC) | ||||
|       ~'100%/100%' no-repeat; | ||||
|     font-size: 24px; | ||||
|     line-height: 40px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										2
									
								
								v2-doc
								
								
								
								
							
							
								
								
								
								
								
								
							
						
						
									
										2
									
								
								v2-doc
								
								
								
								
							|  | @ -1 +1 @@ | |||
| Subproject commit 4f1ece5073f736e79c6eb22527a9a83e8c6182b3 | ||||
| Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2 | ||||
		Loading…
	
		Reference in New Issue
	
	 tangjinzhou
						tangjinzhou