Skip to content

Commit 8a9ad64

Browse files
author
Tenny
committed
refactor(Popover): 优化代码
Signed-off-by: Tenny <joel.shu@qq.com>
1 parent 5058450 commit 8a9ad64

File tree

5 files changed

+261
-196
lines changed

5 files changed

+261
-196
lines changed

docs/components/popover.md

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -83,140 +83,140 @@
8383
<CodePreview>
8484
<textarea lang="vue-html">
8585
<div class="popover-p-row">
86-
<nt-popover content="提示内容" placement="topLeft">
86+
<nt-popover content="提示内容" placement="topStart">
8787
<template #trigger>
88-
<nt-button>topLeft</nt-button>
88+
<nt-button>topStart</nt-button>
8989
</template>
9090
</nt-popover>
9191
<nt-popover content="提示内容" placement="top">
9292
<template #trigger>
9393
<nt-button>top</nt-button>
9494
</template>
9595
</nt-popover>
96-
<nt-popover content="提示内容" placement="topRight">
96+
<nt-popover content="提示内容" placement="topEnd">
9797
<template #trigger>
98-
<nt-button>topRight</nt-button>
98+
<nt-button>topEnd</nt-button>
9999
</template>
100100
</nt-popover>
101101
</div>
102102
<div class="popover-p-row">
103-
<nt-popover content="提示内容" placement="leftTop">
103+
<nt-popover content="提示内容" placement="leftStart">
104104
<template #trigger>
105-
<nt-button>leftTop</nt-button>
105+
<nt-button>leftStart</nt-button>
106106
</template>
107107
</nt-popover>
108108
<nt-popover content="提示内容" placement="left">
109109
<template #trigger>
110110
<nt-button>left</nt-button>
111111
</template>
112112
</nt-popover>
113-
<nt-popover content="提示内容" placement="leftBottom">
113+
<nt-popover content="提示内容" placement="leftEnd">
114114
<template #trigger>
115-
<nt-button>leftBottom</nt-button>
115+
<nt-button>leftEnd</nt-button>
116116
</template>
117117
</nt-popover>
118118
</div>
119119
<div class="popover-p-row">
120-
<nt-popover content="提示内容" placement="rightTop">
120+
<nt-popover content="提示内容" placement="rightStart">
121121
<template #trigger>
122-
<nt-button>rightTop</nt-button>
122+
<nt-button>rightStart</nt-button>
123123
</template>
124124
</nt-popover>
125125
<nt-popover content="提示内容" placement="right">
126126
<template #trigger>
127127
<nt-button>right</nt-button>
128128
</template>
129129
</nt-popover>
130-
<nt-popover content="提示内容" placement="rightBottom">
130+
<nt-popover content="提示内容" placement="rightEnd">
131131
<template #trigger>
132-
<nt-button>rightBottom</nt-button>
132+
<nt-button>rightEnd</nt-button>
133133
</template>
134134
</nt-popover>
135135
</div>
136136
<div class="popover-p-row">
137-
<nt-popover content="提示内容" placement="bottomLeft">
137+
<nt-popover content="提示内容" placement="bottomStart">
138138
<template #trigger>
139-
<nt-button>bottomLeft</nt-button>
139+
<nt-button>bottomStart</nt-button>
140140
</template>
141141
</nt-popover>
142142
<nt-popover content="提示内容" placement="bottom">
143143
<template #trigger>
144144
<nt-button>bottom</nt-button>
145145
</template>
146146
</nt-popover>
147-
<nt-popover content="提示内容" placement="bottomRight">
147+
<nt-popover content="提示内容" placement="bottomEnd">
148148
<template #trigger>
149-
<nt-button>bottomRight</nt-button>
149+
<nt-button>bottomEnd</nt-button>
150150
</template>
151151
</nt-popover>
152152
</div>
153153
</textarea>
154154
<template #preview>
155155
<div class="popover-p-row">
156-
<Popover content="提示内容" placement="topLeft">
156+
<Popover content="提示内容" placement="topStart">
157157
<template #trigger>
158-
<Button>topLeft</Button>
158+
<Button>topStart</Button>
159159
</template>
160160
</Popover>
161161
<Popover content="提示内容" placement="top">
162162
<template #trigger>
163163
<Button>top</Button>
164164
</template>
165165
</Popover>
166-
<Popover content="提示内容" placement="topRight">
166+
<Popover content="提示内容" placement="topEnd">
167167
<template #trigger>
168-
<Button>topRight</Button>
168+
<Button>topEnd</Button>
169169
</template>
170170
</Popover>
171171
</div>
172172
<div class="popover-p-row">
173-
<Popover content="提示内容" placement="leftTop">
173+
<Popover content="提示内容" placement="leftStart">
174174
<template #trigger>
175-
<Button>leftTop</Button>
175+
<Button>leftStart</Button>
176176
</template>
177177
</Popover>
178178
<Popover content="提示内容" placement="left">
179179
<template #trigger>
180180
<Button>left</Button>
181181
</template>
182182
</Popover>
183-
<Popover content="提示内容" placement="leftBottom">
183+
<Popover content="提示内容" placement="leftEnd">
184184
<template #trigger>
185-
<Button>leftBottom</Button>
185+
<Button>leftEnd</Button>
186186
</template>
187187
</Popover>
188188
</div>
189189
<div class="popover-p-row">
190-
<Popover content="提示内容" placement="rightTop">
190+
<Popover content="提示内容" placement="rightStart">
191191
<template #trigger>
192-
<Button>rightTop</Button>
192+
<Button>rightStart</Button>
193193
</template>
194194
</Popover>
195195
<Popover content="提示内容" placement="right">
196196
<template #trigger>
197197
<Button>right</Button>
198198
</template>
199199
</Popover>
200-
<Popover content="提示内容" placement="rightBottom">
200+
<Popover content="提示内容" placement="rightEnd">
201201
<template #trigger>
202-
<Button>rightBottom</Button>
202+
<Button>rightEnd</Button>
203203
</template>
204204
</Popover>
205205
</div>
206206
<div class="popover-p-row">
207-
<Popover content="提示内容" placement="bottomLeft">
207+
<Popover content="提示内容" placement="bottomStart">
208208
<template #trigger>
209-
<Button>bottomLeft</Button>
209+
<Button>bottomStart</Button>
210210
</template>
211211
</Popover>
212212
<Popover content="提示内容" placement="bottom">
213213
<template #trigger>
214214
<Button>bottom</Button>
215215
</template>
216216
</Popover>
217-
<Popover content="提示内容" placement="bottomRight">
217+
<Popover content="提示内容" placement="bottomEnd">
218218
<template #trigger>
219-
<Button>bottomRight</Button>
219+
<Button>bottomEnd</Button>
220220
</template>
221221
</Popover>
222222
</div>
@@ -261,7 +261,7 @@
261261
| --- | --- | --- | --- |
262262
| `trigger` | 触发方式 | `hover``click` | `hover` |
263263
| `content` | 显示内容, 也可以通过 `default-slot` 显示 | `string` | - |
264-
| `placement` | 弹出位置 | `topLeft``top``topRight``leftTop``left``leftBottom``rightTop``right``rightBottom``bottomLeft``bottom``bottomRight` | `top` |
264+
| `placement` | 弹出位置 | `topStart``top``topEnd``leftStart``left``leftEnd``rightStart``right``rightEnd``bottomStart``bottom``bottomEnd` | `top` |
265265
| `visible` | 受控模式显示与隐藏 | `boolean` | - |
266266
| `to` | 受控模式下起泡对标元素 | `HTMLElement``string``Ref<HTMLElement>` | - |
267267

src/components/popover/Popover.vue

Lines changed: 55 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@ import { popoverProps } from './constant';
1616
import Clickoutside from '../../directives/clickoutside';
1717
import { round } from 'ph-utils';
1818
import { elem } from 'ph-utils/dom';
19+
import {
20+
getPopoverOffsetX,
21+
getPopoverOffsetY,
22+
impactDetect,
23+
} from '../../utils';
1924
20-
const XM_REG = /^(left)|(right)/;
25+
const POS_REG = /^(left|right|top|bottom)(Start|Center|End)?$/;
2126
2227
function getFirstTriggerVNode(slots: any): VNode | null {
2328
if (slots.trigger != null) {
@@ -26,28 +31,6 @@ function getFirstTriggerVNode(slots: any): VNode | null {
2631
return null;
2732
}
2833
29-
/** 获取某个节点距离容器顶部和左边的距离 */
30-
function getDistanceToContainer(el: HTMLElement) {
31-
let offsetLeft = 0;
32-
let offsetTop = 0;
33-
let top = 0;
34-
let bottom = 0;
35-
let left = 0;
36-
let right = 0;
37-
while (el) {
38-
const rect = el.getBoundingClientRect();
39-
offsetTop += el.offsetTop;
40-
offsetLeft += el.offsetLeft;
41-
top = rect.top;
42-
bottom = rect.bottom;
43-
left = rect.left;
44-
right = rect.right;
45-
el = el.offsetParent as HTMLElement;
46-
}
47-
48-
return { offsetLeft, offsetTop, top, bottom, left, right };
49-
}
50-
5134
export default defineComponent({
5235
props: popoverProps,
5336
inheritAttrs: false,
@@ -99,120 +82,61 @@ export default defineComponent({
9982
show.value = true;
10083
10184
nextTick(() => {
102-
const { offsetLeft, offsetTop, top, left } =
103-
getDistanceToContainer($target);
104-
let topDiff = 0;
105-
let leftDiff = 0;
106-
let tmpPlace = props.placement;
85+
// 获取水平和垂直方向的位置
86+
let mainPos = 'bottom';
87+
let crossPos = '';
88+
const poss = props.placement.match(POS_REG);
89+
if (poss != null) {
90+
mainPos = poss[1];
91+
crossPos = poss[2] || '';
92+
}
93+
let x = 0,
94+
y = 0;
95+
// 获取滚动容器
96+
const container = document.documentElement;
97+
// 滚动条水平方向滚动距离
98+
const scrollLeft = container.scrollLeft;
99+
// 滚动条垂直方向滚动距离
100+
const scrollTop = container.scrollTop;
101+
107102
if ($popover.value != null) {
108103
const popoverRect = $popover.value.getBoundingClientRect();
109104
const targetRect = $target.getBoundingClientRect();
110-
if (props.placement.startsWith('top')) {
111-
topDiff = popoverRect.height + 8;
112-
} else if (props.placement.startsWith('bottom')) {
113-
topDiff = -(targetRect.height + 8);
114-
} else if (
115-
props.placement === 'left' ||
116-
props.placement === 'right'
117-
) {
118-
topDiff = popoverRect.height / 2 - targetRect.height / 2;
119-
} else if (
120-
props.placement === 'leftBottom' ||
121-
props.placement === 'rightBottom'
122-
) {
123-
topDiff = popoverRect.height - targetRect.height;
124-
}
125-
126-
if (props.placement.startsWith('left')) {
127-
leftDiff = popoverRect.width + 8;
128-
} else if (props.placement.startsWith('right')) {
129-
leftDiff = -(targetRect.width + 8);
130-
} else if (
131-
props.placement === 'top' ||
132-
props.placement === 'bottom'
133-
) {
134-
leftDiff = popoverRect.width / 2 - targetRect.width / 2;
135-
} else if (
136-
props.placement === 'bottomRight' ||
137-
props.placement === 'topRight'
138-
) {
139-
leftDiff = popoverRect.width - targetRect.width;
140-
}
141-
142-
const posLeft = offsetLeft - leftDiff;
143-
let xPos = '';
144-
let yPos = '';
145-
146-
if (
147-
offsetTop - topDiff + popoverRect.height >=
148-
Math.abs(top) + window.innerHeight - 15
149-
) {
150-
if (props.placement.match(XM_REG)) {
151-
yPos = 'bottom';
152-
topDiff = popoverRect.height - targetRect.height;
153-
} else {
154-
yPos = 'top';
155-
topDiff = popoverRect.height + 8;
156-
}
157-
}
158-
if (offsetTop - topDiff <= Math.abs(top)) {
159-
if (props.placement.match(XM_REG)) {
160-
yPos = 'top';
161-
topDiff = 0;
162-
} else {
163-
yPos = 'bottom';
164-
topDiff = -(targetRect.height + 8);
165-
}
166-
}
167-
168-
if (posLeft <= Math.abs(left)) {
169-
xPos = 'left';
170-
leftDiff = 0;
171-
}
172-
if (
173-
posLeft + popoverRect.width >=
174-
Math.abs(left) + window.innerWidth - 15
175-
) {
176-
xPos = 'left';
177-
leftDiff = 0;
178-
}
179-
180-
if (xPos === '' && yPos === '') {
181-
tmpPlace = props.placement;
182-
} else {
183-
if (xPos === '') {
184-
if (props.placement.match(/left/i)) {
185-
xPos = 'left';
186-
} else if (props.placement.match(/right/i)) {
187-
xPos = 'right';
188-
}
189-
}
190-
if (yPos === '') {
191-
if (props.placement.match(/top/i)) {
192-
yPos = 'top';
193-
}
194-
if (props.placement.match(/bottom/i)) {
195-
yPos = 'bottom';
196-
}
197-
}
198-
if (props.placement.match(/^(left)|(right)/)) {
199-
const yName =
200-
yPos === '' ? '' : yPos[0].toUpperCase() + yPos.substring(1);
201-
tmpPlace = `${xPos}${yName}` as any;
202-
} else if (props.placement.match(/^(top)|(bottom)/)) {
203-
const xName =
204-
xPos === '' ? '' : xPos[0].toUpperCase() + xPos.substring(1);
205-
tmpPlace = `${yPos}${xName}` as any;
206-
}
207-
}
105+
// 获取水平、垂直方向弹窗坐标点偏移
106+
const yOffset = getPopoverOffsetY(
107+
targetRect,
108+
popoverRect,
109+
mainPos,
110+
crossPos,
111+
);
112+
const xOffset = getPopoverOffsetX(
113+
targetRect,
114+
popoverRect,
115+
mainPos,
116+
crossPos,
117+
);
118+
// 碰撞检测
119+
const impactRes = impactDetect(
120+
targetRect,
121+
popoverRect,
122+
mainPos,
123+
crossPos,
124+
scrollLeft,
125+
scrollTop,
126+
xOffset,
127+
yOffset,
128+
);
129+
x = impactRes.x;
130+
y = impactRes.y;
131+
mainPos = impactRes.mainAlign;
132+
crossPos = impactRes.crossAlign;
208133
}
209-
const posTop = round(offsetTop - topDiff);
210-
const posLeft = round(offsetLeft - leftDiff);
134+
211135
posStyle.value = {
212-
top: `${posTop}px`,
213-
left: `${posLeft}px`,
136+
top: `${round(y)}px`,
137+
left: `${round(x)}px`,
214138
};
215-
place.value = tmpPlace;
139+
place.value = `${mainPos}${crossPos}` as any;
216140
});
217141
}
218142

0 commit comments

Comments
 (0)