Fabric.js 將本地圖像上傳到畫布背景

消息可靠么?.jpg

本文介紹

我使用 Fabric.js 的版本是 4.6.0。

這次要實現(xiàn)的效果是:在本地上傳一張圖片,然后渲染到 canvas 里(當(dāng)做背景圖)。

我會用 原生 的方法實現(xiàn)一次,然后再在 Vue3 + Element-plus 環(huán)境下實現(xiàn)一次。

最后聊聊我在真實項目中的做法。

01.gif


需求:

  1. 通過點擊上傳按鈕上傳圖片
  2. 拿到圖片,放到畫布上渲染

需要注意的是,本文主要實現(xiàn) 上傳圖片并渲染到畫布 的邏輯,所以沒有做上傳文件類型的限制,也沒做文件大小限制。如果你的業(yè)務(wù)中需要限制文件類型,只需在本案例基礎(chǔ)上添加限制的方法就行了。


本文所有代碼都在文末給出的倉庫里。

如果本文內(nèi)容對你有所幫助,也請你幫我點個贊唄~



原生操作

通過 <input type="file" /> 獲取圖片路徑,會受到瀏覽器安全策略影響,所以需要處理一下。

實現(xiàn)邏輯:

  • 定義好 上傳按鈕畫布(HTML部分);
  • 初始化畫布;
  • 點擊上傳按鈕 獲取圖片地址(這里需要處理一下安全策略的問題);
  • 拿到圖片路徑,使用 canvas.setBackgroundImage 將圖片設(shè)置成畫布背景;
  • canvas.setBackgroundImage 的回調(diào)函數(shù)里刷新一下畫布;


<div>
  <input type="file" name="file" id="upload" onchange="handleUpload()" />
  <button onclick="saveCanvas()">保存</button>
</div>

<canvas id="canvas" width="600" height="600" style="border: 1px solid #ccc;"></canvas>

<!-- 引入fabric.js -->
<script src="https://cdn.bootcdn.net/ajax/libs/fabric.js/460/fabric.js"></script>
<script>

// 上傳文件的DOM元素
const uploadEl = document.getElementById("upload")

// 畫布
let canvas = null

// 初始化畫布
function initCanvas() {
  canvas = new fabric.Canvas('canvas')
}

// 上傳文件事件
function handleUpload() {
  // 上傳文件列表的第一個文件
  const file = uploadEl.files[0]

  // 圖片文件的地址
  let imgPath = null

  // 獲取圖片文件真實路徑
  // 由于瀏覽器安全策略,現(xiàn)在需要這么做了
  // 這段代碼是網(wǎng)上復(fù)制下來的,想深入理解的可以百度搜搜 “C:\fakepath\”
  if (window.createObjcectURL != undefined) {
    imgPath = window.createOjcectURL(file); 
  } else if (window.URL != undefined) {
    imgPath = window.URL.createObjectURL(file); 
  } else if (window.webkitURL != undefined) {
    imgPath = window.webkitURL.createObjectURL(file);
  }

  // 設(shè)置畫布背景,并刷新畫布
  canvas.setBackgroundImage(
    imgPath,
    canvas.renderAll.bind(canvas)
  )
}

// 保存畫布
function saveCanvas() {
  let data = canvas.toJSON()
  console.log(data)
}

window.onload = function() {
  initCanvas()
}
</script>

上面的實現(xiàn)方式,如果是在純前端的環(huán)境下,保存時背景圖是地址是本地地址( "blob:http://127.0.0.1:5500/383e7860-3fa5-43b9-92d9-e7165760e60b" )。

這樣其實不是很好,如果在別的電腦想通過 反序列化 渲染出來的時候,可能會出現(xiàn)一點問題。


如果純前端實現(xiàn)的方式,可以將圖片轉(zhuǎn)成 base64 再生成背景圖。

fabric.Image.fromURL(
  imgPath, // 真實圖片地址
  img => {
    // 將圖片設(shè)置再畫布上,然后重新渲染畫布,圖片就出來了。
    canvas.setBackgroundImage(
      img, // 要設(shè)置的圖片
      canvas.renderAll.bind(canvas) // 重新渲染畫布
    )
  }
)



在 element-plus 里的操作

我使用了 vue3 + element-plus 。

02.gif

實現(xiàn)邏輯和原生方法一樣。
唯一不同的是本例用了 el-upload 這個組件。
我將圖片文件轉(zhuǎn)成 base64 再放進畫布。

<template>
  <div>
    <div class="btn__x">
      <!-- 上傳組件 -->
      <el-upload
        action="https://jsonplaceholder.typicode.com/posts/"
        :multiple="false"
        :show-file-list="false"
        :limit="1"
        accept=".jpg,.png"
        :before-upload="onProgress"
      >
        <el-button type="primary">上傳</el-button>
      </el-upload>

      <!-- 保存按鈕(序列化) -->
      <el-button @click="saveCanvas">保存:打開控制臺查看</el-button>
    </div>

    <!-- 畫布 -->
    <canvas id="canvas" width="600" height="600" style="border: 1px solid #ccc;"></canvas>
  </div>
</template>

<script setup>
import { onMounted, ref } from 'vue'
import { useStore } from 'vuex'
import { fabric } from 'fabric'

const store = useStore()

// 畫布
let canvas = null

// 上傳
function onProgress(file) {
  // 拿圖片文件
  const reader = new FileReader()
  reader.readAsDataURL(file)

  // 圖片文件完全拿到后執(zhí)行
  reader.onload = () => {
    // 轉(zhuǎn)換成base64格式
    const base64Img = reader.result

    // 將base64圖片設(shè)置成背景
    canvas.setBackgroundImage(
      base64Img,
      canvas.renderAll.bind(canvas) // 刷新畫布
    )
  }
  return false
}

// 初始化畫布
function init() {
  canvas = new fabric.Canvas('canvas')
}

// 保存
function saveCanvas() {
  console.log(canvas.toJSON())
}

// 頁面加載完成后,初始化畫布
onMounted(() => {
  init()
})
</script>

<style lang="scss" scoped>
.btn__x {
  display: flex;

  .el-button {
    margin-right: 20px;
  }
}
</style>



在正式開發(fā)中

在正式的項目開發(fā)中,上面兩種情況出現(xiàn)的概率應(yīng)該不多(除非你的后端伙伴是個懶人)

先說說上面兩種情況存在的問題:

  1. 圖片路徑是本地地址,保存到服務(wù)器是沒意義的。
  2. 轉(zhuǎn)成 base64 來保存,字段可能會很大。


03.png

這種情況放到服務(wù)器可能沒什么用的。 127.0.0.1 是你本機的,你上傳的圖片在別人的電腦可能無法查看。


04.png

這種情況雖然問題不大,但 backgroundImage.src 的值有點大。


我在項目中的做法:

  1. 前端上傳圖片給后端
  2. 后端把圖片存到服務(wù)器,然后返回一個圖片url給前端
  3. 前端拿到圖片url,再放到 fabric 里渲染出來

這樣做的好處是 backgroundImage.src 的值變短了。


在正式項目中,你可能還要考慮到背景圖的大小和畫布大小不匹配問題。
你可以參考 《Fabric.js 從入門到膨脹》“拉伸背景圖” 這小節(jié)。



代碼倉庫

原生方式實現(xiàn)

在 Vue3+Element-plus 中實現(xiàn)



推薦閱讀

《Fabric.js 從入門到膨脹》


《Fabric.js 漸變效果(包括徑向漸變)》


《Fabric.js 3個api設(shè)置畫布寬高》


《Fabric.js 自定義右鍵菜單》


《Fabric.js 更換圖片的3種方法(包括更換分組內(nèi)的圖片,以及存在緩存的情況)》

如果本文內(nèi)容對你有所幫助,也請你幫我點個贊唄~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容