feature(tooltip): implement calculate tooltip position and tooltip arrow position

This commit is contained in:
Sagi 2022-10-13 11:40:56 +08:00
parent 26bbfaea20
commit 4367870061
1 changed files with 67 additions and 5 deletions

View File

@ -1,16 +1,63 @@
import { computed, SetupContext } from "vue";
import { computed, ref, SetupContext } from "vue";
import { TooltipPlacement, TooltipProps } from "../tooltip.props";
export function useTooltipPosition(props: TooltipProps, context: SetupContext) {
type RectDirection = 'top' | 'bottom' | 'right' | 'left';
type RectSizeProperty = 'height' | 'width';
interface RectPosition {
top: number;
left: number;
right: number;
}
export function useTooltipPosition(
props: TooltipProps,
context: SetupContext,
hostBound: DOMRect,
tooltipBound: DOMRect,
arrowBound: DOMRect) {
const space = 2;
const rectifyGutter = 20;
const revertDirectionMap = new Map<RectDirection, RectDirection>(
[['top', 'bottom'], ['bottom', 'top'], ['left', 'right'], ['right', 'left']]
);
const directionBoundMap = new Map<RectDirection, RectSizeProperty>(
[['top', 'height'], ['bottom', 'height'], ['left', 'width'], ['right', 'width']]
);
const placement = ref(props.placement);
const tooltipPlacement = computed(() => {
return props.placement;
});
function revertPlacement(placement: string, direction: RectDirection) {
const revertDirection: RectDirection = revertDirectionMap.get(direction) || direction;
const revertedPlacement = placement.replace(direction, revertDirection);
return revertDirection;
}
function autoRectifyDirection(
placement: TooltipPlacement,
referenceBoundingRect: DOMRect,
arrowReferenceBoundingRect: DOMRect,
tooltipBound: DOMRect,
arrowBound: DOMRect) {
let rectifyPlacement = placement;
const direction = placement.split('-')[0] as RectDirection;
const boundProperty = directionBoundMap.get(direction) as RectSizeProperty;
const boundSize = tooltipBound[boundProperty] + arrowBound[boundProperty];
const referenceBoundSize = Math.abs(arrowReferenceBoundingRect[direction] - referenceBoundingRect[direction]);
if (referenceBoundSize < boundSize) {
rectifyPlacement = revertPlacement(rectifyPlacement, direction);
}
return rectifyPlacement;
}
function calculateTooltipTopPositoin(placement: TooltipPlacement, hostBound: DOMRect, tooltipBound: DOMRect, arrowBound: DOMRect) {
let offsetY = 0;
const verticalAlignment = placement.indexOf('bottom') > -1 ? 'bottom' : (placement.indexOf('top') > -1 ? 'top' : 'middle');
@ -94,9 +141,10 @@ export function useTooltipPosition(props: TooltipProps, context: SetupContext) {
function tryFixOverBound(
placement: TooltipPlacement,
originalPosition: RectPosition,
referenceBoundingRect: DOMRect,
originalBound: DOMRect,
tooltipBound: DOMRect): DOMRect {
tooltipBound: DOMRect): RectPosition {
let fixedLeft = originalBound.left;
let fixedTop = originalBound.top;
if (['top', 'top-left', 'top-right', 'bottom', 'bottom-left', 'bottom-righ'].includes(placement)) {
@ -111,7 +159,7 @@ export function useTooltipPosition(props: TooltipProps, context: SetupContext) {
fixedTop = overTopBound.overBound ?
overTopBound.fixedValue :
(overBottomBound.overBound ? overBottomBound.fixedValue - tooltipBound.height : originalBound.top);
return Object.assign({}, originalBound, { left: fixedLeft, top: fixedTop });
return { left: fixedLeft, top: fixedTop, right: originalPosition.right };
}
function calculateArrowPosition(
@ -129,9 +177,23 @@ export function useTooltipPosition(props: TooltipProps, context: SetupContext) {
const fixedArrowLeft = fixedTooltipBound.left - hostBound.left - hostBound.width * 0.5 + arrowBound.width * 0.5 + offsetX;
const fixedArrowTop = fixedTooltipBound.top - hostBound.top - hostBound.height * 0.5 + arrowBound.height * 0.5 + offsetY;
return Object.assign({}, arrowBound, { left: fixedArrowLeft, top: fixedArrowTop });
return Object.assign({}, arrowBound, { left: Math.abs(fixedArrowLeft), top: Math.abs(fixedArrowTop) });
}
const tooltipPosition = computed<RectPosition>(() => {
const originalTop = calculateTooltipTopPositoin(placement.value, hostBound, tooltipBound, arrowBound);
const originalLeft = calculateTooltipLeftPosition(placement.value, hostBound, tooltipBound, arrowBound);
const originalRight = calculateTooltipRightPosition(placement.value, hostBound, tooltipBound, arrowBound);
const { top, left, right } = tryFixOverBound(
placement.value,
{ top: originalTop, left: originalLeft, right: originalRight },
hostBound,
tooltipBound,
tooltipBound
);
return { top, left, right };
});
/* 计算tooltip最新位置 */
function calculatePosition(hostBound: DOMRect, tooltipBound: DOMRect, arrowBound: DOMRect) {
let top = 0;