厚街公司網(wǎng)站建設(shè)外貿(mào)營銷型網(wǎng)站建設(shè)公司
上接文章“圖片處理”
canvas元素其實(shí)就是一個(gè)畫布,我們可以很方便地繪制一些文字、線條、圖形等,它也可以將一個(gè)img標(biāo)簽里渲染的圖片畫在畫布上。
我們在上傳文件到后端的時(shí)候,使用input標(biāo)簽讀取用戶本地文件后得到的其實(shí)是一個(gè)Blob對象(更精確的說是File對象,特殊的Blob對象);而在頁面上展示一個(gè)圖片使用的是img標(biāo)簽;繪制功能用canvas實(shí)現(xiàn)。
添加水印的功能需要在img標(biāo)簽、canvas畫布、Blob對象這三者之間相互轉(zhuǎn)換,通過一些API可以完成這個(gè)工作:
我們可以從本地讀取圖片Blob,然后渲染到img標(biāo)簽,使用canvas繪制img內(nèi)容并且繪制水印內(nèi)容到畫布,再將canvas內(nèi)容轉(zhuǎn)為Blob對象上傳服務(wù)器,這樣就完整實(shí)現(xiàn)了圖片+水印的功能。
一、本地讀取圖像文件渲染到img標(biāo)簽
本地讀取圖片文件將會得到一個(gè)Blob對象,我們可以借助FileReader.readAsDataURL方法讀取Blob的內(nèi)容,并得到一個(gè)Base64編碼的文件內(nèi)容,可以將該內(nèi)容賦值給img.src從而在瀏覽器上渲染出本地的圖像。當(dāng)然,img并非必須渲染到DOM樹。
讀取操作是個(gè)異步操作,讀取完成會觸發(fā)load事件,為了便于之后的調(diào)用,我們可以用一個(gè)Promise包裝這個(gè)操作,最后返回一個(gè)Promise對象。
function blobToImg (blob) {return new Promise((resolve, reject) => {let reader = new FileReader()reader.addEventListener('load', () => {let img = new Image()img.src = reader.resultimg.addEventListener('load', () => resolve(img))})reader.readAsDataURL(blob)})
}
二、將img標(biāo)簽內(nèi)容繪制到canvas畫布
調(diào)用canvas元素畫布上下文對象的drawImage方法即可實(shí)現(xiàn)將img內(nèi)容繪制到畫布。
function imgToCanvas (img) {let canvas = document.createElement('canvas')canvas.width = img.widthcanvas.height = img.heightlet ctx = canvas.getContext('2d')ctx.drawImage(img, 0, 0)return canvas
}
drawImage這個(gè)方法可以傳入多個(gè)參數(shù),以定義繪制的圖像的范圍,這里傳入的0, 0定義從圖像左上角開始繪制,后面可以繼續(xù)傳入兩個(gè)參數(shù)來定義圖像的繪制終點(diǎn),不過這里整個(gè)圖片都要繪制到canvas,所以采用默認(rèn)值即可。
三、canvas畫布上繪制水印并轉(zhuǎn)換為Blob對象
在圖片上傳的時(shí)候,我們通常采用FormData,圖片文件以一個(gè)Blob對象的形式放到FormData中,所以我們需要把canvas再轉(zhuǎn)為Blob以便文件上傳等操作。利用HTMLCanvasElement.toBlob方法:
function watermark (canvas, text) {return new Promise((resolve, reject) => {let ctx = canvas.getContext('2d')// 設(shè)置填充字號和字體,樣式ctx.font = "24px 宋體"ctx.fillStyle = "#FFC82C"// 設(shè)置右對齊ctx.textAlign = 'right'// 在指定位置繪制文字,這里指定距離右下角20坐標(biāo)的地方ctx.fillText(text, canvas.width - 20, canvas.height - 20)canvas.toBlob(blob => resolve(blob))})
}
四、圖片添加水印完整接口
將以上三個(gè)步驟結(jié)合起來,就完整地實(shí)現(xiàn)了從圖片添加水印,下面是一個(gè)簡單的使用示例:從本地選擇一個(gè)圖片文件,然后添加水印后,在傳入的dom元素下預(yù)覽添加水印后的圖片。
function imgWatermark (dom, text) {let input = document.createElement('input')input.setAttribute('type', 'file')input.setAttribute('accept', 'image/*')input.onchange = async () => {let img = await blobToImg(input.files[0])let canvas = imgToCanvas(img)let blob = await watermark(canvas, text)// 此處將Blob讀取到img標(biāo)簽,并在dom內(nèi)渲染出來;如果是上傳文件,可以將blob添加到FormData中l(wèi)et newImage = await blobToImg(blob)dom.appendChild(newImage)}input.click()
}
給頁面加一個(gè)id為container的div元素,然后如下調(diào)用:
let dom = document.querySelector('#container')
imgWatermark(dom, '水印文字')
這樣就完整地給圖片添加了水印效果,下面看一下實(shí)際效果。
添加水印前:
?
添加水印后(水印內(nèi)容:“canvas添加的水印”):
五、總結(jié)
本文僅僅介紹了圖像+水印文字的簡單實(shí)現(xiàn),但是涉及的一些接口其實(shí)很有用。比如有時(shí)候遇到的一個(gè)功能是頭像上傳的預(yù)覽和剪裁,這時(shí)候你可以利用FileReader來讀取文件內(nèi)容預(yù)覽,利用CanvasRenderingContext2D.drawImage來實(shí)現(xiàn)剪裁功能。