Fabric.js 拖放元素進畫布

来源:https://www.cnblogs.com/k21vin/archive/2023/01/10/17039823.html
-Advertisement-
Play Games

本文簡介 點贊 + 關註 + 收藏 = 學會了 學習 Fabric.js,我的建議是看文檔不如看 demo。 本文實現的功能:將元素拖進到畫布中並生成對應的圖形或圖片。 效果如下圖所示: 思路 要實現以上效果,需要考慮以下幾點: 元素有拖拽功能。 能在畫布中生成對應的元素。 畫佈有可能縮放。 畫佈有 ...


本文簡介

點贊 + 關註 + 收藏 = 學會了


學習 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/[email protected]/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 拖拽創建元素



推薦閱讀


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 定義: 相同文檔結構(Mapping)文檔的結合 由唯一索引名稱標定 一個集群中有多個索引 不同的索引代表不同的業務類型數據 註意事項: 索引名稱不支持大寫 索引名稱最大支持255個字元長度 欄位的名稱,支持大寫,不過建議全部統一小寫 ...
  • 檢索數據 SELECT語句 SELECT語句用途是從一個或多個表中檢索信息。為了使用SELECT檢索表數據,必須至少給出兩條信息——想選擇什麼,以及從什麼地方選擇。 檢索單個列 利用SELECT語句從products表中檢索一個名為prod_name的列: SELECT prod_name FROM ...
  • 摘要:增量備份是重要的常規備份策略,正確快速識別增量變化文件的相關信息對增量備份至關重要。 本文分享自華為雲社區《其疾如風,GaussDB(DWS)增量備份核心設計》,作者: 我的橘子呢 。 1、認識增量備份 GaussDB(DWS)數倉的備份恢復工具Roach支持集群級增量備份。全量備份會將源數據 ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者: 楊延昭 文章來源:GreatSQL社區投稿 在資料庫中,除傳統的計算資源(CPU、RAM、IO)的爭用以外,數據也是一種供許多用戶共用的資源。如 ...
  • 華為HMS Core 視頻編輯服務依托自身AI技術的核心優勢,在最新版本HMS Core 6.8.0中上線了全新的視頻美顏功能,能對指定圖片或視頻中的人臉實現磨皮、美白、大眼、瘦臉的美顏效果,適用於直播、相機、視頻剪輯、圖片處理等場景中,打造獨特自然的美顏效果。 HMS Core視頻美顏功能在技術上 ...
  • 一. Rollbar可以幫你解決哪些問題 無特別說明,文中Rollbar統指Rollbar-flutter 1. 代碼復用 Rollbar官方文檔說是純Dart實現,該特征意味著自帶”代碼復用”光環。 如圖當接入端(Third-APP)調用Rollbar SDK時表示包含的網路(異常數據上傳等)和存 ...
  • 方法一: 在 JavaScript 中,可以使用 URLSearchParams 對象來處理 URL 中的查詢字元串。 序列化(將 JavaScript 對象轉換為查詢字元串)可以使用 URLSearchParams 對象的 append() 方法,如下所示: let params = new UR ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 測試環境好好地功能,上了生產,莫名其妙報錯,開始以為是沒有設置Js安全介面功能變數名稱,結果讓相應人員一查,已經設置了相應的功能變數名稱,再看下公眾號內的介面許可權部分,相關js許可權也都是已授權,沒辦法,只能按照微信文檔上的常見錯誤及解決方法,設置debu ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...