From b8818d3804b824e7382f99267409a6deaf357eec Mon Sep 17 00:00:00 2001
From: sight <1453017105@qq.com>
Date: Thu, 23 Dec 2021 16:53:39 +0800
Subject: [PATCH] =?UTF-8?q?refactor:(backtop)1.=E8=87=AA=E5=8A=A8=E5=AF=BB?=
 =?UTF-8?q?=E6=89=BE=E5=8F=AF=E6=BB=9A=E5=8A=A8=E7=A5=96=E5=85=88=E5=85=83?=
 =?UTF-8?q?=E7=B4=A0;props=20circular=20=E4=BF=AE=E6=94=B9=E4=B8=BA=20circ?=
 =?UTF-8?q?le?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
 example/docs/zh-CN/components/backtop.md | 16 ++++++------
 src/module/backTop/index.vue             | 31 ++++++++++++++++++------
 2 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/example/docs/zh-CN/components/backtop.md b/example/docs/zh-CN/components/backtop.md
index 7c8c7024..a1564478 100644
--- a/example/docs/zh-CN/components/backtop.md
+++ b/example/docs/zh-CN/components/backtop.md
@@ -1,12 +1,11 @@
 ::: title 基础使用
 
-###### 回到顶部组件的默认样式,通过滑动来查看页面右下角的正方形按钮。
+###### 回到顶部组件的默认样式,lay-backtop 会自动寻找最近的可滚动祖先元素,也可以使用 target 属性指定触发滚动事件的元素,通过滑动来查看页面右下角的正方形按钮。
 
 :::
 ::: demo
-
 
-  
+  
 
 
 :::
@@ -17,18 +16,17 @@
 
 ::: demo
 
-
+
 
   
   
-    
+    
       
     
   
   
   
-    
-    
+    
     
 
 
@@ -53,7 +51,7 @@ export default {
 
 ::: title 滚动容器
 
-###### 通过设置 target 和 position="absolute"参数 ,可对特定容器进行返回顶部操作
+###### 通过设置 target 和 position="absolute"参数,可对特定容器进行返回顶部操作
 
 :::
 ::: demo
@@ -94,7 +92,7 @@ export default {
 | opacity                   | 可选,不透明度                                | number  | 0.0-1.0                    |
 | color                     | 可选,前景颜色                                | string  | #FFFFFF                    |
 | borderRadius              | 可选,添加圆角                                | string  | 2px(默认)                  |
-| circular                  | 可选, 使用圆形按钮                            | boolean | true \| false(默认)
+| circle                    | 可选, 使用圆形按钮                            | boolean | true \| false(默认)
 | 图标样式 |
 | icon                      | 可选,图标类型                                 | string  | layui-icon-top(默认)       |
 | iconSize                  | 可选,图标大小                                 | number  | 30                         |
diff --git a/src/module/backTop/index.vue b/src/module/backTop/index.vue
index 66c8bee8..256a0a1a 100644
--- a/src/module/backTop/index.vue
+++ b/src/module/backTop/index.vue
@@ -43,7 +43,7 @@ export interface LayBacktopProps {
   opacity?: number;
   color?: string;
   borderRadius?: number | string;
-  circular?: boolean;
+  circle?: boolean;
   /**图标样式*/
   icon?: string;
   iconSize?: number;
@@ -54,13 +54,11 @@ export interface LayBacktopProps {
 const props = withDefaults(defineProps(), {
   target: 'window',
   showHeight: 200,
-  right: 30,
-  bottom: 40,
   icon: 'layui-icon-top',
   iconSize: 30,
   iconPrefix: 'layui-icon',
   disabled: false,
-  circular: false,
+  circle: false,
 });
 
 const emit = defineEmits(['click']);
@@ -70,7 +68,7 @@ const scrollTarget = shallowRef(undefined);
 let visible = ref(props.showHeight === 0);
 
 const borderRadius = computed(() => {
-  if (props.circular) return "50%";
+  if (props.circle) return "50%";
   return typeof props.borderRadius === 'number' ? `${props.borderRadius}px` : props.borderRadius;
 });
 
@@ -86,6 +84,7 @@ const styleBacktop = computed(() => {
   }
 });
 
+// TODO 待改进
 const easeInOut = (value: number): number => {
   return value < 0.5 ? 2 * value * value : 1 - 2 * (value - 1) * (value - 1);
 }
@@ -135,7 +134,7 @@ const handlerMouseup = () => {
 // 获取滚动目标元素
 const getScrollTarget = () => {
   if (props.target === 'window') {
-    return window || document.documentElement || document.body;
+    return getScrollParent(backtopRef.value!, false);
   } else {
     const targetElement = document.querySelector(props.target);
     if (!targetElement) throw new Error(`target is not existed: ${props.target}`);
@@ -147,8 +146,23 @@ const getScrollTarget = () => {
     }
     return targetElement;
   }
-};
+}
 
+// 获取距离元素最近的可滚动祖先元素
+const getScrollParent = (element: HTMLElement, includeHidden: boolean): HTMLElement => {
+  let style: CSSStyleDeclaration = getComputedStyle(element);
+  let excludeStaticParent: boolean = style.position === "absolute";
+  let overflowRegex: RegExp = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
+  //if (style.position === "fixed") return document.documentElement || document.body || window;
+  for (let parent: HTMLElement = element; (parent = parent.parentElement!);) {
+    style = getComputedStyle(parent);
+    if (excludeStaticParent && style.position === "static") continue;
+    if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent;
+  }
+  return document.documentElement || document.body || window;
+}
+
+// 节流
 const throttle = (func: Function, wait: number) => {
   var timer: any = null;
   return (...args: any) => {
@@ -158,10 +172,11 @@ const throttle = (func: Function, wait: number) => {
         func.apply(this, args);
       }, wait);
     }
-  };
+  }
 }
 
 onMounted(() => {
+  if (!props.target) return;
   scrollTarget.value = getScrollTarget();
   scrollTarget.value.addEventListener('scroll', throttle(handleScroll, 300));
 });