Files
aida_front/src/component/Canvas/CanvasEditor/managers/BrushIndicator.js

435 lines
10 KiB
JavaScript
Raw Normal View History

import { fabric } from "fabric-with-all";
/**
* 笔刷指示器
* 在画笔模式下显示当前笔刷大小的圆圈指示器
* 使用独立的 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._createStaticCanvas();
// 事件处理器
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 };
}
/**
* 创建独立的 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;
// 同步画布尺寸
this.staticCanvas.setWidth(this.canvas.width);
this.staticCanvas.setHeight(this.canvas.height);
// 同步缩放
this.staticCanvas.setZoom(this.canvas.getZoom());
// 同步视口变换
const vpt = this.canvas.viewportTransform;
if (vpt) {
this.staticCanvas.setViewportTransform([...vpt]);
}
// 同步背景色(可选,通常保持透明)
// this.staticCanvas.backgroundColor = 'transparent';
// 重新渲染
this.staticCanvas.renderAll();
}
/**
* 监听主画布变化
* @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;
// 使用主画布的坐标转换
const canvasPointer = this.canvas.getPointer(pointer);
// 更新当前位置
this.currentPosition = {
x: canvasPointer.x,
y: canvasPointer.y,
};
// 更新指示器位置
this.indicatorCircle.set({
left: canvasPointer.x,
top: canvasPointer.y,
});
// 重新渲染静态画布
// this.staticCanvas.renderAll();
// 优化渲染
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);
}
};
// 鼠标离开画布
this._mouseLeaveHandler = () => {
this.hide();
};
// 鼠标移动
this._mouseMoveHandler = (e) => {
if (this._shouldShowIndicator()) {
if (!this.isVisible) {
this.show(e.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);
}
/**
* 解绑事件处理器
* @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() {
// 检查画布是否在绘图模式
if (!this.canvas.isDrawingMode) 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;
}
}