百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

Fabric.js 拖放元素进画布 - 掘金

yuyutoo 2025-05-02 14:26 18 浏览 0 评论

本文简介

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


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

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

效果如下图所示:



思路

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

  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 。



动手

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


创建画布及元素

<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() 方法返回的对象中获取到 top 和 left 两个数据。这两个数据就是 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 拖拽创建元素(
https://gitee.com/k21vin/fabricjs-demo/blob/master/demos/DragCreateElement/DragCreateElement.html)


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

相关推荐

《保卫萝卜2》安卓版大更新 壕礼助阵世界杯

《保卫萝卜2:极地冒险》本周不仅迎来了安卓版本的重大更新,同时将于7月4日本周五,带来“保卫萝卜2”安卓版本世界杯主题活动的火热开启,游戏更新与活动两不误。一定有玩家会问,激萌塔防到底进行了哪些更新?...

儿童手工折纸:胡萝卜,和孩子一起边玩边学carrot

1、准备两张正方形纸,一橙一绿,对折出折痕。2、橙色沿其中一条对角线如图折两三角形。3、把上面三角折平,如图。4、绿色纸折成三角形。5、再折成更小的三角形。6、再折三分之一如图。7、打开折纸,压平中间...

《饥荒》食物代码有哪些(饥荒最新版代码总汇食物篇)

饥荒游戏中,玩家们需要获取各种素材与食物,进行生存。玩家们在游戏中,进入游戏后按“~”键调出控制台使用代码,可以直接获得素材。比如胡萝卜的代码是carrot,玉米的代码是corn,南瓜的代码是pump...

Skyscanner:帮你找到最便宜机票 订票不求人

你喜欢旅行吗?在合适的时间、合适的目的地,来一场说走就走的旅行?机票就是关键!Skyscanner这款免费的手机应用,在几秒钟内比较全球600多家航空公司的航班安排、价格和时刻表,帮你节省金钱和时间。...

小猪佩奇第二季50(小猪佩奇第二季英文版免费观看)

Sleepover过夜Itisnighttime.现在是晚上。...

我在民政局工作的那些事儿(二)(我在民政局上班)

时间到了1997年的秋天,经过一年多的学习和实践,我在处理结婚和离婚的事情更加的娴熟,也获得了领导的器重,所以我在处理平时的工作时也能得心应手。这一天我正在离婚处和同事闲聊,因为离婚处几天也遇不到人,...

夏天来了就你还没瘦?教你不节食13天瘦10斤的哥本哈根减肥法……

好看的人都关注江苏气象啦夏天很快就要来了你是否和苏苏一样身上的肉肉还没做好准备?真是一个悲伤的故事……下面这个哥本哈根减肥法苏苏的同事亲测有效不节食不运动不反弹大家快来一起试试看吧~DAY1...

Pursuing global modernization for peaceful development, mutually beneficial cooperation, prosperity for all

AlocalworkeroperatesequipmentintheChina-EgyptTEDASuezEconomicandTradeCooperationZonei...

Centuries-old tea road regains glory as Belt and Road cooperation deepens

FUZHOU/ST.PETERSBURG,Oct.2(Xinhua)--NestledinthepicturesqueWuyiMountainsinsoutheastChi...

15 THE NUTCRACKERS OF NUTCRACKER LODGE (CONTINUED)胡桃夹子小屋里的胡桃夹子(续篇)

...

AI模型部署:Triton Inference Server模型部署框架简介和快速实践

关键词:...

Ftrace function graph简介(flat function)

引言由于android开发的需要与systrace的普及,现在大家在进行性能与功耗分析时候,经常会用到systrace跟pefetto.而systrace就是基于内核的eventtracing来实...

JAVA历史版本(java各版本)

JAVA发展1.1996年1月23日JDK1.0Java虚拟机SunClassicVM,Applet,AWT2.1997年2月19日JDK1.1JAR文件格式,JDBC,JavaBea...

java 进化史1(java的进阶之路)

java从1996年1月第一个版本诞生,到2022年3月最新的java18,已经经历了27年,整整18个大的版本。很久之前有人就说java要被淘汰,但是java活到现在依然坚挺,不知道java还能活...

学习java第二天(java学完后能做什么)

#java知识#...

取消回复欢迎 发表评论: