国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院

首頁(yè) > 開(kāi)發(fā) > HTML5 > 正文

canvas如何實(shí)現(xiàn)多張圖片編輯的圖片編輯器

2024-09-05 07:23:18
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

圖片編輯器雖然是圖片涂鴉工具,但是在當(dāng)前疫情的大背景下,該工具還可以成為老師在線修改學(xué)生提交的家庭作業(yè)的工具使用的,它可以極大地減輕了老師的批改作業(yè)的工作負(fù)擔(dān),相信這軟件是有市場(chǎng)的。
本次不具體介紹canvas的各種操作畫布的api,而且相信大家在網(wǎng)上能搜到大量的關(guān)于canvas涂鴉的開(kāi)源代碼,即使是這樣,我也相信真正把canvas涂鴉用于公司實(shí)際的項(xiàng)目的不多,改裝成圖片編輯器的會(huì)更少,而用于多張圖片編輯然后多張統(tǒng)一保存、上傳的更是寥寥無(wú)幾。下面,我將在我曾經(jīng)寫過(guò)的一個(gè)canvas涂鴉的基礎(chǔ)上,將多張圖片編輯器的開(kāi)發(fā)和思考過(guò)程記錄下來(lái)。

圖片編輯器產(chǎn)品需求

先說(shuō)需求,由于涉及到實(shí)際公司的項(xiàng)目開(kāi)發(fā),滿足需求的圖片編輯器可能只是編輯一張單獨(dú)的圖片,可能是給你一個(gè)圖片列表也就是多張圖片的編輯,在用戶保存之前,用戶可以來(lái)回切換現(xiàn)在編輯哪張圖片,而且要記住每張照片的編輯操作,都要允許可以撤銷,最后統(tǒng)一點(diǎn)擊保存按鈕,沒(méi)有編輯過(guò)的圖片將被丟棄,已經(jīng)編輯過(guò)的帶著涂鴉的圖片上傳給服務(wù)器。

準(zhǔn)備工作,必須了解的相關(guān)知識(shí)點(diǎn)

html2canvas的使用

html2canvas這個(gè)插件實(shí)際是將網(wǎng)頁(yè)上的普通html元素轉(zhuǎn)化成canvas。那么為什么要將html元素轉(zhuǎn)化成canvas呢?那是因?yàn)閏anva具有許多html元素不具備的特點(diǎn),例如可以在canvas上畫圖,畫線條等等操作,而且canvas直接提供api可以將畫布上展示的內(nèi)容導(dǎo)成圖片。是不是有點(diǎn)類似于截圖,html2canvas就是利用這一點(diǎn)。 我最開(kāi)始使用html2canvas時(shí)在公司h5項(xiàng)目有一個(gè)手寫板的項(xiàng)目。需要在是手寫板中簽字,然后將簽字之后的圖片導(dǎo)出然后上傳。后來(lái)又有一個(gè)在web端的圖片編輯器的項(xiàng)目,又用到了html2canvas這個(gè)插件,下面來(lái)介紹下該插件

介紹

html2canvas的缺點(diǎn):

用html2canvas轉(zhuǎn)成的canvas,在最后生成的視覺(jué)效果上并不是100%還原原來(lái)的元素,也就是說(shuō)會(huì)有部分像素點(diǎn)會(huì)丟失。

html2canvas的安裝

npm install html2canvas

html2canvas的引入

import html2canvas from 'html2canvas'

html2canvas的使用

如果一個(gè)帶圖片(無(wú)論是img標(biāo)簽還是背景圖)的html元素在轉(zhuǎn)成canvas之后,該生成的canvas中img或者背景圖缺失,變成了白色的底,并且控制臺(tái)都告訴你圖片跨域了,解決方案如下:
以下第1條必須滿足,然后第2、3是根據(jù)是html元素是img標(biāo)簽還是背景圖,來(lái)添加不同的配置項(xiàng)。

1.圖片服務(wù)器配置Access-Control-Allow-Origin 或使用代理
2.html2canvas的配置項(xiàng)中配置useCORS:true
3.img標(biāo)簽增加 crossOrigin='anonymous'

html轉(zhuǎn)canvas,并且解決圖片跨域問(wèn)題

html2canvas(dom, {  useCORS: true, // 背景圖片跨域  scale: window.devicePixelRatio,//像素比  width: dom.offsetWidth  height: dom.offsetHeight}).then(canvas => {  //得到base64  let base64 = canvas.toDateUrl() })

因?yàn)閔tml2canvas將普通的html轉(zhuǎn)成了canvas,而canvas自帶將自身轉(zhuǎn)成的base64的方法,而base64格式文件可以直接作為img標(biāo)簽的src,展示在頁(yè)面上

<img :src='base64' />

此時(shí)如果希望將生成的圖片上傳,base64是無(wú)法直接上傳的,需要將base64轉(zhuǎn)成blob格式。所以如下:我們將得到的base64數(shù)據(jù)傳進(jìn)base64ToBlob函數(shù),最后函數(shù)return的結(jié)果就是blob數(shù)據(jù)。請(qǐng)注意:該方法只是單純的將base64轉(zhuǎn)成blob的通用方法

base64轉(zhuǎn)blob

function base64ToBlob(base64){    var arr = base64.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 }))}

其實(shí)canvas自帶將canas轉(zhuǎn)化成blob對(duì)象的方法

canvas轉(zhuǎn)blob

canvas.toBlob(file=>{    //此時(shí)file就是blob數(shù)據(jù)格式。    //成功上傳成blob之后需要為該blob數(shù)據(jù)添加name屬性,否則后面上傳到服務(wù)器會(huì)報(bào)錯(cuò):size not set    file.name = 'cover.png'})

但是在實(shí)際項(xiàng)目開(kāi)發(fā)中遇到部分低版本的手機(jī)瀏覽器中的canvas元素不支持toBlob方法,報(bào)錯(cuò)toBlob is not a function。所以此時(shí)我們需要對(duì)當(dāng)前不支持canvas.toBlo方法的瀏覽器做polyfill

DOM canvas元素暴露了HTMLCanvasElement接口,該接口提供了用來(lái)操作一個(gè)canvas元素布局和呈現(xiàn)的屬性和方法HTMLCanvasElement接口繼承了element接口的屬性和方法。(來(lái)自MDN)

toBlob方法兼容性寫法

if (!HTMLCanvasElement.prototype.toBlob) {    Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {      value: function (callback, type, quality) {        var binStr = atob(this.toDataURL(type, quality).split(',')[1])        var len = binStr.length        var arr = new Uint8Array(len)        for (var i = 0; i < len; i++) {          arr[i] = binStr.charCodeAt(i)        }        callback(new Blob([arr], { type: type || 'image/png' }))     }    })}

以上代碼判斷當(dāng)前瀏覽器HTMLCanvasElement.prototype內(nèi)是否含有toBlob方法,如果沒(méi)有該方法,則為HTMLCanvasElement.prototype添加toBlob方法。
到目前為止,無(wú)論是base64直接轉(zhuǎn)成blob,還是canvas直接轉(zhuǎn)成blob,最后目的都是為了將blob數(shù)據(jù)上傳到服務(wù)器。請(qǐng)注意:當(dāng)前頁(yè)面的blob數(shù)據(jù)是存儲(chǔ)在緩沖區(qū)的,而不是像電腦本地文件存儲(chǔ)在硬盤中上傳那樣,所以需要添加 isLocalFile:false屬性,fileData屬性接收一個(gè)數(shù)組,如果是一張圖片,則是個(gè)長(zhǎng)度為1的,元素為blob格式的數(shù)組。上傳文件到服務(wù)器在這里不做介紹。

提交blob到服務(wù)器

// 轉(zhuǎn)完圖片數(shù)據(jù),提交服務(wù)器    async upload (blobArr) {      const oss = await this.$uploadOSS.init({        fileData: blobArr,        isLocalFile: false,        onProgress: percent => {},        onSuccess: (res) => {          console.log(res)        }      })      if (oss) {        const data = await oss.start()        if (!data.code) {          this.$message.error(data.message)        }      }    },

以上是對(duì)html2canvas好和canvas的常見(jiàn)的屬性的介紹,同時(shí)也是開(kāi)發(fā)圖片編輯器的前提。下面會(huì)一步步詳細(xì)介紹下在已有的canvas涂鴉板的基礎(chǔ)上的開(kāi)發(fā)多張圖片編輯器的思路和過(guò)程。

基于canvas的圖片編輯器的怎么做?

1、既然要編輯圖片,自然要提供給我們的圖片的地址,可能是本地相對(duì)路徑,也可能是遠(yuǎn)程地址,也可能是一張或者是多張圖片。所以進(jìn)行操作的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,如果只有一張圖那就是長(zhǎng)度為1的,元素為圖片地址的數(shù)組,另外需要解決圖片跨域來(lái)避免生成的圖片只有涂鴉部分,且背景白色、頭像或者圖片丟失的情況,上面已經(jīng)提供了詳細(xì)的解決方案。

2、圖片編輯器肯定是個(gè)通用組件,所以根據(jù)父組件傳進(jìn)來(lái)的圖片src的一個(gè)長(zhǎng)度為length的數(shù)組,來(lái)填充一個(gè)相同長(zhǎng)度的二維數(shù)組,二維數(shù)組中的元素記錄的是每一張圖片被編輯的歷史記錄的集合,也就是每一次操作canvas的時(shí)候生成的base64數(shù)據(jù),在開(kāi)始編輯之前初始化的時(shí)候該圖片默認(rèn)填充空數(shù)組[],在這里介紹一個(gè)填充數(shù)組很好用的方法fill,值得注意:fill的內(nèi)容如果是引用類型,會(huì)導(dǎo)致fill的每一項(xiàng)都是同一個(gè)引用,導(dǎo)致在操作其中一個(gè)元素時(shí),其余所有的元素都一起改變。

//result:[[],[],...]this.canvasArrBase = Array(length).fill(0).map(item => {  return []})

2、點(diǎn)擊不同的圖片,將該圖片的src傳進(jìn)編輯器組件,并將該src作為canvas背景圖,此時(shí)視覺(jué)上看到了一圖,在dom中其實(shí)是canvas的一張背景圖為src。
3、開(kāi)始在頁(yè)面涂鴉畫布,為了避免無(wú)法在surface等觸控屏設(shè)備上使用畫布涂鴉或者圖片編輯器,請(qǐng)使用touch事件,而不是mouse事件。

 // 開(kāi)始繪制  canvas.addEventListener('touchstart', e=> {    ...  },false)    // 繪制中  canvas.addEventListener('touchmove', e=>{   ...  },false)    // 結(jié)束繪制  canvas.addEventListener('touchend', e=>{    ...  },false)

4、在進(jìn)入圖片編輯器之前將點(diǎn)擊的圖片的索引和圖片所在的圖片數(shù)組,一起作為props傳入圖片編輯器,記住當(dāng)前在編輯第幾張圖片currentIndex,才能將涂鴉或者撤銷操作都記錄在這張圖片之上。當(dāng)用戶在畫布上每操作一次,也就是開(kāi)始touchstart,然后touchmove,最后touchend的時(shí)候,將當(dāng)前的canvas畫布的內(nèi)容(所有內(nèi)容,不只是剛剛涂鴉的那一筆。類似于git的每一次提交,即使只是提交一行代碼,git也都會(huì)為整個(gè)項(xiàng)目生成一份快照,而不只是記錄本次修改的一行代碼,git回滾就是快照的回滾),包括這張背景圖片使用canvas.toDateUrl()生成base64,將其push進(jìn)我們上面介紹的this.canvasArrBase[currenIndex]里,這時(shí)currenIndex所在數(shù)組的圖片多了一張歷史記錄,也就是剛剛涂鴉的畫面被我們保存下來(lái)了。這也就是canvas圖片編輯器,撤銷的基礎(chǔ),有了它我們才知道撤銷之后展示什么。

//新的土涂鴉操作生成的快照存儲(chǔ)在當(dāng)前索引的數(shù)組內(nèi) this.canvasArrBase[this.currentIdex].push(canvas.toDataUrl())

5、切換圖片也就意味著改變了canvas的背景圖,改變了currentIndex,然后在新的currentIndex上對(duì)另外一張圖片進(jìn)行編輯,其實(shí)也是同樣的步驟,無(wú)非就是往當(dāng)前編輯的圖片的push畫布剛剛展示的所有內(nèi)容。
6、重點(diǎn)來(lái)了,撤銷如何做呢?如何撤銷,如何在畫布上一步步撤銷并且看到上一次,上上次的繪畫記錄呢?
隨著每張圖片的編輯,每張圖片產(chǎn)生一些歷史記錄,我們就是利用這些歷史記錄的來(lái)進(jìn)行回滾撤銷操作的。當(dāng)我們?cè)诋?dāng)前已經(jīng)編輯過(guò)的圖片中點(diǎn)一次撤銷的時(shí)候,我們需要將上次canvas中展示的畫布內(nèi)容畫到當(dāng)前的畫布中。在撤銷的時(shí)候可以直接將我們存儲(chǔ)的數(shù)據(jù)源直接刪掉,所以只需要利用 canvas.drawImage() 方法,將需要展示的那一條記錄畫到canvas畫布中即可。

drawImage方法有很多參數(shù),用法點(diǎn)擊查看詳情

let newBaseArr = this.canvasArrBase[this.currentIdex]let { offsetWidth: canvasWidth, offsetHeight: canvasHeight } = canvas//生成一張新的圖片let image = new Image()//圖片的src賦值成當(dāng)前需要展示的base64的值(也就是讓用戶看到撤銷后的畫面)image.src = this.canvasArrBase[this.currentIdex][newBaseArr.length-1]//給img的src賦值后,使用onload監(jiān)聽(tīng)加載圖片情況,加載完成后再繪制,不加onload就極可能繪制失敗。image.onload = function () {    canvas.drawImage(image, 0, 0, canvasWidth, canvasHeight)}

7、關(guān)于性能優(yōu)化:由于你根本不知道用戶會(huì)拿多么大的圖片來(lái)進(jìn)行編輯,所以如果有9張圖片,每張圖片都編輯10次,那么存在內(nèi)存中的base64數(shù)據(jù)量就會(huì)非常龐大,在進(jìn)行頁(yè)面切換或者圖片涂鴉歷史撤銷的時(shí)候就可能造成頁(yè)面的卡頓,為了避免這種情況,在切換圖片之前,我只會(huì)存儲(chǔ)當(dāng)前圖片最后一張圖片的base64。
8、提交,保存之前判斷this.canvasArrBase中每一項(xiàng)的長(zhǎng)度,大于0,則是有編輯過(guò),否則被過(guò)濾掉。然后利用前面介紹上傳方法上傳到服務(wù)器,就OK啦!

總結(jié)

在學(xué)習(xí)canvas的道路上它總能給我?guī)?lái)一些驚喜和驚訝,一些我們熟知的工具,例如:echart,高德地圖等無(wú)一不是基于canvas做出的。我曾經(jīng)有位同事是一位echart圖表大神,受他的影響,我逐漸對(duì)echart的威力有所領(lǐng)略,希望在這樣一個(gè)開(kāi)源社區(qū)里和大家一起學(xué)習(xí),進(jìn)步。

到此這篇關(guān)于canvas如何實(shí)現(xiàn)多張圖片編輯的圖片編輯器的文章就介紹到這了,更多相關(guān)canvas圖片編輯器內(nèi)容請(qǐng)搜索武林網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持武林網(wǎng)!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
国产激情自拍_国产9色视频_丁香花在线电影小说观看 _久久久久国产精品嫩草影院
另类视频在线| 国产精品毛片一区二区三区四区| 国产成人午夜精品| 日本中文字幕在线观看| 国产精品作爱| 国产丝袜在线| 午夜视频在线| 91国内在线| 日韩国产成人| 国产一二三视频| av丝袜在线| 久久久久久久美女| 国产三级自拍| 午夜在线网站| 国产福利免费在线观看| 日本黄色免费网址| 国产精品秘入口| 麻豆国产在线视频| 精品福利视频导航大全| 久久91精品视频| 在线成人综合色一区| xxxxx中文字幕| 97国产视频| 最新天堂资源在线| 开心丁香婷婷深爱五月| 中文av字幕| 国产图片综合| 成人无遮挡免费网站视频在线观看| 国产系列电影在线播放网址| 成年女人在线视频| 在线国产网址| 精品国产高清自在线一区二区三区 | 国产中文字幕在线观看| 免费三级毛片| 亚洲精品在线播放视频| 国产视频在线播放| 国产在线看片| 国产对白国语对白| 天天操天天操天天色天天要| 免费国产阿v视频在线观看| 尤物在线视频观看| 国产www网站| 国产xxx在线| 精品麻豆国产| 香蕉视频网站在线观看| 91精品专区| 国产视频三区| 国产黄色片在线播放| 国产日韩网站| 97中文字幕| 亚色视频在线观看| 性欧美精品xxxx| 香蕉视频在线观看www| 亚洲成a人v欧美综合天堂麻豆| 在线免费观看你懂的| 在线免费观看黄色av| 在线三级中文| 欧美亚洲另类在线观看| www555久久| 国产精品臀控福利在线观看| 开心丁香婷婷深爱五月| 欧美日韩**字幕一区| 国产在线www| 2020亚洲男人天堂| 欧美日韩视频精品二区| 国产一级免费在线观看| 99高清免费国产自产拍| 日本h片在线观看| 国产视频精选在线| baoyu777.永久免费视频| 国内外激情在线| 国产成人精品综合网站| www.狠狠操.com| 国产丝袜在线| 黄色av免费在线| 国产成人精品实拍在线| 在线黄色.com| 中文字幕一区免费| 亚洲欧美日韩成人网| 五月综合激情在线| 波多野结衣久久高清免费| 天天操天天艹| 亚洲天堂久久久| 国产经典av| 国产精品亚洲第五区在线| 国产黄色大片在线观看| 高清视频一区二区三区四区| 香蕉视频网站在线播放| 国产黄色在线免费观看| 五月综合网站| 资源视频在线播放免费| 青青草在线播放| 日本福利午夜视频在线| 亚洲天堂影院在线观看| 国产成人va亚洲电影| 天堂在线看视频| 成年网在线观看免费观看网址| gogo高清在线播放免费| 精品99又大又爽又硬少妇毛片| 国产视频二区在线观看| 最新黄网在线观看| 国产美女在线看| 欧美精品久久久久久久小说| www.狠狠操.com| v天堂福利视频在线观看| gogo在线高清视频| 在线激情网站| 99福利在线| 精品推荐国产麻豆剧传媒| 国产三级视频在线看| 国产极品一区二区三区| 国产成人综合美国十次| 91精选福利| av在线不卡网站| 福利在线国产| 国产盗摄精品一区二区酒店| 精品街拍一区二区| 日本视频在线观看一区二区三区| jlzzjlzz欧美大全| 国产免费高清| 精品一二三四| 亚洲精品xxxxx| 成人亚洲一区二区三区| 国产视频97| www.久草.com| 国产丝袜自拍| 欧美日韩不卡中文字幕在线| 国产你懂的在线观看| 国产福利一区二区在线精品| 亚洲国产精华液| 最好看更新中文字幕| 精品亚洲成a人片在线观看| 美女被人操视频在线观看| 国产精品美女一区二区三区四区| 性网站在线观看| 天天操夜夜操天天射| 国产视频精品久久| 牛牛在线精品视频| 91精选福利| 最新天堂资源在线资源| 亚洲精品男人| 国产精品视频二区三区| 天堂√中文在线| 国产福利三区| 国产美女性感在线观看懂色av| 亚洲免费国产| 国产不卡精品一区二区三区| 国产自产视频| 女人色在线免费视频| 伊人免费在线| 在线激情小视频| 国产精品日日爱| 国产一区二区三区美女秒播| 激情丁香在线| 超碰在线网站| 麻豆精品视频入口| 日本福利午夜视频在线| 在线免费国产视频| 2019中文字幕视频| 国产区在线观看| 国产一区二区三区不卡在线| 国产在线更新| 六月天色婷婷| 爱福利在线视频| 狠狠操天天操夜夜操| 九色福利视频| 九九视频精品在线| 高清视频一区二区三区四区| 欧美日韩在线中文字幕| 欧美日韩不卡中文字幕在线| 日本久久网站| 国产麻豆麻豆| 91午夜在线| 欧美日韩亚洲国内综合网| 黄色av网址在线免费观看| 五月婷婷开心综合| 国产黄在线观看免费观看不卡| 国产福利在线| 亚洲电影视频在线| 国产在线一区二区视频| 中文字幕在线看精品乱码| 最新国产在线精品91尤物| 国产欧美在线观看视频| 国产高清在线观看| 精品国产一区二区三区不卡在线 | 碰草在线视频| 国产在线观看色| 国产精品yjizz视频网一二区 | 国产三级在线看| 99热国产在线| 久久精品无码一区二区日韩av | 国产在线三区| 国产一级性片| 欧美亚洲天堂| 国产不卡精品一区二区三区| 精品a在线观看| 中文字幕在线影院| 天天草天天草| 91美女在线| 国产精品入口麻豆免费|