Fabric 画布缩放、拖动、初始化大小

2023-12-13 05:39:38

作为自己项目的基础功能之一,自然是需要第一个回顾记录的了!

1.拖动画布
2.缩放画布
3.监听窗口大小变化,从而初始化画布位置、大小


涉及相关API:键盘快捷键功能、滚轮功能、监听窗口变化、fabric.js相关事件及API;

示例说明:

  1. 拖动画布:按住空格键,然后点击鼠标左键拖动画布;(在操作过程中,需要处理fabric.js图形的相关控制项,避免影响拖动操作!)
  2. 缩放画布:以鼠标当前位置为中心,进行画布内容的整体缩放;
  3. 初始化大小:设定一个舞台与画布之间的大小比例,当窗口大小变化时,对舞台进行居中且缩放至自己所设定的比例大小!

以下代码仅作为功能的完整示例;未进行相关模块划分、封装,实际开发中自行处理这些即可!

<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';
let isDragMode = false
let f = null
function getAllValidObjects() {
  const objs = f.getObjects().filter(e => !e.death)
  return objs;
}
function setDragState(selectable) {
  const alls = getAllValidObjects();
  alls?.forEach((selection) => {
    selection.selectable = selectable;
    selection.evented = selectable;// 禁掉图形事件,避免hover时覆盖鼠标样式
  });
}
function initHotkey() {
  hotkeys('space', {
    keydown: true,
    keyup: true,
  }, (event, handler) => {
    if (event.type === 'keydown') {
      f.discardActiveObject();
      isDragMode = true
      setDragState(false)
      f.selection = false;
      f.defaultCursor = 'grab';
      f.setCursor('grab');
    } else {
      drag = false
      isDragMode = false
      setDragState(true)
      f.selection = true;
      f.defaultCursor = 'default';
      f.setCursor('default');
    }
    event.preventDefault();
  });
}
let canvas = ref();
window.fabric = fabric
onMounted(() => {
  const stageWidth = 400
  const stageHeight = 300
  window.canvas = f = new fabric.Canvas(canvas.value, {
    backgroundColor: "grey",
    minZoom: 0.5, // 最小缩放比例
    maxZoom: 10, // 最大缩放比例
    width: 1000,
    height: 500,
  });
  function resize() {
    f.setWidth(canvas.value.clientWidth);
    f.setHeight(canvas.value.clientHeight);
    const canvasW = f.getWidth();
    const canvasH = f.getHeight();
    const screenRatio = stageWidth / stageHeight; // 计算舞台的宽高比
    const sreenW = canvasW * 0.8; // 计算舞台将要缩放至最大的宽高值
    const sreenH = canvasH * 0.8;
    const ratioW = sreenW / stageWidth; // 计算对应最大宽高值与舞台对应宽高值的比例
    const ratioH = sreenH / stageHeight;
    let absoluteP: fabric.Point;
    let ratio; // 哪个边放大的比例小,就取哪个边的比例,例如高度只需要1.333倍就能达到对应边的80%,则宽度同样放大1.333倍即可。
    if (ratioW < ratioH) { 
      absoluteP = new fabric.Point(-(canvasW - sreenW) / 2, -(canvasH - sreenW / screenRatio) / 2);
      ratio = ratioW;
    } else {
      // absoluteP = new fabric.Point(-(canvasW - sreenH * screenRatio) / 2, -(canvasH - sreenH) / 2); 
      absoluteP = new fabric.Point(-(canvasW - stageWidth * ratioH) / 2, -(canvasH - sreenH) / 2); // 这种计算规则更容易理解点。。。
      ratio = ratioH;
    }
    // absoluteP = new fabric.Point(0,0)
    f.setZoom(ratio); // 设置画布的缩放比例,根据画布元素的左上角为参考点进行缩放的; 此案例为舞台长的那边等比缩放至画布对应边的 80% 大小时的比例。
    f.absolutePan(absoluteP); // 平移视口,根据画布元素左上角作为参考点进行平移,负数xy就是向右下平移。
  }
  window.addEventListener('resize', resize);
  f.upperCanvasEl.addEventListener('wheel', function (e: WheelEvent) {
    const point = f.getPointer(e, true);
    const oldZoom = f.getZoom();
    let newZoom
    if (e.deltaY < 0) {
      // console.log('放大');
      newZoom = oldZoom * 1.1
      newZoom > f.maxZoom && (newZoom = f.maxZoom)
    } else {
      // console.log('缩小');
      newZoom = oldZoom / 1.1;
      newZoom < f.minZoom && (newZoom = f.minZoom)
    }
    f.zoomToPoint(point, newZoom); // 基于画布html元素的左上角的相对坐标进行缩放。
    e.preventDefault();
  });
  initHotkey()
  setInterval(() => {
    f.renderAll(); // 懒得在测试代码renderAll, 统一这里处理了;
  }, 1)
  // 解决矢量图放大过程中模糊 ; 缓存相关,性能好坏影响程度暂不确定;
  fabric.Object.prototype.objectCaching = false;

  // fabric.Object.prototype.originX = fabric.Object.prototype.originY = "center";

  const stage = new fabric.Rect({ // 创建舞台,还可给舞台添加网格,便于用户参考坐标位置等。
    left: 0,
    top: 0,
    width: stageWidth,
    height: stageHeight,
    fill: '#30b980',
    stroke: 'red',
    strokeWidth: 0,
    strokeUniform: true,
    selectable: false,
    evented: false,
    death: true // 随便声明个变量,表示该图形为非活动图形;
  });
  f.add(stage);

  const relativeP = new fabric.Point(
    (1000 - stageWidth) / 2,
    (500 - stageHeight) / 2,
  );
  f.relativePan(relativeP);// 相对当前位置进行视点平移 > 将舞台移动至居中;后续新图形的起始点也从该点位开始。

  let line = new fabric.Line([0, 0, 100, 100], {
    stroke: "blue",
  });
  f.add(line);

  let line2 = new fabric.Line([220, 220, 311, 311], {
    stroke: "red",
  });
  f.add(line2);

  initEvent(f)
  resize()
});
let drag = false
function initEvent(canvas) {
  canvas.on('mouse:down', (IEvent) => {
    // console.log(IEvent);
    // IEvent.absolutePointer 是鼠标点击的相对于起始点的偏移坐标。
    // IEvent.pointer 是相对于fabric画布左上角的偏移坐标。
    window.selected = IEvent?.target // 当点击选择到有可选图形时,会获得图形的数据。
    if (isDragMode) {
      drag = true
    }
  }).on('mouse:up', (e) => {
    drag = false
    if (isDragMode) {
      f.defaultCursor = 'grab';
      f.setCursor('grab');
    }
  }).on('mouse:move', (IEvent: fabric.IEvent<MouseEvent>) => {
    if (isDragMode) {
      if (drag) {
        f.defaultCursor = 'grab';
        f.setCursor('grab');
        const { e } = IEvent
        // console.log(e.movementX, e.movementY); // 表示鼠标相对于上一次触发鼠标事件时的移动距离。
        const point = new fabric.Point(e.movementX, e.movementY);
        f.relativePan(point);
      }
    }
  }).on('mouse:dblclick', (e) => {
  })
}

</script>

<style scoped lang="less">

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

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