我正在使用 Fabric.js 和 Pinia 开发 Nuxt3 项目。我想使用 Pinia 商店全局管理 Fabric.js 画布实例。目标是使用商店中的操作将文本添加到画布。
但是,当直接在组件中添加 Fabric.Textbox 时,它可以完美运行(可调整大小和可拖动)。但是,当通过存储操作添加相同的文本框时,文本只能拖动但不能调整大小。
这是我到目前为止所拥有的:
Pinia Store (stores/canvasStore.js):
import { defineStore } from "pinia";
import * as fabric from "fabric";
export const useCanvasStore = defineStore("canvas", {
state: () => ({
canvas: null,
}),
actions: {
setCanvas(canvasInstance) {
this.canvas = canvasInstance;
},
addText(text = "New Text") {
if (!this.canvas) return;
const textbox = new fabric.Textbox(text, {
left: 150,
top: 200,
fontSize: 24,
fill: "black",
width: 200,
selectable: true,
hasControls: true,
});
this.canvas.add(textbox);
this.canvas.setActiveObject(textbox);
},
},
});
Canvas Initialization in Component (CanvasManager.vue):
<template>
<canvas ref="canvasRef" class="border"></canvas>
<AddAddTextButton />
</template>
<script setup>
import { ref, onMounted } from "vue";
import * as fabric from "fabric";
import { useCanvasStore } from "@/stores/canvasStore";
const canvasRef = ref(null);
const canvasStore = useCanvasStore();
onMounted(() => {
const canvas = new fabric.Canvas(canvasRef.value, {
width: 800,
height: 600,
backgroundColor: "#fff",
});
canvasStore.setCanvas(canvas);
});
</script>
Add Text Button (AddTextButton.vue)
<template>
<button @click="addTextToCanvas">Add Text</button>
</template>
<script setup>
import { useCanvasStore } from "@/stores/canvasStore";
const canvasStore = useCanvasStore();
const addTextToCanvas = () => {
canvasStore.addText("Resizable Text");
};
</script>
当通过 store 调用 addText 方法时,文本框被添加但不能调整大小。如果我将相同的 Fabric.Textbox 逻辑直接移动到组件(商店外部),则调整大小效果完美。
工作示例:
<template>
<div class="flex justify-center flex-col items-center">
<canvas ref="canvasRef"></canvas>
<button
@click="addTextBlock"
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
>
Add Text Block
</button>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import * as fabric from "fabric";
const canvasRef = ref(null);
let canvas = null;
onMounted(() => {
canvas = new fabric.Canvas(canvasRef.value, {
width: 500,
height: 500,
backgroundColor: "lightgray",
});
canvas.renderAll();
});
const addTextBlock = () => {
if (!canvas) return;
const textbox = new fabric.Textbox("New Text Bzlocks", {
left: 150,
top: 200,
fontSize: 30,
fill: "black",
width: 200,
});
canvas.add(textbox);
};
</script>
为什么通过 Pinia 商店添加文本框会破坏调整大小和编辑功能?我做错了什么
由于该商店仅在客户端使用(
onMounted
),因此 Nuxt SSR 部分不会产生任何影响。
这些片段并不等效,因为
let canvas
是非反应性对象,而 Pinia 存储是反应性的。让随机对象产生反应总是危险且无效的。反应式代理有开销,并且它们包装的对象不能保证正常工作。
要使
canvas
非反应性,可以将其设置为浅引用,这样可以避免在 state
中展开引用时它产生深度反应:
state: () => ({
canvas: shallowRef(null),
}),
或者,可以将对象标记为非响应式,但这需要在每次属性分配时完成:
setCanvas(canvasInstance) {
this.canvas = markRaw(canvasInstance);
},