From 89af82dc2f592fa30a36e317b07fb2107c36ff8a Mon Sep 17 00:00:00 2001
From: g1879 <g1879@qq.com>
Date: Sat, 6 Jul 2024 09:42:45 +0800
Subject: [PATCH] =?UTF-8?q?rect=E5=A2=9E=E5=8A=A0scroll=5Fposition?=
 =?UTF-8?q?=E5=B1=9E=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 DrissionPage/_functions/web.py   | 10 ++++++---
 DrissionPage/_functions/web.pyi  |  4 ++--
 DrissionPage/_units/clicker.py   | 37 ++++++++++++++++----------------
 DrissionPage/_units/clicker.pyi  |  7 +++++-
 DrissionPage/_units/rect.py      | 26 +++++++++++++---------
 DrissionPage/_units/rect.pyi     |  8 ++++++-
 DrissionPage/_units/scroller.py  | 10 ++++-----
 DrissionPage/_units/scroller.pyi |  6 +++---
 8 files changed, 64 insertions(+), 44 deletions(-)

diff --git a/DrissionPage/_functions/web.py b/DrissionPage/_functions/web.py
index c9b1987..8527bb4 100644
--- a/DrissionPage/_functions/web.py
+++ b/DrissionPage/_functions/web.py
@@ -107,12 +107,12 @@ def location_in_viewport(page, loc_x, loc_y):
 
 
 def offset_scroll(ele, offset_x, offset_y):
-    """接收元素及偏移坐标,把坐标滚动到页面中间,返回该点绝对坐标
+    """接收元素及偏移坐标,把坐标滚动到页面中间,返回该点坐标
     有偏移量时以元素左上角坐标为基准,没有时以click_point为基准
     :param ele: 元素对象
     :param offset_x: 偏移量x
     :param offset_y: 偏移量y
-    :return: 绝对坐标
+    :return: 绝对坐标和相对坐标
     """
     loc_x, loc_y = ele.rect.location
     cp_x, cp_y = ele.rect.click_point
@@ -122,7 +122,11 @@ def offset_scroll(ele, offset_x, offset_y):
         clientWidth = ele.owner._run_js('return document.body.clientWidth;')
         clientHeight = ele.owner._run_js('return document.body.clientHeight;')
         ele.owner.scroll.to_location(lx - clientWidth // 2, ly - clientHeight // 2)
-    return lx, ly
+    cl_x, cl_y = ele.rect.viewport_location
+    ccp_x, ccp_y = ele.rect.viewport_click_point
+    cx = cl_x + offset_x if offset_x else ccp_x
+    cy = cl_y + offset_y if offset_y else ccp_y
+    return lx, ly, cx, cy
 
 
 def make_absolute_link(link, baseURI=None):
diff --git a/DrissionPage/_functions/web.pyi b/DrissionPage/_functions/web.pyi
index fdd2d76..2f5e702 100644
--- a/DrissionPage/_functions/web.pyi
+++ b/DrissionPage/_functions/web.pyi
@@ -6,7 +6,7 @@
 @License  : BSD 3-Clause.
 """
 from pathlib import Path
-from typing import Union, Optional
+from typing import Union, Optional, Tuple
 
 from .._base.base import DrissionElement, BaseParser
 from .._elements.chromium_element import ChromiumElement
@@ -24,7 +24,7 @@ def format_html(text: str) -> str: ...
 def location_in_viewport(page: ChromiumBase, loc_x: float, loc_y: float) -> bool: ...
 
 
-def offset_scroll(ele: ChromiumElement, offset_x: float, offset_y: float) -> tuple: ...
+def offset_scroll(ele: ChromiumElement, offset_x: float, offset_y: float) -> Tuple[int, int, int, int]: ...
 
 
 def make_absolute_link(link: str, baseURI: str = None) -> str: ...
diff --git a/DrissionPage/_units/clicker.py b/DrissionPage/_units/clicker.py
index b748ad9..15a6b9d 100644
--- a/DrissionPage/_units/clicker.py
+++ b/DrissionPage/_units/clicker.py
@@ -53,7 +53,7 @@ class Clicker(object):
                 try:
                     self._ele.scroll.to_see()
                     if self._ele.states.is_enabled and self._ele.states.is_displayed:
-                        rect = self._ele.rect.corners
+                        rect = self._ele.rect.viewport_corners
                         can_click = True
                 except NoRectError:
                     if by_js is False:
@@ -90,14 +90,17 @@ class Clicker(object):
                     r = self._ele.owner._run_cdp('DOM.getNodeForLocation', x=int(x), y=int(y),
                                                  includeUserAgentShadowDOM=True, ignorePointerEventsNone=True)
                     if r['backendNodeId'] != self._ele._backend_id:
-                        vx, vy = self._ele.rect.midpoint
+                        vx, vy = self._ele.rect.viewport_midpoint
+                        lx, ly = self._ele.rect._get_page_coord(vx, vy)
                     else:
-                        vx, vy = self._ele.rect.click_point
+                        vx, vy = self._ele.rect.viewport_click_point
+                        lx, ly = self._ele.rect._get_page_coord(vx, vy)
 
                 except CDPError:
-                    vx, vy = self._ele.rect.midpoint
+                    vx, vy = self._ele.rect.viewport_midpoint
+                    lx, ly = self._ele.rect._get_page_coord(vx, vy)
 
-                self._click(vx, vy)
+                self._click(lx, ly, vx, vy)
                 return True
 
         if by_js is not False:
@@ -110,8 +113,7 @@ class Clicker(object):
     def right(self):
         """右键单击"""
         self._ele.owner.scroll.to_see(self._ele)
-        x, y = self._ele.rect.click_point
-        self._click(x, y, 'right')
+        self._click(*self._ele.rect.click_point, *self._ele.rect.viewport_click_point, button='right')
 
     def middle(self, get_tab=True):
         """中键单击,默认返回新出现的tab对象
@@ -119,9 +121,8 @@ class Clicker(object):
         :return: Tab对象或None
         """
         self._ele.owner.scroll.to_see(self._ele)
-        x, y = self._ele.rect.click_point
         curr_tid = self._ele.tab.browser.tab_ids[0]
-        self._click(x, y, 'middle')
+        self._click(*self._ele.rect.click_point, *self._ele.rect.viewport_click_point, button='middle')
         if get_tab:
             tid = self._ele.tab.browser.wait.new_tab(curr_tab=curr_tid)
             if not tid:
@@ -142,8 +143,7 @@ class Clicker(object):
             w, h = self._ele.rect.size
             offset_x = w // 2
             offset_y = h // 2
-        x, y = offset_scroll(self._ele, offset_x, offset_y)
-        self._click(x, y, button, count)
+        self._click(*offset_scroll(self._ele, offset_x, offset_y), button=button, count=count)
 
     def multi(self, times=2):
         """多次点击
@@ -198,19 +198,18 @@ class Clicker(object):
         return (self._ele.tab.browser.get_mix_tab(tid) if self._ele.tab._type == 'MixTab'
                 else self._ele.tab.browser.get_tab(tid))
 
-    def _click(self, loc_x, loc_y, button='left', count=1):
+    def _click(self, loc_x, loc_y, view_x, view_y, button='left', count=1):
         """实施点击
         :param loc_x: 绝对x坐标
         :param loc_y: 绝对y坐标
+        :param view_x: 视口x坐标
+        :param view_y: 视口y坐标
         :param button: 'left' 'right' 'middle'  'back' 'forward'
         :param count: 点击次数
         :return: None
         """
         self._ele.owner.actions.move_to((loc_x, loc_y), duration=.1)
-        sx, sy = self._ele.owner.rect.scrollbar_position
-        x = loc_x - sx
-        y = loc_y - sy
-        self._ele.owner._run_cdp('Input.dispatchMouseEvent', type='mousePressed', x=x,
-                                 y=y, button=button, clickCount=count, _ignore=AlertExistsError)
-        self._ele.owner._run_cdp('Input.dispatchMouseEvent', type='mouseReleased', x=x,
-                                 y=y, button=button, _ignore=AlertExistsError)
+        self._ele.owner._run_cdp('Input.dispatchMouseEvent', type='mousePressed', x=view_x,
+                                 y=view_y, button=button, clickCount=count, _ignore=AlertExistsError)
+        self._ele.owner._run_cdp('Input.dispatchMouseEvent', type='mouseReleased', x=view_x,
+                                 y=view_y, button=button, _ignore=AlertExistsError)
diff --git a/DrissionPage/_units/clicker.pyi b/DrissionPage/_units/clicker.pyi
index 5031e27..af3c7b4 100644
--- a/DrissionPage/_units/clicker.pyi
+++ b/DrissionPage/_units/clicker.pyi
@@ -45,4 +45,9 @@ class Clicker(object):
 
     def for_new_tab(self, by_js: bool = False, timeout: float = 3) -> Union[ChromiumTab, MixTab]: ...
 
-    def _click(self, client_x: float, client_y: float, button: str = 'left', count: int = 1) -> None: ...
+    def _click(self, loc_x: float,
+               loc_y: float,
+               view_x: float,
+               view_y: float,
+               button: str = 'left',
+               count: int = 1) -> None: ...
diff --git a/DrissionPage/_units/rect.py b/DrissionPage/_units/rect.py
index 8250642..f94a94b 100644
--- a/DrissionPage/_units/rect.py
+++ b/DrissionPage/_units/rect.py
@@ -39,20 +39,17 @@ class ElementRect(object):
     @property
     def location(self):
         """返回元素左上角的绝对坐标"""
-        cl = self.viewport_location
-        return self._get_page_coord(cl[0], cl[1])
+        return self._get_page_coord(*self.viewport_location)
 
     @property
     def midpoint(self):
         """返回元素中间点的绝对坐标"""
-        cl = self.viewport_midpoint
-        return self._get_page_coord(cl[0], cl[1])
+        return self._get_page_coord(*self.viewport_midpoint)
 
     @property
     def click_point(self):
         """返回元素接受点击的点的绝对坐标"""
-        cl = self.viewport_click_point
-        return self._get_page_coord(cl[0], cl[1])
+        return self._get_page_coord(*self.viewport_click_point)
 
     @property
     def viewport_location(self):
@@ -96,6 +93,13 @@ class ElementRect(object):
         pr = self._ele.owner._run_js('return window.devicePixelRatio;')
         return (vx + ex) * pr, (ey + vy) * pr
 
+    @property
+    def scroll_position(self):
+        """返回滚动条位置,格式:(x, y)"""
+        r = self._ele._run_js('return this.scrollLeft.toString() + " " + this.scrollTop.toString();')
+        w, h = r.split(' ')
+        return int(w), int(h)
+
     def _get_viewport_rect(self, quad):
         """按照类型返回在可视窗口中的范围
         :param quad: 方框类型,margin border padding
@@ -177,7 +181,7 @@ class TabRect(object):
         return int(w), int(h)
 
     @property
-    def scrollbar_position(self):
+    def scroll_position(self):
         """返回滚动条位置,格式:(x, y)"""
         r = self._get_page_rect()['visualViewport']
         return r['pageX'], r['pageY']
@@ -238,7 +242,9 @@ class FrameRect(object):
         return self._frame.frame_ele.rect.viewport_corners
 
     @property
-    def scrollbar_position(self):
+    def scroll_position(self):
         """返回滚动条位置,格式:(x, y)"""
-        r = self._frame._run_cdp_loaded('Page.getLayoutMetrics')['visualViewport']
-        return r['pageX'], r['pageY']
+        r = self._frame.doc_ele._run_js('return this.documentElement.scrollLeft.toString() + " " '
+                                        '+ this.documentElement.scrollTop.toString();')
+        w, h = r.split(' ')
+        return int(w), int(h)
diff --git a/DrissionPage/_units/rect.pyi b/DrissionPage/_units/rect.pyi
index dff526d..0b2c767 100644
--- a/DrissionPage/_units/rect.pyi
+++ b/DrissionPage/_units/rect.pyi
@@ -56,6 +56,9 @@ class ElementRect(object):
     @property
     def viewport_corners(self) -> Tuple[Tuple[float, float], ...]: ...
 
+    @property
+    def scroll_position(self) -> Tuple[float, float]: ...
+
     def _get_viewport_rect(self, quad: str) -> Union[list, None]: ...
 
     def _get_page_coord(self, x: float, y: float) -> Tuple[float, float]: ...
@@ -90,7 +93,7 @@ class TabRect(object):
     def viewport_size_with_scrollbar(self) -> Tuple[int, int]: ...
 
     @property
-    def scrollbar_position(self) -> Tuple[int, int]: ...
+    def scroll_position(self) -> Tuple[int, int]: ...
 
     def _get_page_rect(self) -> dict: ...
 
@@ -121,3 +124,6 @@ class FrameRect(object):
 
     @property
     def viewport_corners(self) -> Tuple[Tuple[float, float], ...]: ...
+
+    @property
+    def scroll_position(self) -> Tuple[float, float]: ...
diff --git a/DrissionPage/_units/scroller.py b/DrissionPage/_units/scroller.py
index edf0e82..bc5f334 100644
--- a/DrissionPage/_units/scroller.py
+++ b/DrissionPage/_units/scroller.py
@@ -16,11 +16,11 @@ class Scroller(object):
         :param ele: 元素对象
         """
         self._driver = ele
-        self.t1 = self.t2 = 'this'
+        self._t1 = self._t2 = 'this'
         self._wait_complete = False
 
     def _run_js(self, js):
-        js = js.format(self.t1, self.t2, self.t2)
+        js = js.format(self._t1, self._t2, self._t2)
         self._driver._run_js(js)
         self._wait_scrolled()
 
@@ -125,8 +125,8 @@ class PageScroller(Scroller):
         :param owner: 页面对象
         """
         super().__init__(owner)
-        self.t1 = 'window'
-        self.t2 = 'document.documentElement'
+        self._t1 = 'window'
+        self._t2 = 'document.documentElement'
 
     def to_see(self, loc_or_ele, center=None):
         """滚动页面直到元素可见
@@ -165,7 +165,7 @@ class FrameScroller(PageScroller):
         :param frame: ChromiumFrame对象
         """
         super().__init__(frame.doc_ele)
-        self.t1 = self.t2 = 'this.documentElement'
+        self._t1 = self._t2 = 'this.documentElement'
 
     def to_see(self, loc_or_ele, center=None):
         """滚动页面直到元素可见
diff --git a/DrissionPage/_units/scroller.pyi b/DrissionPage/_units/scroller.pyi
index 7e8b7c6..e48c976 100644
--- a/DrissionPage/_units/scroller.pyi
+++ b/DrissionPage/_units/scroller.pyi
@@ -13,8 +13,8 @@ from .._pages.chromium_base import ChromiumBase
 
 class Scroller(object):
     def __init__(self, page_or_ele: Union[ChromiumBase, ChromiumElement]):
-        self.t1: str = ...
-        self.t2: str = ...
+        self._t1: str = ...
+        self._t2: str = ...
         self._driver: Union[ChromiumBase, ChromiumElement] = ...
         self._wait_complete: bool = ...
 
@@ -64,7 +64,7 @@ class FrameScroller(PageScroller):
         :param frame: ChromiumFrame对象
         """
         self._driver = frame.doc_ele
-        self.t1 = self.t2 = 'this.documentElement'
+        self._t1 = self._t2 = 'this.documentElement'
         self._wait_complete = False
 
     def to_see(self, loc_or_ele, center=None):