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

消息可靠么?.jpg

本文介紹

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

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

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

最后聊聊我在真實(shí)項(xiàng)目中的做法。

01.gif


需求:

  1. 通過點(diǎn)擊上傳按鈕上傳圖片
  2. 拿到圖片溯香,放到畫布上渲染

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


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

如果本文內(nèi)容對(duì)你有所幫助,也請(qǐng)你幫我點(diǎn)個(gè)贊唄~



原生操作

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

實(shí)現(xiàn)邏輯:

  • 定義好 上傳按鈕畫布(HTML部分)蚀腿;
  • 初始化畫布嘴瓤;
  • 點(diǎn)擊上傳按鈕 獲取圖片地址(這里需要處理一下安全策略的問題);
  • 拿到圖片路徑莉钙,使用 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() {
  // 上傳文件列表的第一個(gè)文件
  const file = uploadEl.files[0]

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

  // 獲取圖片文件真實(shí)路徑
  // 由于瀏覽器安全策略磁玉,現(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>

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

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


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

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



在 element-plus 里的操作

我使用了 vue3 + element-plus 毁靶。

02.gif

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

<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">保存:打開控制臺(tái)查看</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ā)中

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

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

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


03.png

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


04.png

這種情況雖然問題不大,但 backgroundImage.src 的值有點(diǎn)大控汉。


我在項(xiàng)目中的做法:

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

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


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



代碼倉庫

原生方式實(shí)現(xiàn)

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



推薦閱讀

《Fabric.js 從入門到膨脹》


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


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


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


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

如果本文內(nèi)容對(duì)你有所幫助,也請(qǐng)你幫我點(diǎn)個(gè)贊唄~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沐旨,一起剝皮案震驚了整個(gè)濱河市森逮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌希俩,老刑警劉巖吊宋,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異颜武,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拖吼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門鳞上,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吊档,你說我怎么就攤上這事篙议。” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵鬼贱,是天一觀的道長移怯。 經(jīng)常有香客問我,道長这难,這世上最難降的妖魔是什么舟误? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮姻乓,結(jié)果婚禮上嵌溢,老公的妹妹穿的比我還像新娘。我一直安慰自己蹋岩,他們只是感情好赖草,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著剪个,像睡著了一般秧骑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扣囊,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天腿堤,我揣著相機(jī)與錄音,去河邊找鬼如暖。 笑死笆檀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盒至。 我是一名探鬼主播酗洒,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼枷遂!你這毒婦竟也來了樱衷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤酒唉,失蹤者是張志新(化名)和其女友劉穎矩桂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痪伦,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侄榴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了网沾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片癞蚕。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辉哥,靈堂內(nèi)的尸體忽然破棺而出桦山,到底是詐尸還是另有隱情攒射,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布恒水,位于F島的核電站会放,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏钉凌。R本人自食惡果不足惜咧最,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甩骏。 院中可真熱鬧窗市,春花似錦、人聲如沸饮笛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽福青。三九已至摄狱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間无午,已是汗流浹背媒役。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宪迟,地道東北人酣衷。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像次泽,于是被迫代替她去往敵國和親穿仪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容