Vue+vant實(shí)現(xiàn)圖片上傳添加水印
圖片上傳大家都不會(huì)陌生,就算是一個(gè)新人也會(huì)干的事兒。但說(shuō)到加水印,當(dāng)初我一直以為只能是在后端實(shí)現(xiàn)。
原來(lái),在前端也是能實(shí)現(xiàn)圖片上傳加水印的。(注:以下的代碼都來(lái)自于網(wǎng)絡(luò),只是我把兩部份代碼合在一起來(lái)實(shí)現(xiàn)我的需求而已,在最后面,我也會(huì)附上原作者的接,大家都不容易)
【注:我自己寫的注釋后面會(huì)有個(gè)"(小冰)",其他的為原作者的注釋】
<van-uploader v-model="fileList" multiple:after-read="afterRead"/>
data(){ fileList:[], //vant中圖片上傳的雙向邦定(小冰) wmConfig : { //用于水印的東西,下面會(huì)用到 (小冰) font: "microsoft yahei", //字體 textArray: ['張三','2021/11/26 16:44'],//水印文本內(nèi)容,允許數(shù)組最大長(zhǎng)度3 即:3行水印 density: 3 //密度 建議取值范圍1-5 值越大,水印越多,可能會(huì)導(dǎo)致水印重疊等問(wèn)題,慎重!??! } }
methods:{ //dataURLtoBlob 、 blobToFile 兩個(gè)方法的意思是把base64的圖片轉(zhuǎn)換成file文件 //1,先將base64轉(zhuǎn)換為blob dataURLtoBlob(dataurl) { var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], { type: mime }); }, //2,再將blob轉(zhuǎn)換為file blobToFile(theBlob, fileName){ theBlob.lastModifiedDate = new Date(); // 文件最后的修改日期 theBlob.name = fileName; // 文件名 return new File([theBlob], fileName, {type: theBlob.type, lastModified: Date.now()}); }, //這個(gè)不多說(shuō),都懂,文件上傳的方法(小冰) async afterRead(file){ console.log(file) let base64 = file.content; let res = await this.base64AddWaterMaker(base64,this.wmConfig) //加水印的重點(diǎn)就是這個(gè)(小冰) file.content = res; let blob = this.dataURLtoBlob(file.content); // 拿到文件名 let fileName = file.file.name; // 2,在轉(zhuǎn)為 file類型 let file1 = this.blobToFile(blob,fileName); console.log("file1:",file1); }, base64AddWaterMaker (base64Img, wmConfig) { let that = this; if (wmConfig.textArray.length === 0) { console.error("****沒有水印內(nèi)容*****"); return base64Img; } return new Promise((resolve, reject) => { const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); const img = new Image(); let resultBase64 = null; img.onload = function() { canvas.width = img.width; canvas.height = img.height; //canvas繪制圖片,0 0 為左上角坐標(biāo)原點(diǎn) ctx.drawImage(img, 0, 0); //寫入水印 that.drawWaterMark(ctx, img.width, img.height, wmConfig); resultBase64 = canvas.toDataURL("image/png"); if (!resultBase64) { reject(); } else { resolve(resultBase64); } }; img.src = base64Img; }); }, drawWaterMark (ctx, imgWidth, imgHeight, wmConfig) { let fontSize; if (imgWidth >= 3456) { fontSize = 50; } else if (imgWidth >= 2700) { fontSize = 30; } else if (imgWidth >= 2000) { fontSize = 26; } else if (imgWidth >= 1436) { fontSize = 20; } else if (imgWidth >= 800) { fontSize = 12; } else if (imgWidth >= 500) { fontSize = 10; } else { fontSize = 8; } console.log(imgWidth, imgHeight, fontSize); ctx.fillStyle = "white"; ctx.font = `${fontSize}px ${wmConfig.font}`; ctx.lineWidth = 1; ctx.fillStyle = "rgba(255,255,255,1)"; ctx.textAlign = "left"; ctx.textBaseline = "middle"; //文字坐標(biāo) const maxPx = Math.max(imgWidth, imgHeight); const stepPx = Math.floor(maxPx / wmConfig.density); let arrayX = [0];//初始水印位置 canvas坐標(biāo) 0 0 點(diǎn) while (arrayX[arrayX.length - 1] < maxPx/2) { arrayX.push(arrayX[arrayX.length - 1] + stepPx); } arrayX.push(...arrayX.slice(1, arrayX.length).map((el) => { return -el; })); console.log(arrayX); for (let i = 0; i < arrayX.length; i++) { for (let j = 0; j < arrayX.length; j++) { ctx.save(); ctx.translate(imgWidth / 2, imgHeight / 2); ///畫布旋轉(zhuǎn)原點(diǎn) 移到 圖片中心 ctx.rotate(-Math.PI / 5); if (wmConfig.textArray.length > 3) { wmConfig.textArray = wmConfig.textArray.slice(0, 3); } wmConfig.textArray.forEach((el, index) => { let offsetY = fontSize * index + 2; ctx.fillText(el, arrayX[i], arrayX[j] + offsetY); }); ctx.restore(); } } } }
代碼就是這些,然而,在此中,我是把圖片加水印和圖片base64轉(zhuǎn)file兩個(gè)不同的博文混在一起寫的。
圖片加水印 :JavaScript實(shí)現(xiàn)為圖片添加水印的方法
base64轉(zhuǎn)file:JS中將圖片base64轉(zhuǎn)file文件的兩種方式
順便我也把file文件轉(zhuǎn)base64的封裝函數(shù)也發(fā)一下。當(dāng)然,這個(gè)我也是在網(wǎng)上找的。
function fileToBase64(file, callback) { //callback 是一個(gè)回調(diào)函數(shù),而回調(diào)函數(shù)往這兒走一回,它結(jié)果就是base64 const fileReader = new FileReader() fileReader.readAsDataURL(file) fileReader.onload = function () { callback(this.result) } }, function callbaseFun(res){ console.log(res,'res打印出來(lái)就是base64') },
//---------------------------分割線--------------------
第二種方法,跟上面的方法差不多,只是這種方法可以隨意去調(diào)整水印的位置和大小什么的。
//將base64轉(zhuǎn)化為二進(jìn)制 dataURItoBlob(base64Data) { var date = new Date(); //console.log('將base64轉(zhuǎn)化為二進(jìn)制', date.getMinutes(), date.getSeconds()) var byteString; if (base64Data.split(',')[0].indexOf('base64') >= 0) byteString = atob(base64Data.split(',')[1]); else byteString = unescape(base64Data.split(',')[1]); var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0]; var ia = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ia], { type: mimeString }); }, //將base64轉(zhuǎn)化為二進(jìn)制 dataURItoBlob(base64Data) { var date = new Date(); //console.log('將base64轉(zhuǎn)化為二進(jìn)制', date.getMinutes(), date.getSeconds()) var byteString; if (base64Data.split(',')[0].indexOf('base64') >= 0) byteString = atob(base64Data.split(',')[1]); else byteString = unescape(base64Data.split(',')[1]); var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0]; var ia = new Uint8Array(byteString.length); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ia], { type: mimeString }); }, //Base64轉(zhuǎn)Canvas async imgToCanvas(base64) { var date = new Date(); //console.log('Base64轉(zhuǎn)Canvas', date.getMinutes(), date.getSeconds()) // 創(chuàng)建img元素 const img = document.createElement('img') img.setAttribute('src', base64) await new Promise((resolve) => (img.onload = resolve)) // 創(chuàng)建canvas DOM元素,并設(shè)置其寬高和圖片一樣 const canvas = document.createElement('canvas') //console.log(img.height) //console.log(img.width) canvas.width = img.width canvas.height = img.height // 坐標(biāo)(0,0) 表示從此處開始繪制,相當(dāng)于偏移。 canvas.getContext('2d').drawImage(img, 0, 0) return canvas }, //寫入水印 addWatermark(canvas, txt1, txt2, txt3, tex4) { //alert(canvas.height) var date = new Date(); //console.log('寫入水印', date.getMinutes(), date.getSeconds()) const ctx = canvas.getContext('2d') //ctx.font = '50px Arial' //ctx.strokeText(text, 0, 0) ctx.font = '20px Arial' ctx.fillStyle = 'white' ctx.fillText(txt1, 20, canvas.height - 240) ctx.fillText(txt2, 20, canvas.height - 200) ctx.fillText(txt3, 20, canvas.height - 160) ctx.fillText(tex4, 20, canvas.height - 120) ctx.fillText(tex4, 20, canvas.height - 80) return canvas }, //Canvas轉(zhuǎn)圖片文件(Image) convasToImg(canvas) { var date = new Date(); //console.log('Canvas轉(zhuǎn)圖片文件(Image)', date.getMinutes(), date.getSeconds()) // 新建Image對(duì)象,可以理解為DOM let image = new Image() // canvas.toDataURL 返回的是一串Base64編碼的URL // 指定格式 PNG image.src = canvas.toDataURL('image/png') return image }, //圖片文件(Image)轉(zhuǎn)文件(File) base64ToFile(urlData, fileName) { var date = new Date(); //console.log('圖片文件(Image)轉(zhuǎn)文件(File)', date.getMinutes(), date.getSeconds()) let arr = urlData.split(','); let mime = arr[0].match(/:(.*?);/)[1]; let bytes = atob(arr[1]); // 解碼base64 let n = bytes.length let ia = new Uint8Array(n); while (n--) { ia[n] = bytes.charCodeAt(n); } return new File([ia], fileName, { type: mime }); }, //file對(duì)象轉(zhuǎn)base64 fileToBase64(file, callback) { //callback 是一個(gè)回調(diào)函數(shù),而回調(diào)函數(shù)往這兒走一回,它結(jié)果就是base64 var date = new Date(); //console.log('file對(duì)象轉(zhuǎn)base64', date.getMinutes(), date.getSeconds()) return new Promise((resolve, reject) => { const fileReader = new FileReader() fileReader.readAsDataURL(file) fileReader.onload = function () { let res = callback(this.result) resolve(res) } }) }, callbaseFun(res) { //console.log('上傳的方法') return new Promise((resolve, reject) => { let that = this; //console.log(res, 'base64') let str = res.split(','); str[0] = 'data:image/jpeg;base64'; let r = str.toString(); let url = "/ServerAPI/ServerBase64API.ashx" var obj = { imgbase64: r, method: "ServerBase64png", Module: "DaKa", id: "a4e89358-9465-4295-87ea-4d0af2bd9133" } jQuery.ajax({ url: url, type: 'POST', data: obj, dataType: 'json', timeout: 8000, }) .done(res => { var date = new Date(); //console.log('上傳成功', date.getMinutes(), date.getSeconds()) //console.log('成功時(shí)執(zhí)行') resolve(res) }) .fail(err => { //console.log(err, '上傳失敗') reject({ msg: '上傳失敗' }) }) }) },
async oneUpload(e){ let base64 = e.content; let tempCanvas = await this.imgToCanvas(base64) //得到canvas的值 const canvas = this.addWatermark(tempCanvas , '水印1' , '水印2' , '水印3'); //這兒水印的參數(shù)是根據(jù) addWatermark()方法里面的 ctx.fillText(txt1, 20, canvas.height - 240) 進(jìn)行修改 const img = this.convasToImg(canvas); //Canvas轉(zhuǎn)圖片文件(Image) let newFile = this.base64ToFile(img.src) //圖片文件轉(zhuǎn)file對(duì)象 let res = await this.fileToBase64(newFile, this.callbaseFun) //file轉(zhuǎn)base64, 注意 第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),也就是說(shuō)回調(diào)函數(shù)里面的參數(shù)就是base64的值 }
到此這篇關(guān)于Vue+vant實(shí)現(xiàn)圖片上傳添加水印的文章就介紹到這了,更多相關(guān)Vue vant圖片加水印內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于vue-upload-component封裝一個(gè)圖片上傳組件的示例
這篇文章主要介紹了基于vue-upload-component封裝一個(gè)圖片上傳組件的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10Avue自定義formslot調(diào)用rules自定義規(guī)則方式
在Avue框架中,使用formslot自定義表格列時(shí)可能會(huì)遇到無(wú)法調(diào)用Avue的自定義校驗(yàn)規(guī)則的問(wèn)題,這通常發(fā)生在嘗試通過(guò)formslot自定義設(shè)置列的場(chǎng)景中,解決這一問(wèn)題的一個(gè)有效方法是將自定義列與Avue的校驗(yàn)規(guī)則通過(guò)特定方式連接起來(lái)2024-10-10Vue實(shí)現(xiàn)購(gòu)物車詳情頁(yè)面的方法
這篇文章主要介紹了Vue實(shí)戰(zhàn)之購(gòu)物車詳情頁(yè)面的實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08vue init webpack myproject構(gòu)建項(xiàng)目 ip不能訪問(wèn)的解決方法
下面小編就為大家分享一篇vue init webpack myproject構(gòu)建項(xiàng)目 ip不能訪問(wèn)的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03vue基礎(chǔ)知識(shí)--axios合并請(qǐng)求和slot
這篇文章主要介紹了vue中的axios和slot,文中代碼非常詳細(xì),對(duì)大家的工作學(xué)習(xí)有所幫助,感興趣的朋友可以參考下2020-06-06Vue項(xiàng)目代碼之路由拆分、Vuex模塊拆分、element按需加載詳解
這篇文章主要介紹了Vue項(xiàng)目代碼之路由拆分、Vuex模塊拆分、element按需加載,項(xiàng)目較大路由較多時(shí),路由拆分是一個(gè)不錯(cuò)的代碼優(yōu)化方案,按不同業(yè)務(wù)分為多個(gè)模塊,結(jié)構(gòu)清晰便于統(tǒng)一管理,本文通過(guò)示例給大家詳細(xì)講解,需要的朋友可以參考下2022-11-11詳解Vue Elementui中的Tag與頁(yè)面其它元素相互交互的兩三事
這篇文章主要介紹了詳解Vue Elementui中的Tag與頁(yè)面其它元素相互交互的兩三事,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09一次vue項(xiàng)目?jī)?yōu)化的實(shí)際操作記錄
用vue開發(fā)項(xiàng)目上線以后,發(fā)現(xiàn)首頁(yè)加載速度非常慢,如果項(xiàng)目比較大,甚至可能出現(xiàn)10s以上的等待,下面這篇文章主要給大家介紹了關(guān)于vue項(xiàng)目?jī)?yōu)化的相關(guān)資料,需要的朋友可以參考下2022-09-09vue.js+element-ui動(dòng)態(tài)配置菜單的實(shí)例
今天小編就為大家分享一篇vue.js+element-ui動(dòng)態(tài)配置菜單的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09關(guān)于vue-cli3+webpack熱更新失效及解決
這篇文章主要介紹了關(guān)于vue-cli3+webpack熱更新失效及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04