【fabrc.js】 操作鼠标自由绘制图形:矩形、圆形、直线等图形【画图功能】

2023-12-15 19:12:36

前言:

????????在图形编辑器类型的项目当中,通过键盘触发想要绘制的图形类型,然后通过鼠标在fabric画布上自由绘制你想需要的内容。从画基本的矩形、圆形、直线、文本、三角形、折线等功能中,可以扩展出“钢笔path贝塞尔路径”、“多图形组合”、图形合并、图形拆分、解析svg文件(符合要求的文件皆可)进行导入等较为复杂的功能等。

? ? ? ? 虽然上述介绍了很多各个不同的功能,但本篇写的内容仅限于文章标题范围!
? ? ? ? 其他提到的本文肯定不可能都写出来,实际写出来代码就太多了。但是所有的功能都离不开核心的基础地基,打好地基,扩展出对应的功能便轻而易举。

主要涉及功能:

功能对应的全局键盘快捷键、监听画布事件(鼠标按下、鼠标移动、鼠标松开)、初始化图形相关数据并添加进画布、更新画布、计算并更新图形坐标、画布框选功能启用/关闭;

相关要求:

  1. 通过界面按钮或键盘快捷键启用对应图形的绘画模式;(本文所使用的快捷键库若有了解的需要自行搜索我对应文章即可;)
  2. 监听fabric鼠标按下事件、移动事件、弹起事件;
  3. 在鼠标按下事件中创建图形根据不同图形类型声明对应的初始数据
  4. 在鼠标移动事件中实时更新对应图形的相关坐标
  5. 在鼠标弹起事件中结束绘画,恢复相关数据初始值,并根据自身业务需求进行额外操作即可。

其他注意事项:绘画过程中按其他相关键盘快捷键则结束当前图形绘画。当然也不一定都是结束当前绘画执行新快捷键的功能,例如有辅助画正圆 正方的需求处理。所以这些都是根据自身业务需求进行定制功能,思维要灵活。

PS: 本文不对相关功能进行拆分,一个文件里展示完,自己写业务的时候进行相关拆分、封装即可;


<template>
  <div class="cdie" id="cdie">
    <canvas id="c" ref="canvas"></canvas>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, reactive } from "vue";
import { fabric } from "fabric";
import hotkeys from 'hotkeys-js';
window.fabric = fabric
let f = null
let canvas = ref();

let drawType;
function initHotkey() {
  hotkeys('r', () => {
    // 矩形
    drawType = 'r' // 简单写个值,在业务里建议定义枚举类较好。
  });
  hotkeys('l', () => {
    // 直线
    drawType = 'l' // 简单写个值,在业务里建议定义枚举类较好。
  });
  hotkeys('c', () => {
    // 圆形
    drawType = 'c' // 简单写个值,在业务里建议定义枚举类较好。
  });
}
onMounted(() => {
  window.canvas = f = new fabric.Canvas(canvas.value, {
    backgroundColor: "grey",
    width: 1000,
    height: 500,
  });
  initHotkey() // 声明图形绘画的启用快捷键
  initDrawEvent(f) // 创建图形绘画相关事件;
});
function initDrawEvent(canvas) {
  let shape: fabric.Object | null;
  let startPoint: fabric.IPoint; // 记录初始坐标
  canvas.on('mouse:down', (e) => {
    if (e.target || !drawType) {
      // 如果绘画点击在图片上,则不进行绘画
      return;
    }
    if (!shape) {
      f.selection = false;
      startPoint = e.absolutePointer
      switch (drawType) {
        case 'r':
          shape = new fabric.Rect({ //创建对应图形类型
            left: startPoint.x,
            top: startPoint.y,
            width: 0,
            height: 0,
            fill: undefined,
            stroke: 'red'
          });
          break;
        case 'c':
          shape = new fabric.Ellipse({
            left: startPoint.x,
            top: startPoint.y,
            rx: 0,
            ry: 0,
            fill: undefined,
            stroke: 'red'
          });
          break;
        case 'l':
          shape = new fabric.Line([startPoint.x, startPoint.y, startPoint.x, startPoint.y], {
            fill: undefined,
            stroke: 'red'
          });
          break;
        default:
          break;
      }
      if (shape) {
        f.add(shape); //添加图形
        f.requestRenderAll(); //刷新画布
      }
    }
    window.selected = e?.target // 当点击选择到有可选图形时,会获得图形的数据。
  }).on('mouse:move', (e: fabric.IEvent<MouseEvent>) => {
    if (drawType && shape) {
      const p = f.getPointer(e.e) || {
        x: 0,
        y: 0,
      };
      const minX = Math.min(p.x, startPoint.x);
      const minY = Math.min(p.y, startPoint.y);
      let w = Math.abs(p.x - startPoint.x);
      let h = Math.abs(p.y - startPoint.y);
      switch (drawType) {
        case 'r':
          shape.set({
            left: minX,
            top: minY,
            width: w,
            height: h,
          });
          break;
        case 'c':
          shape.set({
            left: minX,
            top: minY,
            rx: w / 2,
            ry: h / 2,
          });
          break;
        case 'l':
          let x1 = startPoint.x;
          let y1 = startPoint.y;
          let x2 = p.x;
          let y2 = p.y;
          console.log(startPoint, p);

          shape.set({
            x1,
            y1,
            x2,
            y2,
          });
          break;
        default:
          break;
      }
      f.requestRenderAll();
    }
  }).on('mouse:up', (e) => {
    if (drawType && shape) {
      shape.setCoords(); // 更新图像坐标;
      drawType = null
      f.selection = true;
      shape = null;
      f.requestRenderAll();  
    }
  })
}

</script>

<style scoped lang="less">
.cdie {
  width: 100%;
  text-align: center;
  display: flex;
  justify-content: center;
}
</style>

文章来源:https://blog.csdn.net/lijiahui_/article/details/135002981
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。