174 lines
3.7 KiB
TypeScript
174 lines
3.7 KiB
TypeScript
/*
|
||
* 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.
|
||
*/
|
||
|
||
import {
|
||
type Canvas,
|
||
type FabricObject,
|
||
type Point,
|
||
type TMat2D,
|
||
type Textbox,
|
||
} from 'fabric';
|
||
|
||
import { Mode, type FabricObjectWithCustomProps } from '../typings';
|
||
|
||
/**
|
||
* 缩放到指定点
|
||
*/
|
||
export const zoomToPoint = ({
|
||
canvas,
|
||
point,
|
||
zoomLevel,
|
||
minZoom,
|
||
maxZoom,
|
||
}: {
|
||
point: Point;
|
||
zoomLevel: number;
|
||
canvas?: Canvas;
|
||
minZoom: number;
|
||
maxZoom: number;
|
||
}): TMat2D => {
|
||
// 设置缩放级别的限制
|
||
zoomLevel = Math.max(zoomLevel, minZoom); // 最小缩放级别
|
||
zoomLevel = Math.min(zoomLevel, maxZoom); // 最大缩放级别
|
||
|
||
// 以鼠标位置为中心进行缩放
|
||
canvas?.zoomToPoint(point, zoomLevel);
|
||
return [...(canvas?.viewportTransform as TMat2D)];
|
||
};
|
||
|
||
/**
|
||
* 设置 canvas 视图
|
||
*/
|
||
export const setViewport = ({
|
||
canvas,
|
||
vpt,
|
||
}: {
|
||
vpt: TMat2D;
|
||
canvas?: Canvas;
|
||
}): TMat2D => {
|
||
canvas?.setViewportTransform(vpt);
|
||
canvas?.requestRenderAll();
|
||
return [...(canvas?.viewportTransform as TMat2D)];
|
||
};
|
||
|
||
/**
|
||
* 画布坐标点距离画布左上角距离(单位:px)
|
||
*/
|
||
export const canvasXYToScreen = ({
|
||
canvas,
|
||
scale,
|
||
point,
|
||
}: {
|
||
canvas: Canvas;
|
||
scale: number;
|
||
point: { x: number; y: number };
|
||
}) => {
|
||
// 获取画布的变换矩阵
|
||
const transform = canvas.viewportTransform;
|
||
|
||
// 应用缩放和平移
|
||
const zoomX = transform[0];
|
||
const zoomY = transform[3];
|
||
const translateX = transform[4];
|
||
const translateY = transform[5];
|
||
|
||
const screenX = (point.x * zoomX + translateX) * scale;
|
||
const screenY = (point.y * zoomY + translateY) * scale;
|
||
|
||
// 获取画布在屏幕上的位置
|
||
const x = screenX;
|
||
const y = screenY;
|
||
|
||
// 不做限制
|
||
return {
|
||
x,
|
||
y,
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 得到选中元素的屏幕坐标(左上 tl、右下 br)
|
||
*/
|
||
export const getPopPosition = ({
|
||
canvas,
|
||
scale,
|
||
}: {
|
||
canvas: Canvas;
|
||
scale: number;
|
||
}) => {
|
||
const selection = canvas?.getActiveObject();
|
||
if (canvas && selection) {
|
||
const boundingRect = selection.getBoundingRect();
|
||
|
||
// 左上角坐标
|
||
const tl = {
|
||
x: boundingRect.left,
|
||
y: boundingRect.top,
|
||
};
|
||
|
||
// 右下角坐标
|
||
const br = {
|
||
x: boundingRect.left + boundingRect.width,
|
||
y: boundingRect.top + boundingRect.height,
|
||
};
|
||
|
||
return {
|
||
tl: canvasXYToScreen({ canvas, scale, point: tl }),
|
||
br: canvasXYToScreen({ canvas, scale, point: br }),
|
||
};
|
||
}
|
||
|
||
return {
|
||
tl: {
|
||
x: -9999,
|
||
y: -9999,
|
||
},
|
||
br: {
|
||
x: -9999,
|
||
y: -9999,
|
||
},
|
||
};
|
||
};
|
||
|
||
export const resetElementClip = ({ element }: { element: FabricObject }) => {
|
||
if (!element.clipPath) {
|
||
return;
|
||
}
|
||
|
||
const clipRect = element.clipPath;
|
||
const padding = (element as Textbox).padding ?? 0;
|
||
|
||
const { height, width } = element as FabricObject;
|
||
|
||
const _height = height + padding * 2;
|
||
const _width = width + padding * 2;
|
||
|
||
const newPosition = {
|
||
originX: 'left',
|
||
originY: 'top',
|
||
left: -_width / 2,
|
||
top: -_height / 2,
|
||
height: _height,
|
||
width: _width,
|
||
absolutePositioned: false,
|
||
};
|
||
|
||
clipRect?.set(newPosition);
|
||
};
|
||
|
||
export const isGroupElement = (obj?: FabricObject) =>
|
||
(obj as FabricObjectWithCustomProps)?.customType === Mode.GROUP;
|