2026-03-11 15:34:56 +08:00
|
|
|
|
import { fabric } from "fabric-with-all";
|
|
|
|
|
|
import { OperationType } from "../tools/layerHelper";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 笔刷指示器
|
|
|
|
|
|
* 在画笔模式下显示当前笔刷大小的圆圈指示器
|
|
|
|
|
|
* 使用独立的 fabric.StaticCanvas,完全不干扰主画布的绘制流程
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class BrushIndicator {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 构造函数
|
|
|
|
|
|
* @param {Object} canvas fabric.js画布实例
|
|
|
|
|
|
* @param {Object} options 配置选项
|
|
|
|
|
|
*/
|
|
|
|
|
|
constructor(canvas, options = {}) {
|
|
|
|
|
|
this.canvas = canvas;
|
|
|
|
|
|
this.options = {
|
|
|
|
|
|
strokeColor: options.strokeColor || "rgba(0, 0, 0, 0.5)",
|
|
|
|
|
|
strokeWidth: options.strokeWidth || 1,
|
|
|
|
|
|
fillColor: options.fillColor || "rgba(0, 0, 0, 0.1)",
|
|
|
|
|
|
...options,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 创建独立的静态画布
|
|
|
|
|
|
this.indicatorCanvas = null;
|
|
|
|
|
|
this.staticCanvas = null;
|
|
|
|
|
|
this.indicatorCircle = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 事件处理器
|
|
|
|
|
|
this._mouseEnterHandler = null;
|
|
|
|
|
|
this._mouseLeaveHandler = null;
|
|
|
|
|
|
this._mouseMoveHandler = null;
|
|
|
|
|
|
this._zoomHandler = null;
|
|
|
|
|
|
this._viewportHandler = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 当前状态
|
|
|
|
|
|
this.isVisible = false;
|
|
|
|
|
|
this.currentSize = 10;
|
|
|
|
|
|
this.isEnabled = false;
|
|
|
|
|
|
this.currentPosition = { x: 0, y: 0 };
|
|
|
|
|
|
|
|
|
|
|
|
// 缓存画布状态,用于优化性能
|
|
|
|
|
|
this._lastCanvasState = {
|
|
|
|
|
|
width: null,
|
|
|
|
|
|
height: null,
|
|
|
|
|
|
zoom: null,
|
|
|
|
|
|
viewportTransform: null,
|
|
|
|
|
|
};
|
|
|
|
|
|
this._createStaticCanvas();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建独立的 fabric.StaticCanvas 画布层
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_createStaticCanvas() {
|
|
|
|
|
|
if (!this.canvas.wrapperEl) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 创建独立的 canvas 元素
|
|
|
|
|
|
this.indicatorCanvas = document.createElement("canvas");
|
|
|
|
|
|
this.indicatorCanvas.className = "brush-indicator-canvas";
|
|
|
|
|
|
|
|
|
|
|
|
// 设置样式,使其覆盖在主画布上
|
|
|
|
|
|
Object.assign(this.indicatorCanvas.style, {
|
|
|
|
|
|
position: "absolute",
|
|
|
|
|
|
top: "0",
|
|
|
|
|
|
left: "0",
|
|
|
|
|
|
pointerEvents: "none", // 不阻挡鼠标事件
|
|
|
|
|
|
zIndex: "10", // 确保在最上层
|
|
|
|
|
|
width: "100%",
|
|
|
|
|
|
height: "100%",
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 将指示器画布添加到 fabric.js 画布容器中
|
|
|
|
|
|
this.canvas.wrapperEl.appendChild(this.indicatorCanvas);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建 fabric.StaticCanvas 实例
|
|
|
|
|
|
this.staticCanvas = new fabric.StaticCanvas(this.indicatorCanvas, {
|
|
|
|
|
|
enableRetinaScaling: this.canvas.enableRetinaScaling,
|
|
|
|
|
|
allowTouchScrolling: false,
|
|
|
|
|
|
selection: false,
|
|
|
|
|
|
skipTargetFind: true,
|
|
|
|
|
|
preserveObjectStacking: true,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 同步画布尺寸和变换
|
|
|
|
|
|
this._syncCanvasProperties();
|
|
|
|
|
|
|
|
|
|
|
|
// 监听主画布变化
|
|
|
|
|
|
this._observeCanvasChanges();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 同步画布属性(尺寸、缩放、视口变换等)
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_syncCanvasProperties() {
|
|
|
|
|
|
if (!this.staticCanvas || !this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否为笔刷或橡皮擦模式,非相关模式直接返回
|
|
|
|
|
|
const isBrushMode =
|
|
|
|
|
|
this.canvas.isDrawingMode && this.canvas.freeDrawingBrush;
|
|
|
|
|
|
const isEraserMode =
|
|
|
|
|
|
this.canvas.isDrawingMode &&
|
|
|
|
|
|
this.canvas.freeDrawingBrush &&
|
|
|
|
|
|
this.canvas.freeDrawingBrush.type === "eraser";
|
|
|
|
|
|
|
|
|
|
|
|
const isLiquifyMode = this.canvas.toolId === OperationType.LIQUIFY;// 检查是否在液化模式
|
|
|
|
|
|
if ([isBrushMode, isEraserMode, isLiquifyMode].every(v => !v)) return;
|
|
|
|
|
|
|
|
|
|
|
|
let hasChanges = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查画布尺寸是否变化
|
|
|
|
|
|
const currentWidth = this.canvas.width;
|
|
|
|
|
|
const currentHeight = this.canvas.height;
|
|
|
|
|
|
if (
|
|
|
|
|
|
currentWidth !== this._lastCanvasState.width ||
|
|
|
|
|
|
currentHeight !== this._lastCanvasState.height
|
|
|
|
|
|
) {
|
|
|
|
|
|
this.staticCanvas.setWidth(currentWidth);
|
|
|
|
|
|
this.staticCanvas.setHeight(currentHeight);
|
|
|
|
|
|
this._lastCanvasState.width = currentWidth;
|
|
|
|
|
|
this._lastCanvasState.height = currentHeight;
|
|
|
|
|
|
hasChanges = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查缩放比例是否变化
|
|
|
|
|
|
const currentZoom = this.canvas.getZoom();
|
|
|
|
|
|
if (Math.abs(currentZoom - (this._lastCanvasState.zoom || 0)) > 0.001) {
|
|
|
|
|
|
this.staticCanvas.setZoom(currentZoom);
|
|
|
|
|
|
this._lastCanvasState.zoom = currentZoom;
|
|
|
|
|
|
hasChanges = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查视口变换是否变化
|
|
|
|
|
|
const currentVpt = this.canvas.viewportTransform;
|
|
|
|
|
|
if (
|
|
|
|
|
|
currentVpt &&
|
|
|
|
|
|
!this._areViewportTransformsEqual(
|
|
|
|
|
|
currentVpt,
|
|
|
|
|
|
this._lastCanvasState.viewportTransform
|
|
|
|
|
|
)
|
|
|
|
|
|
) {
|
|
|
|
|
|
this.staticCanvas.setViewportTransform([...currentVpt]);
|
|
|
|
|
|
this._lastCanvasState.viewportTransform = [...currentVpt];
|
|
|
|
|
|
hasChanges = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 只有在有变化时才重新渲染
|
|
|
|
|
|
if (hasChanges) {
|
|
|
|
|
|
this.staticCanvas.renderAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 比较两个视口变换矩阵是否相等
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @param {Array} vpt1 视口变换矩阵1
|
|
|
|
|
|
* @param {Array} vpt2 视口变换矩阵2
|
|
|
|
|
|
* @returns {Boolean} 是否相等
|
|
|
|
|
|
*/
|
|
|
|
|
|
_areViewportTransformsEqual(vpt1, vpt2) {
|
|
|
|
|
|
if (!vpt1 || !vpt2) return false;
|
|
|
|
|
|
if (vpt1.length !== vpt2.length) return false;
|
|
|
|
|
|
|
|
|
|
|
|
const tolerance = 0.001;
|
|
|
|
|
|
for (let i = 0; i < vpt1.length; i++) {
|
|
|
|
|
|
if (Math.abs(vpt1[i] - vpt2[i]) > tolerance) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 监听主画布变化
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_observeCanvasChanges() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 监听缩放变化
|
|
|
|
|
|
this._zoomHandler = () => {
|
|
|
|
|
|
this._syncCanvasProperties();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 监听视口变化
|
|
|
|
|
|
this._viewportHandler = () => {
|
|
|
|
|
|
this._syncCanvasProperties();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 监听画布尺寸变化
|
|
|
|
|
|
this._resizeHandler = () => {
|
|
|
|
|
|
this._syncCanvasProperties();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定事件
|
|
|
|
|
|
this.canvas.on("after:render", this._zoomHandler);
|
|
|
|
|
|
this.canvas.on("canvas:zoomed", this._zoomHandler);
|
|
|
|
|
|
this.canvas.on("viewport:changed", this._viewportHandler);
|
|
|
|
|
|
this.canvas.on("canvas:resized", this._resizeHandler);
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 ResizeObserver 监听 DOM 尺寸变化
|
|
|
|
|
|
if (window.ResizeObserver && this.canvas.upperCanvasEl) {
|
|
|
|
|
|
this.resizeObserver = new ResizeObserver(() => {
|
|
|
|
|
|
this._syncCanvasProperties();
|
|
|
|
|
|
});
|
|
|
|
|
|
this.resizeObserver.observe(this.canvas.upperCanvasEl);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 启用笔刷指示器
|
|
|
|
|
|
* @param {Number} brushSize 笔刷大小
|
|
|
|
|
|
*/
|
|
|
|
|
|
enable(brushSize) {
|
|
|
|
|
|
if (this.isEnabled) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.isEnabled = true;
|
|
|
|
|
|
this.currentSize = brushSize;
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定事件
|
|
|
|
|
|
this._bindEvents();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 禁用笔刷指示器
|
|
|
|
|
|
*/
|
|
|
|
|
|
disable() {
|
|
|
|
|
|
if (!this.isEnabled) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.isEnabled = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏指示器
|
|
|
|
|
|
this.hide();
|
|
|
|
|
|
|
|
|
|
|
|
// 重置颜色配置为默认值
|
|
|
|
|
|
this.options.strokeColor = "";
|
|
|
|
|
|
this.options.fillColor = "";
|
|
|
|
|
|
|
|
|
|
|
|
// 解绑事件
|
|
|
|
|
|
this._unbindEvents();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新笔刷大小
|
|
|
|
|
|
* @param {Number} size 新的笔刷大小
|
|
|
|
|
|
*/
|
|
|
|
|
|
updateSize(size) {
|
|
|
|
|
|
this.currentSize = size;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果指示器正在显示,更新其大小
|
|
|
|
|
|
if (this.isVisible && this.indicatorCircle) {
|
|
|
|
|
|
this.indicatorCircle.set({
|
|
|
|
|
|
radius: size / 2,
|
|
|
|
|
|
});
|
|
|
|
|
|
this.staticCanvas.renderAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新指示器颜色
|
|
|
|
|
|
* @param {String} color 新的颜色值
|
|
|
|
|
|
*/
|
|
|
|
|
|
updateColor(color) {
|
|
|
|
|
|
// 更新配置选项中的颜色
|
|
|
|
|
|
if (color) {
|
|
|
|
|
|
this.options.strokeColor = color;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果指示器正在显示,更新其颜色
|
|
|
|
|
|
if (this.isVisible && this.indicatorCircle) {
|
|
|
|
|
|
this.indicatorCircle.set({
|
|
|
|
|
|
stroke: this.options.strokeColor,
|
|
|
|
|
|
fill: this.options.fillColor,
|
|
|
|
|
|
});
|
|
|
|
|
|
this.staticCanvas.renderAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 显示指示器
|
|
|
|
|
|
* @param {Object} pointer 鼠标位置
|
|
|
|
|
|
*/
|
|
|
|
|
|
show(pointer) {
|
|
|
|
|
|
if (!this.isEnabled || this.isVisible) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.isVisible = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 创建指示器圆圈
|
|
|
|
|
|
this._createIndicatorCircle();
|
|
|
|
|
|
|
|
|
|
|
|
// 更新位置
|
|
|
|
|
|
this.updatePosition(pointer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 隐藏指示器
|
|
|
|
|
|
*/
|
|
|
|
|
|
hide() {
|
|
|
|
|
|
if (!this.isVisible) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.isVisible = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 移除指示器圆圈
|
|
|
|
|
|
if (this.indicatorCircle && this.staticCanvas) {
|
|
|
|
|
|
this.staticCanvas.remove(this.indicatorCircle);
|
|
|
|
|
|
this.indicatorCircle = null;
|
|
|
|
|
|
this.staticCanvas.renderAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新指示器位置
|
|
|
|
|
|
* @param {Object} pointer 鼠标位置
|
|
|
|
|
|
*/
|
|
|
|
|
|
updatePosition(pointer) {
|
|
|
|
|
|
if (!this.isVisible || !this.indicatorCircle) return;
|
|
|
|
|
|
|
|
|
|
|
|
let canvasPointer;
|
|
|
|
|
|
// 如果是原生事件(如 e.e),优先用 clientX/clientY 转换
|
|
|
|
|
|
if (
|
|
|
|
|
|
pointer &&
|
|
|
|
|
|
pointer.clientX !== undefined &&
|
|
|
|
|
|
pointer.clientY !== undefined
|
|
|
|
|
|
) {
|
|
|
|
|
|
// 获取主画布的包裹元素位置
|
|
|
|
|
|
const rect = this.canvas.upperCanvasEl.getBoundingClientRect();
|
|
|
|
|
|
const x = pointer.clientX - rect.left;
|
|
|
|
|
|
const y = pointer.clientY - rect.top;
|
|
|
|
|
|
// 逆变换到画布坐标
|
|
|
|
|
|
canvasPointer = fabric.util.transformPoint(
|
|
|
|
|
|
new fabric.Point(x, y),
|
|
|
|
|
|
fabric.util.invertTransform(this.canvas.viewportTransform)
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 兼容 fabric 的 getPointer
|
|
|
|
|
|
canvasPointer = this.canvas.getPointer(pointer);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新当前位置
|
|
|
|
|
|
this.currentPosition = {
|
|
|
|
|
|
x: canvasPointer.x,
|
|
|
|
|
|
y: canvasPointer.y,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 更新指示器位置
|
|
|
|
|
|
this.indicatorCircle.set({
|
|
|
|
|
|
left: canvasPointer.x,
|
|
|
|
|
|
top: canvasPointer.y,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 优化渲染
|
|
|
|
|
|
this.staticCanvas.requestRenderAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建指示器圆圈对象
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_createIndicatorCircle() {
|
|
|
|
|
|
if (this.indicatorCircle || !this.staticCanvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 创建圆圈对象
|
|
|
|
|
|
this.indicatorCircle = new fabric.Circle({
|
|
|
|
|
|
radius: this.currentSize / 2,
|
|
|
|
|
|
fill: this.options.fillColor,
|
|
|
|
|
|
stroke: this.options.strokeColor,
|
|
|
|
|
|
strokeWidth: this.options.strokeWidth,
|
|
|
|
|
|
originX: "center",
|
|
|
|
|
|
originY: "center",
|
|
|
|
|
|
selectable: false,
|
|
|
|
|
|
evented: false,
|
|
|
|
|
|
excludeFromExport: true,
|
|
|
|
|
|
isTemp: true,
|
|
|
|
|
|
pointer: true,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 添加到静态画布
|
|
|
|
|
|
this.staticCanvas.add(this.indicatorCircle);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 绑定事件处理器
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_bindEvents() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 鼠标进入画布
|
|
|
|
|
|
this._mouseEnterHandler = (e) => {
|
|
|
|
|
|
if (this._shouldShowIndicator()) {
|
|
|
|
|
|
this.show(e.e);
|
|
|
|
|
|
const currentVpt = this.canvas.viewportTransform;
|
|
|
|
|
|
this.staticCanvas.setViewportTransform([...currentVpt]);
|
|
|
|
|
|
this.staticCanvas.renderAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 鼠标离开画布
|
|
|
|
|
|
this._mouseLeaveHandler = () => {
|
|
|
|
|
|
this.hide();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 鼠标移动
|
|
|
|
|
|
this._mouseMoveHandler = (e) => {
|
|
|
|
|
|
if (this._shouldShowIndicator()) {
|
|
|
|
|
|
if (!this.isVisible) {
|
|
|
|
|
|
// this.show(e.e);
|
|
|
|
|
|
this._mouseEnterHandler && this._mouseEnterHandler(e)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// requestIdleCallback(() => {
|
|
|
|
|
|
// this.updatePosition(e.e);
|
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
|
this.updatePosition(e.e);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.hide();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定事件
|
|
|
|
|
|
this.canvas.on("mouse:over", this._mouseEnterHandler);
|
|
|
|
|
|
this.canvas.on("mouse:out", this._mouseLeaveHandler);
|
|
|
|
|
|
this.canvas.on("mouse:move", this._mouseMoveHandler);
|
2026-03-16 11:38:58 +08:00
|
|
|
|
|
|
|
|
|
|
this._observeCanvasChanges();
|
2026-03-11 15:34:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解绑事件处理器
|
|
|
|
|
|
* @private
|
|
|
|
|
|
*/
|
|
|
|
|
|
_unbindEvents() {
|
|
|
|
|
|
if (!this.canvas) return;
|
|
|
|
|
|
// 解绑鼠标事件
|
|
|
|
|
|
if (this._mouseEnterHandler) {
|
|
|
|
|
|
this.canvas.off("mouse:over", this._mouseEnterHandler);
|
|
|
|
|
|
this._mouseEnterHandler = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this._mouseLeaveHandler) {
|
|
|
|
|
|
this.canvas.off("mouse:out", this._mouseLeaveHandler);
|
|
|
|
|
|
this._mouseLeaveHandler = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this._mouseMoveHandler) {
|
|
|
|
|
|
this.canvas.off("mouse:move", this._mouseMoveHandler);
|
|
|
|
|
|
this._mouseMoveHandler = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解绑画布变化事件
|
|
|
|
|
|
if (this._zoomHandler) {
|
|
|
|
|
|
this.canvas.off("after:render", this._zoomHandler);
|
|
|
|
|
|
this.canvas.off("canvas:zoomed", this._zoomHandler);
|
|
|
|
|
|
this._zoomHandler = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this._viewportHandler) {
|
|
|
|
|
|
this.canvas.off("viewport:changed", this._viewportHandler);
|
|
|
|
|
|
this._viewportHandler = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this._resizeHandler) {
|
|
|
|
|
|
this.canvas.off("canvas:resized", this._resizeHandler);
|
|
|
|
|
|
this._resizeHandler = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断是否应该显示指示器
|
|
|
|
|
|
* @private
|
|
|
|
|
|
* @returns {Boolean} 是否显示
|
|
|
|
|
|
*/
|
|
|
|
|
|
_shouldShowIndicator() {
|
|
|
|
|
|
const isDrawingMode = this.canvas.isDrawingMode;// 检查画布是否在绘图模式
|
|
|
|
|
|
const isLiquifyMode = this.canvas.toolId === OperationType.LIQUIFY;// 检查是否在液化模式
|
|
|
|
|
|
// console.log(`笔刷指示器\n绘图模式:${isDrawingMode}\n液化模式:${isLiquifyMode}`)
|
|
|
|
|
|
// 检查画布是否在绘图模式OR液化模式
|
|
|
|
|
|
if ([isDrawingMode, isLiquifyMode].every(v => !v)) return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否有笔刷
|
|
|
|
|
|
if (!this.canvas.freeDrawingBrush) return false;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 销毁指示器
|
|
|
|
|
|
*/
|
|
|
|
|
|
dispose() {
|
|
|
|
|
|
this.disable();
|
|
|
|
|
|
|
|
|
|
|
|
// 解绑画布变化事件
|
|
|
|
|
|
this._unbindEvents();
|
|
|
|
|
|
|
|
|
|
|
|
// 停止监听尺寸变化
|
|
|
|
|
|
if (this.resizeObserver) {
|
|
|
|
|
|
this.resizeObserver.disconnect();
|
|
|
|
|
|
this.resizeObserver = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 销毁静态画布
|
|
|
|
|
|
if (this.staticCanvas) {
|
|
|
|
|
|
this.staticCanvas.dispose();
|
|
|
|
|
|
this.staticCanvas = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 移除指示器画布
|
|
|
|
|
|
if (this.indicatorCanvas && this.indicatorCanvas.parentNode) {
|
|
|
|
|
|
this.indicatorCanvas.parentNode.removeChild(this.indicatorCanvas);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.canvas = null;
|
|
|
|
|
|
this.indicatorCanvas = null;
|
|
|
|
|
|
this.indicatorCircle = null;
|
|
|
|
|
|
this.options = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|