Skip to content

Commit 65d54ee

Browse files
authored
fix: Clip overflow calculation & rm position check (#381)
* feat: support clip * chore: add test case
1 parent 9afa96a commit 65d54ee

File tree

4 files changed

+241
-67
lines changed

4 files changed

+241
-67
lines changed

docs/demos/clip.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
title: Clip
3+
nav:
4+
title: Demo
5+
path: /demo
6+
---
7+
8+
<code src="../examples/clip.tsx"></code>

docs/examples/clip.tsx

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/* eslint no-console:0 */
2+
import Trigger from 'rc-trigger';
3+
import React from 'react';
4+
import '../../assets/index.less';
5+
6+
const builtinPlacements = {
7+
top: {
8+
points: ['bc', 'tc'],
9+
overflow: {
10+
adjustX: true,
11+
adjustY: true,
12+
},
13+
offset: [0, 0],
14+
},
15+
bottom: {
16+
points: ['tc', 'bc'],
17+
overflow: {
18+
adjustX: true,
19+
adjustY: true,
20+
},
21+
offset: [0, 0],
22+
},
23+
};
24+
25+
const popupPlacement = 'top';
26+
27+
export default () => {
28+
const [scale, setScale] = React.useState('1');
29+
30+
return (
31+
<React.StrictMode>
32+
<div>
33+
<div
34+
style={{
35+
position: 'fixed',
36+
top: 0,
37+
right: 0,
38+
}}
39+
>
40+
<input
41+
type="number"
42+
value={scale}
43+
onChange={(e) => setScale(e.target.value)}
44+
/>
45+
</div>
46+
47+
<div
48+
style={{
49+
height: 500,
50+
width: 500,
51+
boxSizing: 'border-box',
52+
background: 'rgba(255,0,0,0.1)',
53+
border: '50px solid rgba(0,0,255,0.1)',
54+
overflow: 'clip',
55+
overflowClipMargin: 50,
56+
// overflow: 'hidden',
57+
position: 'relative',
58+
}}
59+
>
60+
<Trigger
61+
arrow
62+
action="click"
63+
popupVisible
64+
popup={
65+
<div
66+
style={{
67+
background: 'yellow',
68+
border: '1px solid blue',
69+
width: 100,
70+
height: 100,
71+
opacity: 0.9,
72+
boxSizing: 'border-box',
73+
}}
74+
>
75+
Popup
76+
</div>
77+
}
78+
getPopupContainer={(n) => n.parentNode as any}
79+
popupStyle={{ boxShadow: '0 0 5px red' }}
80+
popupPlacement={popupPlacement}
81+
builtinPlacements={builtinPlacements}
82+
stretch="minWidth"
83+
>
84+
<span
85+
style={{
86+
background: 'green',
87+
color: '#FFF',
88+
opacity: 0.9,
89+
display: 'flex',
90+
alignItems: 'center',
91+
justifyContent: 'center',
92+
width: 100,
93+
height: 100,
94+
position: 'absolute',
95+
left: 0,
96+
top: 80,
97+
}}
98+
>
99+
Target
100+
</span>
101+
</Trigger>
102+
</div>
103+
</div>
104+
105+
{/* <div style={{ height: '100vh' }} /> */}
106+
</React.StrictMode>
107+
);
108+
};

src/util.ts

Lines changed: 87 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ export function collectScroller(ele: HTMLElement) {
7878
const scrollerList: HTMLElement[] = [];
7979
let current = ele?.parentElement;
8080

81-
const scrollStyle = ['hidden', 'scroll', 'auto'];
81+
const scrollStyle = ['hidden', 'scroll', 'clip', 'auto'];
8282

8383
while (current) {
84-
const { overflowX, overflowY } = getWin(current).getComputedStyle(current);
85-
if (scrollStyle.includes(overflowX) || scrollStyle.includes(overflowY)) {
84+
const { overflowX, overflowY, overflow } =
85+
getWin(current).getComputedStyle(current);
86+
if ([overflowX, overflowY, overflow].some((o) => scrollStyle.includes(o))) {
8687
scrollerList.push(current);
8788
}
8889

@@ -92,8 +93,12 @@ export function collectScroller(ele: HTMLElement) {
9293
return scrollerList;
9394
}
9495

95-
export function toNum(num: number) {
96-
return Number.isNaN(num) ? 1 : num;
96+
export function toNum(num: number, defaultValue = 1) {
97+
return Number.isNaN(num) ? defaultValue : num;
98+
}
99+
100+
function getPxValue(val: string) {
101+
return toNum(parseFloat(val), 0);
97102
}
98103

99104
export interface VisibleArea {
@@ -103,6 +108,28 @@ export interface VisibleArea {
103108
bottom: number;
104109
}
105110

111+
/**
112+
*
113+
*
114+
* **************************************
115+
* * Border *
116+
* * ************************** *
117+
* * * * * *
118+
* * B * * S * B *
119+
* * o * * c * o *
120+
* * r * Content * r * r *
121+
* * d * * o * d *
122+
* * e * * l * e *
123+
* * r ******************** l * r *
124+
* * * Scroll * *
125+
* * ************************** *
126+
* * Border *
127+
* **************************************
128+
*
129+
*/
130+
/**
131+
* Get visible area of element
132+
*/
106133
export function getVisibleArea(
107134
initArea: VisibleArea,
108135
scrollerList?: HTMLElement[],
@@ -115,10 +142,14 @@ export function getVisibleArea(
115142
}
116143

117144
// Skip if static position which will not affect visible area
118-
const { position } = getWin(ele).getComputedStyle(ele);
119-
if (position === 'static') {
120-
return;
121-
}
145+
const {
146+
overflow,
147+
overflowClipMargin,
148+
borderTopWidth,
149+
borderBottomWidth,
150+
borderLeftWidth,
151+
borderRightWidth,
152+
} = getWin(ele).getComputedStyle(ele);
122153

123154
const eleRect = ele.getBoundingClientRect();
124155
const {
@@ -128,20 +159,61 @@ export function getVisibleArea(
128159
clientWidth: eleInnerWidth,
129160
} = ele;
130161

162+
const borderTopNum = getPxValue(borderTopWidth);
163+
const borderBottomNum = getPxValue(borderBottomWidth);
164+
const borderLeftNum = getPxValue(borderLeftWidth);
165+
const borderRightNum = getPxValue(borderRightWidth);
166+
131167
const scaleX = toNum(
132168
Math.round((eleRect.width / eleOutWidth) * 1000) / 1000,
133169
);
134170
const scaleY = toNum(
135171
Math.round((eleRect.height / eleOutHeight) * 1000) / 1000,
136172
);
137173

138-
const eleScrollWidth = (eleOutWidth - eleInnerWidth) * scaleX;
139-
const eleScrollHeight = (eleOutHeight - eleInnerHeight) * scaleY;
140-
const eleRight = eleRect.x + eleRect.width - eleScrollWidth;
141-
const eleBottom = eleRect.y + eleRect.height - eleScrollHeight;
174+
// Original visible area
175+
const eleScrollWidth =
176+
(eleOutWidth - eleInnerWidth - borderLeftNum - borderRightNum) * scaleX;
177+
const eleScrollHeight =
178+
(eleOutHeight - eleInnerHeight - borderTopNum - borderBottomNum) * scaleY;
179+
180+
// Cut border size
181+
const scaledBorderTopWidth = borderTopNum * scaleY;
182+
const scaledBorderBottomWidth = borderBottomNum * scaleY;
183+
const scaledBorderLeftWidth = borderLeftNum * scaleX;
184+
const scaledBorderRightWidth = borderRightNum * scaleX;
185+
186+
// Clip margin
187+
let clipMarginWidth = 0;
188+
let clipMarginHeight = 0;
189+
if (overflow === 'clip') {
190+
const clipNum = getPxValue(overflowClipMargin);
191+
clipMarginWidth = clipNum * scaleX;
192+
clipMarginHeight = clipNum * scaleY;
193+
}
142194

143-
visibleArea.left = Math.max(visibleArea.left, eleRect.x);
144-
visibleArea.top = Math.max(visibleArea.top, eleRect.y);
195+
// Region
196+
const eleLeft = eleRect.x + scaledBorderLeftWidth - clipMarginWidth;
197+
const eleTop = eleRect.y + scaledBorderTopWidth - clipMarginHeight;
198+
199+
const eleRight =
200+
eleLeft +
201+
eleRect.width +
202+
2 * clipMarginWidth -
203+
scaledBorderLeftWidth -
204+
scaledBorderRightWidth -
205+
eleScrollWidth;
206+
207+
const eleBottom =
208+
eleTop +
209+
eleRect.height +
210+
2 * clipMarginHeight -
211+
scaledBorderTopWidth -
212+
scaledBorderBottomWidth -
213+
eleScrollHeight;
214+
215+
visibleArea.left = Math.max(visibleArea.left, eleLeft);
216+
visibleArea.top = Math.max(visibleArea.top, eleTop);
145217
visibleArea.right = Math.min(visibleArea.right, eleRight);
146218
visibleArea.bottom = Math.min(visibleArea.bottom, eleBottom);
147219
});

0 commit comments

Comments
 (0)