本文介紹
我使用 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)目中的做法。
需求:
- 通過點(diǎn)擊上傳按鈕上傳圖片
- 拿到圖片溯香,放到畫布上渲染
需要注意的是帘营,本文主要實(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
毁靶。
實(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è)懶人)
先說說上面兩種情況存在的問題:
- 圖片路徑是本地地址拐叉,保存到服務(wù)器是沒意義的。
- 轉(zhuǎn)成 base64 來保存扇商,字段可能會(huì)很大凤瘦。
這種情況放到服務(wù)器可能沒什么用的。 127.0.0.1
是你本機(jī)的案铺,你上傳的圖片在別人的電腦可能無法查看蔬芥。
這種情況雖然問題不大,但 backgroundImage.src
的值有點(diǎn)大控汉。
我在項(xiàng)目中的做法:
- 前端上傳圖片給后端
- 后端把圖片存到服務(wù)器笔诵,然后返回一個(gè)圖片url給前端
- 前端拿到圖片url,再放到
fabric
里渲染出來
這樣做的好處是 backgroundImage.src
的值變短了姑子。
在正式項(xiàng)目中乎婿,你可能還要考慮到背景圖的大小和畫布大小不匹配問題。
你可以參考 《Fabric.js 從入門到膨脹》 中 “拉伸背景圖” 這小節(jié)街佑。
代碼倉庫
在 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è)贊唄~