feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @coze-arch/max-line-per-function */
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { type InputVariable } from '@coze-workflow/base/types';
|
||||
|
||||
import {
|
||||
REF_VARIABLE_ID_PREFIX,
|
||||
type FabricClickEvent,
|
||||
type FabricObjectWithCustomProps,
|
||||
type FabricSchema,
|
||||
type VariableRef,
|
||||
} from '../typings';
|
||||
import { useViewport } from './use-viewport';
|
||||
import { useSnapMove } from './use-snap-move';
|
||||
import { useRedoUndo } from './use-redo-undo';
|
||||
import { usePosition } from './use-position';
|
||||
import { useMousePosition } from './use-mouse-position';
|
||||
import { useInlineTextAdd } from './use-inline-text-add';
|
||||
import { useInitCanvas } from './use-init-canvas';
|
||||
import { useImagAdd } from './use-img-add';
|
||||
import { useGroup } from './use-group';
|
||||
import { useFreePencil } from './use-free-pencil';
|
||||
import { useDragAdd } from './use-drag-add';
|
||||
import { useCopyPaste } from './use-copy-paste';
|
||||
import { useCommonOperation } from './use-common-operation';
|
||||
import { useCanvasResize } from './use-canvas-resize';
|
||||
import { useCanvasChange } from './use-canvas-change';
|
||||
import { useBackground } from './use-background';
|
||||
import { useAlign } from './use-align';
|
||||
import { useActiveObjectChange } from './use-active-object-change';
|
||||
|
||||
export const useFabricEditor = ({
|
||||
ref,
|
||||
schema: _schema,
|
||||
onChange,
|
||||
maxWidth,
|
||||
maxHeight,
|
||||
startInit,
|
||||
maxZoom = 3,
|
||||
minZoom = 0.3,
|
||||
readonly = false,
|
||||
onShapeAdded,
|
||||
variables,
|
||||
id,
|
||||
helpLineLayerId,
|
||||
onClick,
|
||||
}: {
|
||||
id?: string;
|
||||
helpLineLayerId: string;
|
||||
ref: React.RefObject<HTMLCanvasElement>;
|
||||
schema: FabricSchema;
|
||||
onChange?: (schema: FabricSchema) => void;
|
||||
maxWidth: number;
|
||||
maxHeight: number;
|
||||
startInit: boolean;
|
||||
maxZoom?: number;
|
||||
minZoom?: number;
|
||||
readonly?: boolean;
|
||||
onShapeAdded?: (data: { element: FabricObjectWithCustomProps }) => void;
|
||||
variables?: InputVariable[];
|
||||
onClick?: (e: FabricClickEvent) => void;
|
||||
}) => {
|
||||
const schema: FabricSchema = useMemo(() => {
|
||||
/**
|
||||
* 兼容历史数据
|
||||
* 删除时机,见 apps/fabric-canvas-node-render/utils/replace-ref-value.ts 注释
|
||||
*/
|
||||
if (
|
||||
!_schema?.customVariableRefs &&
|
||||
(_schema?.objects?.filter(d => d.customVariableName)?.length ?? 0) > 0
|
||||
) {
|
||||
const refObjects = _schema?.objects?.filter(d => d.customVariableName);
|
||||
const newRefs: VariableRef[] =
|
||||
refObjects?.map(d => ({
|
||||
variableId: d.customId
|
||||
.replace(`${REF_VARIABLE_ID_PREFIX}-img-`, '')
|
||||
.replace(`${REF_VARIABLE_ID_PREFIX}-text-`, ''),
|
||||
objectId: d.customId,
|
||||
variableName: d.customVariableName as string,
|
||||
})) ?? [];
|
||||
|
||||
return {
|
||||
..._schema,
|
||||
customVariableRefs: newRefs,
|
||||
};
|
||||
}
|
||||
return _schema;
|
||||
}, [_schema]);
|
||||
|
||||
const objectLength = useMemo(() => schema.objects.length, [schema]);
|
||||
|
||||
/**
|
||||
* 最大可添加元素数量限制
|
||||
*/
|
||||
const MAX_OBJECT_LENGTH = 50;
|
||||
const couldAddNewObject = useMemo(
|
||||
() => objectLength < MAX_OBJECT_LENGTH,
|
||||
[objectLength],
|
||||
);
|
||||
|
||||
const { resize, scale } = useCanvasResize({
|
||||
maxWidth,
|
||||
maxHeight,
|
||||
width: schema.width,
|
||||
height: schema.height,
|
||||
});
|
||||
|
||||
// 初始化 fabric canvas
|
||||
const { canvas, loadFromJSON } = useInitCanvas({
|
||||
startInit,
|
||||
ref: ref.current,
|
||||
schema,
|
||||
resize,
|
||||
scale,
|
||||
readonly,
|
||||
onClick,
|
||||
});
|
||||
|
||||
const { viewport, setViewport, zoomToPoint } = useViewport({
|
||||
canvas,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
schema,
|
||||
});
|
||||
|
||||
const { mousePosition } = useMousePosition({ canvas });
|
||||
|
||||
const { group, unGroup } = useGroup({
|
||||
canvas,
|
||||
});
|
||||
|
||||
const {
|
||||
startListen,
|
||||
stopListen,
|
||||
addRefObjectByVariable,
|
||||
updateRefByObjectId,
|
||||
customVariableRefs,
|
||||
} = useCanvasChange({
|
||||
variables,
|
||||
canvas,
|
||||
schema,
|
||||
onChange: json => {
|
||||
onChange?.(json);
|
||||
pushOperation(json);
|
||||
},
|
||||
});
|
||||
|
||||
useSnapMove({ canvas, helpLineLayerId, scale });
|
||||
|
||||
const { copy, paste, disabledPaste } = useCopyPaste({
|
||||
canvas,
|
||||
mousePosition,
|
||||
couldAddNewObject,
|
||||
customVariableRefs,
|
||||
addRefObjectByVariable,
|
||||
variables,
|
||||
});
|
||||
|
||||
const { pushOperation, undo, redo, disabledRedo, disabledUndo, redoUndoing } =
|
||||
useRedoUndo({
|
||||
id,
|
||||
schema,
|
||||
loadFromJSON,
|
||||
startListen,
|
||||
stopListen,
|
||||
onChange,
|
||||
});
|
||||
|
||||
const {
|
||||
activeObjects,
|
||||
activeObjectsPopPosition,
|
||||
setActiveObjectsProps,
|
||||
isActiveObjectsInBack,
|
||||
isActiveObjectsInFront,
|
||||
} = useActiveObjectChange({
|
||||
canvas,
|
||||
scale,
|
||||
});
|
||||
|
||||
const {
|
||||
alignLeft,
|
||||
alignRight,
|
||||
alignCenter,
|
||||
alignTop,
|
||||
alignBottom,
|
||||
alignMiddle,
|
||||
verticalAverage,
|
||||
horizontalAverage,
|
||||
} = useAlign({
|
||||
canvas,
|
||||
selectObjects: activeObjects,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (canvas) {
|
||||
resize(canvas);
|
||||
}
|
||||
}, [resize, canvas]);
|
||||
|
||||
const { backgroundColor, setBackgroundColor } = useBackground({
|
||||
canvas,
|
||||
schema,
|
||||
});
|
||||
|
||||
const {
|
||||
moveActiveObject,
|
||||
removeActiveObjects,
|
||||
moveTo,
|
||||
discardActiveObject,
|
||||
resetWidthHeight,
|
||||
} = useCommonOperation({
|
||||
canvas,
|
||||
});
|
||||
|
||||
const { addImage } = useImagAdd({
|
||||
canvas,
|
||||
onShapeAdded,
|
||||
});
|
||||
|
||||
const { enterDragAddElement, exitDragAddElement } = useDragAdd({
|
||||
canvas,
|
||||
onShapeAdded,
|
||||
});
|
||||
|
||||
const { enterFreePencil, exitFreePencil } = useFreePencil({
|
||||
canvas,
|
||||
});
|
||||
|
||||
const { enterAddInlineText, exitAddInlineText } = useInlineTextAdd({
|
||||
canvas,
|
||||
onShapeAdded,
|
||||
});
|
||||
|
||||
const { allObjectsPositionInScreen } = usePosition({
|
||||
canvas,
|
||||
scale,
|
||||
viewport,
|
||||
});
|
||||
|
||||
return {
|
||||
canvas,
|
||||
canvasSettings: {
|
||||
width: schema.width,
|
||||
height: schema.height,
|
||||
backgroundColor,
|
||||
},
|
||||
state: {
|
||||
viewport,
|
||||
cssScale: scale,
|
||||
activeObjects,
|
||||
activeObjectsPopPosition,
|
||||
objectLength,
|
||||
couldAddNewObject,
|
||||
disabledUndo,
|
||||
disabledRedo,
|
||||
redoUndoing,
|
||||
disabledPaste,
|
||||
isActiveObjectsInBack,
|
||||
isActiveObjectsInFront,
|
||||
canvasWidth: canvas?.getElement().getBoundingClientRect().width,
|
||||
canvasHeight: canvas?.getElement().getBoundingClientRect().height,
|
||||
customVariableRefs,
|
||||
allObjectsPositionInScreen,
|
||||
},
|
||||
sdk: {
|
||||
discardActiveObject,
|
||||
setActiveObjectsProps,
|
||||
setBackgroundColor,
|
||||
moveToFront: () => {
|
||||
moveTo('front');
|
||||
},
|
||||
moveToBackend: () => {
|
||||
moveTo('backend');
|
||||
},
|
||||
moveToFrontOne: () => {
|
||||
moveTo('front-one');
|
||||
},
|
||||
moveToBackendOne: () => {
|
||||
moveTo('backend-one');
|
||||
},
|
||||
zoomToPoint,
|
||||
setViewport,
|
||||
moveActiveObject,
|
||||
removeActiveObjects,
|
||||
enterDragAddElement,
|
||||
exitDragAddElement,
|
||||
enterFreePencil,
|
||||
exitFreePencil,
|
||||
enterAddInlineText,
|
||||
exitAddInlineText,
|
||||
addImage,
|
||||
undo,
|
||||
redo,
|
||||
copy,
|
||||
paste,
|
||||
group,
|
||||
unGroup,
|
||||
alignLeft,
|
||||
alignRight,
|
||||
alignCenter,
|
||||
alignTop,
|
||||
alignBottom,
|
||||
alignMiddle,
|
||||
verticalAverage,
|
||||
horizontalAverage,
|
||||
resetWidthHeight,
|
||||
addRefObjectByVariable,
|
||||
updateRefByObjectId,
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user