From a898a37326d4f893f688147dd0d88aca67bdb013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=B1=9F=E8=BE=B0?= Date: Mon, 4 Sep 2023 10:56:54 +0800 Subject: [PATCH] feat: add scroll to close distance --- .../components/trigger/README.en-US.md | 1 + .../components/trigger/README.zh-CN.md | 1 + .../web-vue/components/trigger/trigger.tsx | 52 +++++++++++++++++-- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/packages/web-vue/components/trigger/README.en-US.md b/packages/web-vue/components/trigger/README.en-US.md index 7cd74a957..7a1c3dc9a 100644 --- a/packages/web-vue/components/trigger/README.en-US.md +++ b/packages/web-vue/components/trigger/README.en-US.md @@ -61,6 +61,7 @@ description: Used to add hover, click, focus and other events to the element, an |render-to-body|Whether to mount under the `body` element|`boolean`|`true`|| |prevent-focus|Whether to prevent elements in the pop-up layer from gaining focus when clicked|`boolean`|`false`|| |scroll-to-close|Whether to close the popover when scrolling|`boolean`|`false`|2.46.0| +|scroll-to-close-distance|Scroll threshold, trigger close when the scroll distance exceeds this value|`number`|`0`|| ### `` Events |Event Name|Description|Parameters|version| diff --git a/packages/web-vue/components/trigger/README.zh-CN.md b/packages/web-vue/components/trigger/README.zh-CN.md index 914db89aa..c4124d8a8 100644 --- a/packages/web-vue/components/trigger/README.zh-CN.md +++ b/packages/web-vue/components/trigger/README.zh-CN.md @@ -60,6 +60,7 @@ description: 用于对元素添加 hover, click, focus 等事件,并且弹出 |render-to-body|是否挂载在 `body` 元素下|`boolean`|`true`|| |prevent-focus|是否阻止弹出层中的元素点击时获取焦点|`boolean`|`false`|| |scroll-to-close|是否在滚动时关闭弹出框|`boolean`|`false`|2.46.0| +|scroll-to-close-distance|滚动阈值,当滚动距离超过该值时触发关闭|`number`|`0`|| ### `` Events |事件名|描述|参数|版本| diff --git a/packages/web-vue/components/trigger/trigger.tsx b/packages/web-vue/components/trigger/trigger.tsx index 7498da5d4..c300ce8cb 100644 --- a/packages/web-vue/components/trigger/trigger.tsx +++ b/packages/web-vue/components/trigger/trigger.tsx @@ -330,6 +330,14 @@ export default defineComponent({ type: Boolean, default: false, }, + /** + * @zh 滚动阈值,当滚动距离超过该值时触发关闭 + * @en Scroll threshold, trigger close when the scroll distance exceeds this value + */ + scrollToCloseDistance: { + type: Number, + default: 0, + }, }, emits: { 'update:popupVisible': (visible: boolean) => true, @@ -387,6 +395,9 @@ export default defineComponent({ left: 0, }); + let scrollPosition: [number, number] | null = null; + let windowScrollPosition: [number, number] | null = null; + const computedVisible = computed( () => props.popupVisible ?? popupVisible.value ); @@ -500,6 +511,11 @@ export default defineComponent({ } }; + if (!visible) { + scrollPosition = null; + windowScrollPosition = null; + } + if (delay) { cleanDelayTimer(); if (visible !== computedVisible.value) { @@ -636,10 +652,30 @@ export default defineComponent({ changeVisible(false); }; - const handleScroll = throttleByRaf(() => { + const isExceedThreshold = ( + oldPosition: [number, number], + element: HTMLElement + ) => { + const [scrollTop, scrollLeft] = oldPosition; + const { scrollTop: newScrollTop, scrollLeft: newScrollLeft } = element; + return ( + Math.abs(newScrollTop - scrollTop) >= props.scrollToCloseDistance || + Math.abs(newScrollLeft - scrollLeft) >= props.scrollToCloseDistance + ); + }; + + const handleScroll = throttleByRaf((e) => { if (computedVisible.value) { if (props.scrollToClose || configCtx?.scrollToClose) { - changeVisible(false); + const element = e.target as HTMLElement; + if (!scrollPosition) { + scrollPosition = [element.scrollTop, element.scrollLeft]; + } + if (isExceedThreshold(scrollPosition, element)) { + changeVisible(false); + } else { + updatePopupStyle(); + } } else { updatePopupStyle(); } @@ -651,9 +687,15 @@ export default defineComponent({ windowListener = false; }; - const onWindowScroll = throttleByRaf(() => { - changeVisible(false); - removeWindowScroll(); + const onWindowScroll = throttleByRaf((e) => { + const element = e.target.documentElement; + if (!windowScrollPosition) { + windowScrollPosition = [element.scrollTop, element.scrollLeft]; + } + if (isExceedThreshold(windowScrollPosition, element)) { + changeVisible(false); + removeWindowScroll(); + } }); const handleResize = () => {