美文网首页
Fabric.js 拖放元素进画布

Fabric.js 拖放元素进画布

作者: 德育处主任 | 来源:发表于2023-01-10 12:14 被阅读0次

    本文简介

    点赞 + 关注 + 收藏 = 学会了

    学习 Fabric.js,我的建议是看文档不如看 demo。

    本文实现的功能:将元素拖进到画布中并生成对应的图形或图片。

    效果如下图所示:

    file

    思路

    要实现以上效果,需要考虑以下几点:

    1. 元素有拖拽功能。
    2. 能在画布中生成对应的元素。
    3. 画布有可能缩放。
    4. 画布有可能移动。
    5. 画布的位置可能在页面的某处。
    6. 在3和4情况下还能在准确的位置生成元素。

    基于以上几点,我得出以下解法。

    • 解1:要让 HTML 元素具备拖拽功能,只要将 draggable 属性设置为 true 即可。
    • 解2:Fabric.js 创建元素可看 《Fabric.js 从入门到膨胀》的基础图形篇,要创建图片可以看 图片篇
    • 解3:缩放画布我在 《Fabric.js 缩放画布》 里讲解过。
    • 解4:移动画布我在 《Fabric.js 拖拽平移画布》 里讲解过。
    • 解5:画布的左上角不一定在body的左上角,也就是鼠标当前位置可能和画布对应的坐标不一样,需要通过加减法计算一下。
    • 解6:Fabric.js 提供了一个方法可以将鼠标当前坐标转换为画布对应的真实坐标,这个方法叫 restorePointerVpt

    动手

    我分几个步骤慢慢实现上述功能。我知道你很急,但你先别急

    创建画布及元素

    file
    <div class="box">
      <div class="data_list">
        <div class="data_item rect" draggable="true"></div>
        <div class="data_item circle" draggable="true"></div>
        <div class="data_item img" draggable="true"></div>
      </div>
      <canvas id="c" style="border: 1px solid #ccc;"></canvas>
    </div>
    
    <script src="https://unpkg.com/fabric@5.2.1/dist/fabric.min.js"></script>
    <script>
      let canvas = null
    
      // 初始化画布
      function initCanvas() {
        // 创建画布
        canvas = new fabric.Canvas('c', {
          width: 800,
          height: 600
        })
    
        // 矩形
        const rect = new fabric.Rect({
          top: 30,
          left: 30,
          width: 60,
          height: 60,
          fill: 'pink'
        })
        // 将矩形添加到画布中
        canvas.add(rect)
    
        // 接下来3个事件监听的主要功能是移动画布,在按住 alt 后鼠标可以拖拽画布
        // 按下鼠标事件
        canvas.on('mouse:down', function (opt) {
          var evt = opt.e;
          if (evt.altKey === true) {
            this.isDragging = true
            this.lastPosX = evt.clientX
            this.lastPosY = evt.clientY
          }
        })
        // 移动鼠标事件
        canvas.on('mouse:move', function (opt) {
          if (this.isDragging) {
            var e = opt.e;
            var vpt = this.viewportTransform;
            vpt[4] += e.clientX - this.lastPosX
            vpt[5] += e.clientY - this.lastPosY
            this.requestRenderAll()
            this.lastPosX = e.clientX
            this.lastPosY = e.clientY
          }
        })
        // 松开鼠标事件
        canvas.on('mouse:up', function (opt) {
          this.setViewportTransform(this.viewportTransform)
          this.isDragging = false
        })
    
    
        // 监听鼠标滚轮缩放事件,可以缩放画布
        canvas.on('mouse:wheel', opt => {
          const delta = opt.e.deltaY // 滚轮,向上滚一下是 -100,向下滚一下是 100
          let zoom = canvas.getZoom() // 获取画布当前缩放值
          zoom *= 0.999 ** delta
          if (zoom > 20) zoom = 20 // 限制最大缩放级别
          if (zoom < 0.01) zoom = 0.01 // 限制最小缩放级别
    
          // 以鼠标所在位置为原点缩放
          canvas.zoomToPoint(
            { // 关键点
              x: opt.e.offsetX,
              y: opt.e.offsetY
            },
            zoom // 传入修改后的缩放级别
          )
        })
      }
    
      initCanvas()
    </script>
    

    上面的代码使用了 Fabric.js 绑定了页面上的画布,并创造了一个粉红色的矩形。

    按住 alt 后,使用鼠标在画布上可以拖拽画布。

    在画布上滚动鼠标滚轮可以缩放画布。

    左侧的元素列表也将 draggable 属性设置为 true,元素具备拖拽功能了。

    监听元素放进画布

    我们还需要使用一个变量来记录当前拖拽的是什么元素。

    <!-- 省略部分代码 -->
    <div class="data_list">
      <div class="data_item rect" draggable="true" ondragstart="onDragstart('rect')"></div>
      <div class="data_item circle" draggable="true" ondragstart="onDragstart('circle')"></div>
      <div class="data_item img" draggable="true" ondragstart="onDragstart('img')"></div>
    </div>
    
    <script>
    let currentElType = null // 当前要创建的元素类型
    
    // 拖拽开始时就记录当前打算创建的元素类型
    function onDragstart(type) {
      currentType = type
    }
    </script>
    

    前面的代码已经知道拖拽时需要生成什么类型的元素了,现在还需要知道生成到画布的哪个地方(x和y坐标)

    松开鼠标时,需要计算鼠标在画布的坐标。这里的坐标是指画布在页面中的位置转换出来的坐标,而且还要计算画布拖拽和缩放过的情况。

    我的做法是通过 canvas 元素的 getBoundingClientRect() 方法返回的对象中获取到 topleft 两个数据。这两个数据就是 canvas 元素距离页面顶部和左侧的距离。

    然后通过鼠标当前坐标减去 canvas 距离页面顶部或左侧的距离,计算出鼠标点击画布的真实坐标。

    但画布有可能拖拽和缩放,所以需要通过 Fabric.js 提供的 restorePointerVpt() 方法将坐标转换一下。

    于是有了下面的代码。

    // 省略部分代码......
    
    canvas.on('drop', function(opt) {
      // 画布元素距离浏览器左侧和顶部的距离
      let offset = {
        left: canvas.getSelectionElement().getBoundingClientRect().left,
        top: canvas.getSelectionElement().getBoundingClientRect().top
      }
    
        // 鼠标坐标转换成画布的坐标(未经过缩放和平移的坐标)
      let point = {
        x: opt.e.x - offset.left,
        y: opt.e.y - offset.top,
      }
    
      // 转换后的坐标,restorePointerVpt 不受视窗变换的影响
      let pointerVpt = canvas.restorePointerVpt(point)
    })
    

    生成对应的元素

    上面的代码最后得出的 pointerVpt 就是转换后最终的坐标,我们在这个坐标上生成元素即可。

    // 省略部分代码......
    
    canvas.on('drop', function(opt) {
      // 省略部分代码......
    
      switch (currentType) {
        case 'rect':
          createRect(pointerVpt.y, pointerVpt.x)
          break
        case 'circle':
          createCircle(pointerVpt.y, pointerVpt.x)
          break
        case 'img':
          createImg(pointerVpt.y, pointerVpt.x)
        break
      }
      // 创建完元素,把当前操作的元素类型设置回 null
      currentElType = null
    })
    
    // 创建矩形
    function createRect(top, left) {
      canvas.add(new fabric.Rect({
        top,
        left,
        width: 60,
        height: 60,
        fill: 'pink'
      }))
    }
    
    // 创建圆形
    function createCircle(top, left) {
      canvas.add(new fabric.Circle({
        top,
        left,
        radius: 30,
        fill: 'pink'
      }))
    }
    
    // 创建图片元素
    function createImg(top, left) {
      fabric.Image.fromURL('./picture.jpg', oImg => {
        oImg.top = top
        oImg.left = left
        canvas.add(oImg)
      })
    }
    

    代码仓库

    前面都是碎碎念,代码一段一段的。如果需要完整版代码可以打开链接自取。

    Fabric.js 拖拽创建元素

    推荐阅读

    👍《Fabric.js 橡皮擦的用法(包含恢复功能)》

    👍《Fabric.js 监听元素相交(重叠)》

    👍《Fabric.js 自定义子类,创建属于自己的图形》

    👍《Fabric.js 自由绘制圆形》

    👍《Fabric.js 缩放画布》

    👍《Fabric.js 变换视窗》

    👍《Fabric.js 拖拽平移画布》

    点赞 + 关注 + 收藏 = 学会了
    代码仓库

    相关文章

      网友评论

          本文标题:Fabric.js 拖放元素进画布

          本文链接:https://www.haomeiwen.com/subject/pekvcdtx.html