fabricjs画布拖动、画布缩放等方法,我用class进行了封装

1,262 阅读3分钟

背景

在fabricjs中我们想让画布左右拖动并且可以缩放大小,怎么做呢?

这儿借用一位博主的一句话:最早的版本的画布大小调整就是对fabric.js的canvas大小做调整,这样做有2个问题,一是没办法将画布大小保存到json文件中,另外一个问题是缩小放画布时,缩小后画布颜色和背景颜色一致,无法区分画布的边界,效果较差。 最后的实现思路是,使用矩形元素模拟画布区域,fabric.js的canvas大小根据视口DOM的宽高自适应,通过调整矩形元素属性来设置画布到大小和颜色,其他元素通过属相面板修改属性。这样就解决了上边的2个问题。

既然如此那我们基本上大致了解到了其原理;那我们就对其进行封装吧!

绘画一个空白画板

这样我们就可以得到一个空白的画板了,如果不需要拖动和画布缩放的话就可以直接绘画了。

<canvas id="canvas" width="400" height="400"></canvas>
<script lang="ts" setup>
import { onMounted, ref, markRaw } from 'vue';
import { fabric } from 'fabric';
const canvas = ref<fabric.Canvas | null>(null);
onMounted(() => {
  // 获取可视窗口宽高
  const width = document.documentElement.clientWidth;
  const height = document.documentElement.clientHeight;
  console.log(width, height);
  canvas.value = markRaw(
    new fabric.Canvas('canvas', {
      preserveObjectStacking: true,
      backgroundColor: 'grey',
      minZoom: 0.5, // 最小缩放比例
      maxZoom: 10, // 最大缩放比例
      width: width - 300,
      height: height,
    }),
  );
});
</script>

封装calss

初始化

安装事件插件

import hotkeys from 'hotkeys-js';

初始化类:

import { fabric } from 'fabric';
import hotkeys from 'hotkeys-js';

interface IOption {
  width?: number;
  height?: number;
}

class Workspace {
  canvas: fabric.Canvas | null;
  workspace: null | fabric.Rect;
  option: IOption;
  drag: boolean;
  isDragMode: boolean;

  constructor(canvas: fabric.Canvas, option: IOption) {
    console.log('item', option);
    this.init();
  }

  // 初始化
  init() {
  
  }
}

export default Workspace;

初始化画布

通过矩形绘制画板,以后我们的内容都会将在这个画板中进行展示,这儿切记一个点就是如果超出这个画板就不在展示【workspace.clone】,大家可以看下面的使用方法。

// 初始化画布
  initWorkspace() {
    const { width, height } = this.option;
    const workspace = new fabric.Rect({
      fill: 'rgba(255,255,255,1)',
      width,
      height,
      id: 'workspace',
      strokeWidth: 0,
    });
    workspace.set('selectable', false);
    workspace.set('hasControls', false);
    workspace.hoverCursor = 'default';
    // 超出画布不展示
    workspace.clone((cloned: fabric.Rect) => {
      this.canvas.clipPath = cloned;
    });
    this.canvas.add(workspace);
    this.canvas.renderAll();
    this.workspace = workspace;
  }

绑定滚轮事件

当我们绑定滚动事件以后,我在画布上进行滚动的时候就可以看初始化这个画板会进行放大缩小了。你做到这儿的时候可以去试试。

bindWheel() {
    this.canvas.on('mouse:wheel', function (this: fabric.Canvas, opt) {
      // 居中缩放
      const delta = opt.e.deltaY;
      let zoom = this.getZoom();
      zoom *= 0.999 ** delta;
      if (zoom > 20) zoom = 20;
      if (zoom < 0.01) zoom = 0.01;
      const center = this.getCenter();
      this.zoomToPoint(new fabric.Point(center.left, center.top), zoom);
      opt.e.preventDefault();
      opt.e.stopPropagation();
    });
  }

拖动事件

我们将一下代码初始化以后,按空格键+鼠标左键就可以实现拖动了,做到这儿基本上功能就算是完成了。恭喜你学会了。

getAllValidObjects() {
const objs = this.canvas.getObjects().filter((e) => !e.death);
return objs;
}

setDragState(selectable: boolean) {
const alls = this.getAllValidObjects();
alls?.forEach((selection: any) => {
  selection.selectable = selectable;
  selection.evented = selectable;
});
}

// 初始化快捷键
initHotkey() {
hotkeys(
  'space',
  {
    keydown: true,
    keyup: true,
  },
  (event) => {
    if (event.type === 'keydown') {
      this.canvas.discardActiveObject();
      this.isDragMode = true;
      this.setDragState(false);
      this.canvas.selection = false;
      this.canvas.defaultCursor = 'grab';
      this.canvas.setCursor('grab');
    } else {
      this.drag = false;
      this.isDragMode = false;
      this.setDragState(true);
      this.canvas.selection = true;
      this.canvas.defaultCursor = 'default';
      this.canvas.setCursor('default');
    }
    event.preventDefault();
  },
);
}

// 初始化事件
initEvent() {
this.canvas
  .on('mouse:down', (IEvent) => {
    if (this.isDragMode) {
      this.drag = true;
    }
  })
  .on('mouse:up', (e) => {
    this.drag = false;
    if (this.isDragMode) {
      this.canvas.defaultCursor = 'grab';
      this.canvas.setCursor('grab');
    }
  })
  .on('mouse:move', (IEvent: fabric.IEvent<MouseEvent>) => {
    if (this.isDragMode) {
      if (this.drag) {
        this.canvas.defaultCursor = 'grab';
        this.canvas.setCursor('grab');
        const { e } = IEvent;
        // console.log(e.movementX, e.movementY);
        const point = new fabric.Point(e.movementX, e.movementY);
        this.canvas.relativePan(point);
      }
    }
  })
  .on('mouse:dblclick', (e) => {});
}

使用

在vue中引入class类进行初始化画板。

<template>
  <div id="workspace">
    <canvas id="canvas" width="400" height="400"></canvas>
  </div>
</template>

<script lang="ts" setup>
import Workspace from '@/utils/workspace';
import { onMounted, ref, markRaw } from 'vue';
import { fabric } from 'fabric';

const canvas = ref<fabric.Canvas | null>(null);

onMounted(() => {
  // 获取可视窗口宽高
  const width = document.documentElement.clientWidth;
  const height = document.documentElement.clientHeight;
  console.log(width, height);
  canvas.value = markRaw(
    new fabric.Canvas('canvas', {
      // centeredScaling: true, // 全局所有元素都生效
      // uniformScaling: true // 单个元素生效
      preserveObjectStacking: true,
      backgroundColor: 'grey',
      minZoom: 0.5, // 最小缩放比例
      maxZoom: 10, // 最大缩放比例
      width: width - 300,
      height: height,
    }),
  );

  const workspace = new Workspace(canvas.value, {
    width: 900,
    height: 600,
  });
});
</script>

<style scoped lang="less"></style>

学习

fabricjs在网上学习资料还是相对比较少,这个方法封装的还是比较初略,比如窗口缩放画布等比例缩放等等,后续接着更新。