实现功能:编辑器单个元素右键点击需要在鼠标位置出现菜单项
思路:leaferjs有右键点击事件,事件回调函数参数中有x和y坐标,那么在获得容器dom后可以直接使用绝对定位进行偏移
简化点:右键点击菜单不是出现的固定ui,因此使用多余变量进行维护一直是我不怎么喜欢的,并且声明式使用函数还需要传递各种参数,使用Context又难免耦合,因此我使用指令式组件封装针对x和y的一个组件,与leaferjs和要制作的编辑器无关。(指令式封装参考我的另一篇文章)
ts
import React, { FC, useRef } from 'react';
import { useClickAway } from 'ahooks';
import { createPortal } from 'react-dom';
import { css } from '@emotion/css';
import { appInstance } from '@/runTime';
import { InstructionMountProps } from '@/hooks/useElementsContextHolder';
interface Props {
container: HTMLDivElement
x: number
y: number
node: React.ReactNode
}
const CoordinateDom: FC<Props & InstructionMountProps<null>> = (props) => {
const { container, x, y, node, closeResolve } = props;
const ref = useRef<HTMLDivElement>(null);
useClickAway(() => {
closeResolve();
}, ref, ['click', 'contextmenu']);
return (
createPortal(
<div ref={ref} className={css`position: absolute;left: ${x}px;top: ${y}px`}>
{node}
</div>,
container,
)
);
};
export const openCoordinateDomPromise = (args: Props) => {
return appInstance.getContextHolder().instructionMountPromise<Props, null>({
Component: CoordinateDom,
props: args,
});
};
使用如下:
ts
const rect_1 = Rect.one({
editable: true,
fill: '#FEB027',
cornerRadius: [20, 0, 0, 20],
zIndex: 2,
}, 100, 100);
rect_1.on(PointerEvent.MENU, (e) => {
e.stop();
openCoordinateDomPromise({
container: this.containerRef.current,
x: e.x,
y: e.y,
node: (
<div className={css`width: 100px;height: 100px`}>1fefe24</div>
),
});
});
简单封装如上,如菜单位置临界等问题以后在加相关处理逻辑,也可以使用antd的dropdown,但既然是以学习为目的最好是自己实现。