在vue3中使用fabricjs导致图形无法缩放的问题解决
情况说明
首先是官网:http://fabricjs.com/
首先插件版本:使用的是fabric: 5.3.0 vue: 3.3.4
首先我是使用组合API,在外部文件中调用fabricjs。
先说明一下文件目录结构:
edit目录
|- edit.vue 以下简称vue文件
|-useFabric.js 以下简称js文件
0、在js文件onmounted中使用响应式变量
<canvas id="c0"></canvas>
import {fabric} from "fabric";
import { ref, onMounted } from 'vue';
const fabricCanvas = ref(null);
onMounted(() => {
fabricCanvas.value = new fabric.Canvas('c0', {
backgroundColor: 'purple',
width: 600,
height: 600
});
fabricCanvas.value.add(
new fabric.Circle({
radius: 30,
fill: '#f55',
top: 100,
left: 100 })
);
console.log('c0', fabricCanvas.value)
})
随后会发现在页面中,可以拖拽图形,但是无法对图形进行缩放、旋转等操作。
已知上面这种情况会不正常,随后我分别做了如下测试。
1、在js文件onmounted中使用普通变量
<canvas id="c1"></canvas>
onMounted(() => {
let canvas = new fabric.Canvas('c1', {
backgroundColor: 'black',
width: 600,
height: 100
});
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
console.log('c1', canvas)
});
结论:发现拖拽、缩放旋转各种功能正常。
2、在vue文件中初始化,并在mounted中使用
<canvas id="canvas-box"></canvas>
<canvas id="c2"></canvas>
<canvas id="c3"></canvas>
data() {
return {
c2: null
}
},
mounted() {
let canvasbox = new fabric.Canvas('canvas-box', {
backgroundColor: 'yellow',
width: 600,
height: 100
});
canvasbox.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
console.log('canvasbox', canvasbox)
this.c2 = new fabric.Canvas('c2', {
backgroundColor: 'blue',
width: 600,
height: 100
});
this.c2.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
console.log('c2', this.c2)
this.c3 = new fabric.Canvas('c3', {
backgroundColor: 'pink',
width: 600,
height: 100
});
this.c3.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
console.log('c3', this.c3)
},
结论:canvasbox、c3中可以正常缩放、旋转,c2则不行。
问题解决
我们先来看一下打印出来的变量:
可以明显的看到,无法旋转的两种情况,c2和c0他们都是proxy类型!既是vue3的响应式数据,我随后百度了一下,发现我这个不是个例:
使用的是 fabric: 5.3.0 vue: 3.3.4
问题:在项目中,我渲染的矩形和多边形导致无法缩放,只能移动;
新画的没问题,重新渲染就有问题;
【
将[new fabric.Canvas()]对象赋值给vue3的reactive,
四周所有的拖拽点失效。
】
最后的解决办法就是:
Fabricjs 不喜欢将 Canvas 转换为代理。解决它的一种方法是使用全局变量而不是使用数据
解决方法就是:我们不要使用响应式数据存放fabricjs的实例化对象,要用普通的变量或者未在data()中注册的this.xx变量进行存放。
或者使用Markraw
import { markRaw } from 'vue';
...
this.canvas = markRaw(new fabric.Canvas(c, {
width: c.clientWidth,
height: c.clientHeight
}));
...
this.canvas.add(markRaw(new fabric.Rect({
top: 20,
left: 20,
width: 40,
height: 40,
fill: color
})));
markRaw
作用:将一个对象标记为不可以被转化为代理对象。返回该对象本身。
应用场景:
1.有些值不应被设置成响应式时,例如复杂的第三方类库等
2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
3.在动态渲染组件的时候我们就可以使用 markRaw 包裹。
具体参考:https://www.cnblogs.com/IwishIcould/p/16257095.html
预防英文链接失效,现原帖保存一下
Fabricjs, selection handles shown but not clickable before selected together with other shapes
问题:
I'm trying to develop a vue.js component to draw shapes with fabricjs. After a rectangle been drawn, it is selectable, but can't be resized or rotated with the selection handle, which is not what I was expecting. I think it has something to do with vue.js 3, because everything works with vue.js 2.
Inthis demo at JSFiddle, corner handles won't work on rectangles, untill after you have selected the rectangle together with other shapes.
<div id="app">
<canvas ref="c"></canvas>
<div>
<button type="button" @click="add('red')">
Add
</button>
Select the blue rectangle. The corner handles are visible but not working.
Now, click "add“, then select both rectangles. Then just select any single item, now the corner handles are working.
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.2.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
var vm = Vue.createApp({
data: () => ({
canvas: null
}),
mounted: function() {
var c = this.$refs.c
this.canvas = new fabric.Canvas(c, {
width: c.clientWidth,
height: c.clientHeight
});
this.add('blue')
},
methods: {
add(color) {
this.canvas.add(new fabric.Rect({
top: 20,
left: 20,
width: 40,
height: 40,
fill: color
}))
}
}
}).mount('#app');
// Select the blue rectangle. The corner handles are visible but not working.
// Now, click "add", then select both rectangles.
// Then just select any single item, now the corner handles are working.
How do I work around this while sticking to vue.js 3?
回答:
Fabricjs does not like having the canvas converted to a proxy. One way to get around it is to use a global variable instead of using the data. You can also just removecanvas
from thedata()
declaration, which will make it still accessible throughout the template and code, but not make it reactive.
var vm = Vue.createApp({
mounted: function() {
var c = this.$refs.c
this.canvas = new fabric.Canvas(c, {
width: c.clientWidth,
height: c.clientHeight
});
this.add('blue')
},
methods: {
add(color) {
this.canvas.add(new fabric.Rect({
top: 20,
left: 20,
width: 40,
height: 40,
fill: color
}))
}
}
}).mount('#app');
<div id="app">
<canvas ref="c"></canvas>
<div>
<button type="button" @click="add('red')">
Add
</button>
Select the blue rectangle. The corner handles are visible but not working.
Now, click "add“, then select both rectangles. Then just select any single item, now the corner handles are working.
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.1.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.prod.js"></script>
回答
You can usetoRaw()
. The function can change proxy object to common object.
add(color) {
toRaw(this.canvas).add(new fabric.Rect({
top: 20,
left: 20,
width: 40,
height: 40,
fill: color
}))
}
回答
Alternatively, you can use Vue 3'smarkRaw
.
import { markRaw } from 'vue';
...
this.canvas = markRaw(new fabric.Canvas(c, {
width: c.clientWidth,
height: c.clientHeight
}));
...
this.canvas.add(markRaw(new fabric.Rect({
top: 20,
left: 20,
width: 40,
height: 40,
fill: color
})));