背景

最近在投递简历的时候发现,很多HR在哪儿问,你会不会fabric.js啊;我说我暂时没看过,但是应该不难,看哈文档就能上手了。HR跟我说:“那你可能不太适合我们,祝你早日找到工作。”

咦,我这暴脾气…

写了一个简单的教程,大家可以根据到学习学习。

技术栈

我是用vite + vue3 搭建的项目。切记这是vue3,因为这儿在后续会出现一个问题。

绘制一个矩形

绘制一个简单的矩形。

 1
 2<canvas id="canvas" width="400" height="400"></canvas>
 3
 4import { fabric } from 'fabric';
 5
 6canvas.value =  new fabric.Canvas('canvas', {
 7  
 8  
 9  preserveObjectStacking: true,
10}),
11
12const rect: any = new fabric.Rect({
13    left: 100,
14    top: 100,
15    fill: 'red',
16    width: 120,
17    height: 120,
18    z: 10,
19});
20
21
22canvas.value.add(rect);

注意注意注意…

如果你用的是vue3搭建的项目,这样写可能会有问题。我查了很多资料才发现,需要这样修改,这是示例:

 1import { markRaw } from 'vue';
 2import * as fabric from 'fabric';
 3
 4canvas.value = markRaw(
 5    new fabric.Canvas('canvas', {
 6      
 7      
 8      preserveObjectStacking: true,
 9    }),
10);

绘制圆形、三角形等等原理都是一样的,大家可以自己学,并且去看看官方网站的参数,有许多有用的参数比如: angle(角度),opacity(透明度)等等。官方文档-只有英文 | 中文文档,如果你们有更好的可以推荐我修改 | 可以参考文档

图片的渲染方式

既然是canvas那么肯定是要用到图片的,而且可能有很多操作图片的情况,那我们怎么添加图片呢?

【第一种方法:不推荐】

 1
 2<input
 3  id="avatar-upload-input"
 4  style="display: none"
 5  accept="image/png, image/jpeg, image/jpg"
 6  type="file"
 7  @change="changeInput"
 8/>
 9
10
11const changeInput = (event: any) => {
12  
13  let file = event.target.files[0];
14  
15  const reader = new FileReader();
16
17  if (file) {
18    reader.onload = (e: any) => {
19      console.log(e.target.result);
20      const dataUrl = e.target.result;
21      const imgElement = document.createElement('img');
22      imgElement.src = dataUrl;
23      imgElement.onload = () => {
24        const img: any = renderImages(dataUrl);
25        canvas.value.add(img);
26        img.scaleToWidth(400);
27        
28        canvas.value.renderAll();
29      };
30    };
31  }
32  
33  reader.readAsDataURL(file);
34};
35
36const renderImages = (result: string) => {
37  const imgElement = document.createElement('img');
38  imgElement.src = result;
39
40  return new fabric.Image(imgElement, {
41    left: 0,
42    top: 0,
43    angle: 0,
44    opacity: 1,
45    centeredScaling: true, 
46    crossOrigin: 'Anonymous',
47
48    hasControls: false,
49    hasBorders: false,
50    selectable: false,
51    lockMovementX: false,
52    lockMovementY: false,
53    lockScalingX: false,
54    lockScalingY: false,
55  });
56};

【第二种方法:推荐】

通过上传图片获取到的base64然后通过Image加载图片。

 1<input
 2  id="avatar-upload-inputs"
 3  style="display: none"
 4  accept="image/png, image/jpeg, image/jpg"
 5  type="file"
 6  @change="changeInputs"
 7/>
 8
 9const changeInputs = (event: any) => {
10  
11  let file = event.target.files[0];
12  
13  const reader = new FileReader();
14
15  if (file) {
16    reader.onload = (e: any) => {
17      console.log(e.target.result);
18      const dataUrl = e.target.result;
19      fabric.Image.fromURL(dataUrl).then((img: any) => {
20        img.scaleToWidth(400);
21        canvas.value.add(img);
22      });
23    };
24  }
25  
26  reader.readAsDataURL(file);
27};

【第三种方式:推荐】

加载静态图片。

 1const selectProfilePicture = (picture: string) => {
 2  const img: any = canvas.value.getObjects()[0];
 3
 4  if (img) {
 5    canvas.value.remove(img);
 6    fabric.Image.fromURL(getImageUrl(picture)).then((img: any) => {
 7      img.scaleToWidth(400);
 8      canvas.value.add(img);
 9    });
10  } else {
11    fabric.Image.fromURL(getImageUrl(picture)).then((img: any) => {
12      img.scaleToWidth(400);
13      canvas.value.add(img);
14    });
15  }
16};
17
18const getImageUrl = (name: string) => {
19  return new URL(`../images/${name}`, import.meta.url).href;
20}

如果想修改一些参数的话,可以用一下方式。

 1fabric.Image.fromURL(getImageUrl(picture)).then((img: any) => {
 2  img.scaleToWidth(400);
 3  img.set({
 4    left: 100,
 5    top: 100,
 6  });
 7  canvas.value.add(img);
 8});

移出对象元素

 1const img: any = canvas.value.getObjects()[0];
 2
 3if (img) {
 4    canvas.value.remove(img);
 5}

总结

以上是一些基本,操作;后续有时间学习会更新专栏。大家可以持续关注。

个人笔记记录 2021 ~ 2025