拆分图片画布素材拖拽桥接
新增素材拖拽桥接 hook,承接素材拖向画布或文件夹的全局 pointer 监听 恢复认证弹窗 portal 渲染,避免全屏画布遮住账号入口 优化画布背景设置面板,补回当前色、色域、色相、预设、HEX 和恢复默认 补充素材拖拽、认证弹窗和背景面板回归测试并更新文档与 TRACKING
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import { type RefObject, useEffect, useRef } from 'react';
|
||||
|
||||
import type {
|
||||
AssetPointerDragState,
|
||||
EditorAsset,
|
||||
} from './ImageCanvasEditorTypes';
|
||||
|
||||
type CanvasPoint = { x: number; y: number };
|
||||
|
||||
type UseImageCanvasAssetPointerDragBridgeOptions = {
|
||||
assetPointerDragRef: RefObject<AssetPointerDragState | null>;
|
||||
suppressAssetClickRef: RefObject<boolean>;
|
||||
assets: EditorAsset[];
|
||||
resolveAssetFolderId: (clientX: number, clientY: number) => string | null;
|
||||
resolveCanvasPoint: (clientX: number, clientY: number) => CanvasPoint | null;
|
||||
setAssetPointerDrag: (dragState: AssetPointerDragState | null) => void;
|
||||
setUploadDropTarget: (target: 'canvas' | 'assets' | null) => void;
|
||||
updateAssetMoveDropFolder: (folderId: string | null) => void;
|
||||
moveAssetToFolder: (assetId: string, folderId: string) => void;
|
||||
addAssetLayer: (asset: EditorAsset, position?: CanvasPoint) => void;
|
||||
};
|
||||
|
||||
export function useImageCanvasAssetPointerDragBridge({
|
||||
assetPointerDragRef,
|
||||
suppressAssetClickRef,
|
||||
assets,
|
||||
resolveAssetFolderId,
|
||||
resolveCanvasPoint,
|
||||
setAssetPointerDrag,
|
||||
setUploadDropTarget,
|
||||
updateAssetMoveDropFolder,
|
||||
moveAssetToFolder,
|
||||
addAssetLayer,
|
||||
}: UseImageCanvasAssetPointerDragBridgeOptions) {
|
||||
const assetsRef = useRef(assets);
|
||||
const callbacksRef = useRef({
|
||||
resolveAssetFolderId,
|
||||
resolveCanvasPoint,
|
||||
setAssetPointerDrag,
|
||||
setUploadDropTarget,
|
||||
updateAssetMoveDropFolder,
|
||||
moveAssetToFolder,
|
||||
addAssetLayer,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
assetsRef.current = assets;
|
||||
}, [assets]);
|
||||
|
||||
callbacksRef.current = {
|
||||
resolveAssetFolderId,
|
||||
resolveCanvasPoint,
|
||||
setAssetPointerDrag,
|
||||
setUploadDropTarget,
|
||||
updateAssetMoveDropFolder,
|
||||
moveAssetToFolder,
|
||||
addAssetLayer,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const updatePointerDrag = (event: PointerEvent) => {
|
||||
const currentDrag = assetPointerDragRef.current;
|
||||
if (!currentDrag || currentDrag.pointerId !== event.pointerId) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
resolveAssetFolderId: resolveFolder,
|
||||
resolveCanvasPoint: resolvePoint,
|
||||
setAssetPointerDrag: setPointerDrag,
|
||||
setUploadDropTarget: setDropTarget,
|
||||
updateAssetMoveDropFolder: updateDropFolder,
|
||||
} = callbacksRef.current;
|
||||
const distance = Math.hypot(
|
||||
event.clientX - currentDrag.startClientX,
|
||||
event.clientY - currentDrag.startClientY,
|
||||
);
|
||||
const dropFolderId = resolveFolder(event.clientX, event.clientY);
|
||||
const nextDrag: AssetPointerDragState = {
|
||||
...currentDrag,
|
||||
currentClientX: event.clientX,
|
||||
currentClientY: event.clientY,
|
||||
active: currentDrag.active || distance > 4,
|
||||
dropFolderId,
|
||||
};
|
||||
assetPointerDragRef.current = nextDrag;
|
||||
setPointerDrag(nextDrag);
|
||||
setDropTarget(resolvePoint(event.clientX, event.clientY) ? 'canvas' : null);
|
||||
updateDropFolder(dropFolderId);
|
||||
};
|
||||
|
||||
const finishPointerDrag = (event: PointerEvent) => {
|
||||
const currentDrag = assetPointerDragRef.current;
|
||||
if (!currentDrag || currentDrag.pointerId !== event.pointerId) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
resolveAssetFolderId: resolveFolder,
|
||||
resolveCanvasPoint: resolvePoint,
|
||||
setAssetPointerDrag: setPointerDrag,
|
||||
setUploadDropTarget: setDropTarget,
|
||||
updateAssetMoveDropFolder: updateDropFolder,
|
||||
moveAssetToFolder: moveToFolder,
|
||||
addAssetLayer: addLayer,
|
||||
} = callbacksRef.current;
|
||||
const canvasPoint = resolvePoint(event.clientX, event.clientY);
|
||||
const dropFolderId =
|
||||
resolveFolder(event.clientX, event.clientY) ?? currentDrag.dropFolderId;
|
||||
const draggedAsset = assetsRef.current.find(
|
||||
(asset) => asset.id === currentDrag.assetId,
|
||||
);
|
||||
assetPointerDragRef.current = null;
|
||||
setPointerDrag(null);
|
||||
setDropTarget(null);
|
||||
updateDropFolder(null);
|
||||
if (!currentDrag.active || !draggedAsset) {
|
||||
return;
|
||||
}
|
||||
suppressAssetClickRef.current = true;
|
||||
window.setTimeout(() => {
|
||||
suppressAssetClickRef.current = false;
|
||||
}, 0);
|
||||
if (dropFolderId && dropFolderId !== draggedAsset.folderId) {
|
||||
moveToFolder(draggedAsset.id, dropFolderId);
|
||||
return;
|
||||
}
|
||||
if (canvasPoint) {
|
||||
addLayer(draggedAsset, canvasPoint);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('pointermove', updatePointerDrag);
|
||||
window.addEventListener('pointerup', finishPointerDrag);
|
||||
window.addEventListener('pointercancel', finishPointerDrag);
|
||||
return () => {
|
||||
window.removeEventListener('pointermove', updatePointerDrag);
|
||||
window.removeEventListener('pointerup', finishPointerDrag);
|
||||
window.removeEventListener('pointercancel', finishPointerDrag);
|
||||
};
|
||||
}, [assetPointerDragRef, suppressAssetClickRef]);
|
||||
}
|
||||
Reference in New Issue
Block a user