本文简介

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


学习 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() 办法返回的对象中获取到 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 拖拽平移画布》


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