使用场景
fixed的侧边栏按钮点击弹出的fixed弹框,与该按钮的中点水平对齐;如果会超出屏幕上下边界,则会往里偏移使其不超出边界。react实现。
对齐
侧边栏按钮里放个高度为1的居中的线,然后线里再flex放窗口内容,就能实现窗口与按钮的中点水平居中
right: 90px;
position: absolute;
z-index: 1500;
height: 1px;
cursor: auto;
.arrow-window__inner{
display: flex;
align-items: center;
height: 1px;
}
防止超出边界
这个css实在没法做,得用js来做。
获取数据
js需要获取这些数据:浏览器高度、弹窗内容高度、弹窗位置。通过这些数据计算出弹窗是否超出边界,然后进行偏移把它挪回浏览器里。
浏览器高度:
import { useWindowSize } from "react-use";
let { height: windowHeight } = useWindowSize();
弹窗高度,用ref.current.clientHeight获取。
const popUpRef = useRef(null);
const [innerHeight, setInnerHeight] = useState(-1);
useEffect(() => {
if (displayOn) {
if (popUpRef && popUpRef.current) {
const _popHeight = popUpRef.current.clientHeight;
setInnerHeight(_popHeight);
} else {
console.log('can not Fix WindowPos', popUpRef);
}
}
}, [displayOn, innerHeight])
...
<div ref={popUpRef}>...</div>
弹窗位置,用ref.currnet.getBoundingClientRect()。我这里是获取中心点的y坐标。
const [yPos, setYPos] = useState(-1);
useEffect(() => {
if (displayOn) {
if (centerRef && centerRef.current) {
const centerRect = centerRef.current.getBoundingClientRect();
const { top: _centerTop, y: _centerY } = centerRect;
setYPos(_centerTop); // IE11没有y,用top代替
} else {
console.log('can not get center pos', centerRef);
}
}
}, [displayOn, windowHeight])
这样3个数值都拿到了。能再计算一下偏移了
// 调整窗口上下位置,防止超出下边界 参数:整个屏幕高度,窗口高度,窗口中心点y坐标
function FixWindowPos(windowHeight, innerHeight, yPos) {
if (innerHeight !== -1 && yPos + innerHeight / 2 > windowHeight) {
const topMove = (-(yPos + innerHeight / 2 - windowHeight) - MIN_PADDING_BOTTOM);
setPopUpTop(topMove);
} else if (innerHeight !== -1 && yPos - innerHeight / 2 <= MIN_PADDING_TOP) {
// 上边界超出了也往下移动
const topMove = (-(yPos - innerHeight / 2) + MIN_PADDING_TOP);
// 往下移如果又导致下边距超出了,就不移
if (!(MIN_PADDING_TOP + innerHeight > windowHeight)) {
setPopUpTop(topMove);
}
}
}
// 计算弹窗位置偏移
useEffect(() => {
if (displayOn) FixWindowPos(windowHeight, innerHeight, yPos);
}, [displayOn, windowHeight, innerHeight, yPos]);
// style:
transform: translateY(${popUpTop}px);
这样就能把弹窗挪回浏览器视野里了。