摘要:溫馨提示這里除了一些幼稚的小組件啥也沒有寫在前面距離寫完上一篇實(shí)踐是檢驗(yàn)程序員的唯一標(biāo)準(zhǔn)用戶不想跟你說話并向你扔出一張圖片圖片上傳組件開發(fā)思路篇過去了大半年,才開始寫開發(fā)篇真的是令人悲哀,不過有句話說的好,開始做一件事最好的時(shí)間是大半年前
溫馨提示:這里除了一些幼稚的小組件啥也沒有
寫在前面距離寫完上一篇實(shí)踐是檢驗(yàn)程序員的唯一標(biāo)準(zhǔn)01:用戶不想跟你說話并向你扔出一張圖片 - 圖片上傳組件開發(fā)【思路篇】過去了大半年,才開始寫開發(fā)篇真的是令人悲哀,不過有句話說的好,開始做一件事最好的時(shí)間是大半年前,其次是現(xiàn)在
上一篇偏設(shè)計(jì)和嘗試技術(shù)能否實(shí)現(xiàn),這一篇會(huì)在工程層面實(shí)現(xiàn),并且保證他能被(輕易)引用!
上一篇文章的評(píng)論里好多同學(xué)(差不多3個(gè)人)希望我傳到git上。好吧,本文最終的勞動(dòng)成果會(huì)放上去的,不過那是下一篇文章干的事了,不過這里我已經(jīng)把全部源碼貼上來了- -
上傳到了github上,覺得好的給星哦!l-imgupload //181119
功能完善在之前那篇文章中,又習(xí)慣性的做了很多無用的設(shè)計(jì),你就是一個(gè)上傳圖片的組件,低調(diào)點(diǎn)謝謝,所以最終我搞成了這樣子
state-1:初始狀態(tài)
state-2:完成載入狀態(tài)
state-3:圖片截取
總體來說,把能剩的按鈕都省了,本體就是個(gè)框,適合放在任何地方,此外為了防止破壞頁(yè)面的整體性,組件不再自帶截圖預(yù)覽功能,而是通過事件的方式將所截取的圖像的DataURL實(shí)時(shí)穿給父組件,方便父組件自由使用(圖中的展示區(qū)就是在父組件中寫的)
組件設(shè)計(jì)在一開始設(shè)計(jì)組件的時(shí)候簡(jiǎn)直就是父母給孩子報(bào)課外班的心情,希望能盡可能的滿足各種需求,但轉(zhuǎn)頭想想先把最基本的功能(做個(gè)好人)做好別的都是可以慢慢加上的(懶)
要保證基本功能能(好)用,大概以下這幾點(diǎn):
1.要讓其大小可控,方便應(yīng)用于不同場(chǎng)景,所以組件的寬高有必要成為參數(shù)
2.對(duì)于被裁出的部分,在原圖中看和拎出來多帶帶看視覺上差別還挺大的,所以一個(gè)可以實(shí)時(shí)多帶帶展現(xiàn)所截取內(nèi)容的功能就挺重要的
3.在大多數(shù)情況下,裁剪區(qū)域的選定可能是有固定比例的,所以要將是否限制比例以及按照什么比例作為參數(shù),根據(jù)適用場(chǎng)景決定
所以組件的參數(shù)和事件大概也就這么幾個(gè)了
參數(shù)名:inputWidth
說明:組件寬度
類型:Number
默認(rèn)值:200px
參數(shù)名:inputHeight
說明:組件高度
類型:Number
默認(rèn)值:200px
參數(shù)名:cuttingRatio
說明:裁剪比例,限定比例為寬/高,為空時(shí)沒有比例限制
類型:Number
默認(rèn)值:0
事件名:getImageData
說明:框選完成后鼠標(biāo)抬起時(shí)觸發(fā),返回選定區(qū)域的圖像數(shù)據(jù)
參數(shù):blobData
參數(shù)格式:Blob對(duì)象
事件名:getImageDataURL
說明:鼠標(biāo)拖動(dòng)的每一幀觸發(fā),返回選定區(qū)域的圖像數(shù)據(jù),可用于預(yù)覽區(qū)域展示
參數(shù):dataURL
參數(shù)格式:dataURL
由于功能很單一,HTML的布局也就很簡(jiǎn)單
大概結(jié)構(gòu)如下
<根標(biāo)簽> <提示信息 />//絕對(duì)定位,位于組件下方,初始狀態(tài)不可見,載入圖片后出現(xiàn) <重新選擇按鈕 />//絕對(duì)定位,位于組件右上角,初始狀態(tài)不可見,載入圖片后出現(xiàn) <初始及載入層 />//絕對(duì)定位,位于畫布上方,大小與畫布完全相同 <畫布 />//canvas <隱藏的input標(biāo)簽 />//不可見 根標(biāo)簽>
HTML代碼如下
{{notice}}X重新選擇{{loadFlag == 0?"點(diǎn)擊瀏覽圖片":"加載中"}}
對(duì)應(yīng)的css如下
參數(shù)及變量定義以及對(duì)象初始化props:{ inputWidth:{ type:Number, default:200 }, inputHeight:{ type:Number, default:200 }, cuttingRatio:{ type:Number, default:0 } }, data() { return { mouseDownFlag: false,//記錄鼠標(biāo)點(diǎn)擊狀態(tài)用標(biāo)記 loadFlag: 0,//記錄圖像家在狀態(tài)用標(biāo)記 resultImgData: {},//被截取數(shù)據(jù) input: {},//輸入框?qū)ο? imgObj: new Image(),//圖片對(duì)象 inputAreaCanvas: {},//主體canvas對(duì)象 inputArea2D: {},//主體CanvasRenderingContext2D對(duì)象 notice: "拖拽鼠標(biāo)框選所需要的區(qū)域",//提示區(qū)域文本 noticeFlag: false,//提示區(qū)域展示狀態(tài)標(biāo)記 dataURL:"",//被截取dataURL tempCanvas:{},//存放截取結(jié)果用canvas對(duì)象 tempCanvas2D:{},//存放截取結(jié)果用CanvasRenderingContext2D對(duì)象 resetX:0,//組件起點(diǎn)橫坐標(biāo) resetY:0,//組件起點(diǎn)縱坐標(biāo) /* 181031改:其實(shí)并不用這兩個(gè)變量 startX:0,//截取開始點(diǎn)橫坐標(biāo) startY:0,//截取開始點(diǎn)縱坐標(biāo) */ resultX:0,//截取結(jié)束點(diǎn)橫坐標(biāo) resultY:0,//截取結(jié)束點(diǎn)縱坐標(biāo) } }, mounted: function() { //對(duì)象初始化 this.input = document.getElementById("input") this.inputAreaCanvas = document.getElementById("inputAreaCanvas"); this.inputArea2D = this.inputAreaCanvas.getContext("2d"); this.tempCanvas = document.createElement("canvas"); this.tempCanvas2D = this.tempCanvas.getContext("2d"); },圖片的讀取
此部分開始放在methods對(duì)象下
圖片讀取的功能主要設(shè)計(jì)兩個(gè)方法:
openWindow方法主要用于觸發(fā)隱藏的標(biāo)簽的文件讀取功能
//打開文件選擇窗口 openWindow() { this.input.click(); },
loadImg方法完成了以下幾個(gè)步驟
新建一個(gè)FileReader對(duì)象用來讀取選中的圖片文件
將原有的被選中的dataURL變量清空
將讀取的圖片文件轉(zhuǎn)為dataURL格式
將dataURL賦給一個(gè)創(chuàng)建的image對(duì)象
計(jì)算image對(duì)象的長(zhǎng)寬比決定圖片渲染方式
獲取canvas起點(diǎn)坐標(biāo)
將image對(duì)象中的圖像數(shù)據(jù)賦給canvas
//載入圖片方法,當(dāng)圖片被選中后,input的value發(fā)生改變時(shí)觸發(fā) loadImg() { let vm = this; let reader = new FileReader(); //每次載入后傳給父組件的dataURL清空 this.dataURL = ""; //文件為空時(shí)返回 if(this.input.files[0] == null) { return } //開始載入圖片,并將數(shù)據(jù)通過dataURL的方式讀取,展現(xiàn)載入層信息 this.loadFlag = 1; reader.readAsDataURL(this.input.files[0]); //讀取完成后將圖像的dataURL數(shù)據(jù)賦給image對(duì)象的src的屬性,使其加載圖像 reader.onload = function(e) { vm.imgObj.src = e.target.result; } //圖像加載完成,利用drawImage將image對(duì)象渲染至canvas this.imgObj.onload = function() { vm.loadFlag = 2; vm.noticeFlag = true; //計(jì)算載入圖像的長(zhǎng)寬比,決定圖片顯示方式 let ratioHW = (vm.imgObj.height/vm.imgObj.width) //每張圖片根據(jù)比例不同,總有一個(gè)方向占滿顯示區(qū)域 if(ratioHW > 1) { vm.inputAreaCanvas.height = vm.inputHeight; vm.inputAreaCanvas.width = vm.inputHeight / ratioHW; } else { vm.inputAreaCanvas.width = vm.inputWidth; vm.inputAreaCanvas.height = vm.inputWidth * ratioHW; } /* 181031改:其實(shí)并不用這兩個(gè)變量,直接用offset屬性即可 //獲取組件起點(diǎn)坐標(biāo) vm.resetX = vm.inputAreaCanvas.getBoundingClientRect().left; vm.resetY = vm.inputAreaCanvas.getBoundingClientRect().top; */ //將獲取的圖像數(shù)據(jù)選在至canvas vm.inputArea2D.clearRect(0, 0, vm.inputAreaCanvas.width, vm.inputAreaCanvas.height); vm.inputArea2D.drawImage(vm.imgObj, 0, 0, vm.inputAreaCanvas.width, vm.inputAreaCanvas.height); vm.inputArea2D.fillStyle = "rgba(0,0,0,0.5)"; //設(shè)定為半透明的黑色 vm.inputArea2D.fillRect(0, 0, vm.inputWidth, vm.inputHeight); //矩形A } },圖像的截取
圖像截取功能包含四個(gè)方法:
setStartPoint方法用于獲取截取范圍的起點(diǎn)以及更改點(diǎn)擊狀態(tài)
//獲取截取范圍起始坐標(biāo),當(dāng)鼠標(biāo)在canvas標(biāo)簽上點(diǎn)擊時(shí)觸發(fā) setStartPoint(e) { this.mouseDownFlag = true; //改變標(biāo)記狀態(tài),置為點(diǎn)擊狀態(tài) this.startX = e.offsetX //獲得起始點(diǎn)橫坐標(biāo) this.startY = e.offsetY //獲得起始點(diǎn)縱坐標(biāo) },
drawArea方法通過以下步驟實(shí)現(xiàn)了選定區(qū)域的展現(xiàn)和截取功能:
取得實(shí)時(shí)鼠標(biāo)坐標(biāo)作為截取區(qū)域的終點(diǎn)
在被選擇區(qū)域外繪制半透明蒙版
獲取將所選區(qū)域圖像對(duì)應(yīng)imageData數(shù)據(jù)
利用新建的canvas對(duì)象將imageData轉(zhuǎn)為dataURL
//選擇截取范圍,當(dāng)鼠標(biāo)被拖動(dòng)時(shí)觸發(fā) drawArea(e) { //當(dāng)鼠標(biāo)被拖動(dòng)時(shí)觸發(fā)(處于按下狀態(tài)且移動(dòng)) if(this.mouseDownFlag) { /*181031改:結(jié)束坐標(biāo)的獲取方式進(jìn)行了優(yōu)化,請(qǐng)忽略此處 //在canvas標(biāo)簽上范圍的終點(diǎn)橫坐標(biāo) this.resultX = parseInt(e.clientX - this.resetX); //在canvas標(biāo)簽上范圍的終點(diǎn)縱坐標(biāo),根據(jù)比例參數(shù)決定 if(this.cuttingRatio != 0) { //根據(jù)一定比例截取 this.resultY = this.startY + parseInt((1 / this.cuttingRatio) * (this.resultX - this.startX)) } else { //自由截取 this.resultY = parseInt(e.clientY - this.resetY); } */ //在canvas標(biāo)簽上范圍的終點(diǎn)橫坐標(biāo) this.resultX = e.offsetX; //在canvas標(biāo)簽上范圍的終點(diǎn)縱坐標(biāo),根據(jù)比例參數(shù)決定 if(this.cuttingRatio != 0) { //根據(jù)一定比例截取 this.resultY = this.startY + parseInt((1 / this.cuttingRatio) * (this.resultX - this.startX)) } else { //自由截取 this.resultY = e.offsetX; } //所選區(qū)域外陰影部分 this.inputArea2D.clearRect(0, 0, this.inputWidth, this.inputHeight); //清空整個(gè)畫面 this.inputArea2D.drawImage(this.imgObj, 0, 0, this.inputAreaCanvas.width, this.inputAreaCanvas.height); //重新繪制圖片 this.inputArea2D.fillStyle = "rgba(0,0,0,0.5)"; //設(shè)定為半透明的白色 this.inputArea2D.fillRect(0, 0, this.resultX, this.startY); //矩形A this.inputArea2D.fillRect(this.resultX, 0, this.inputWidth, this.resultY); //矩形B this.inputArea2D.fillRect(this.startX, this.resultY, this.inputWidth - this.startX, this.inputHeight - this.resultY); //矩形C this.inputArea2D.fillRect(0, this.startY, this.startX, this.inputHeight - this.startY); //矩形D //當(dāng)選擇區(qū)域大于0時(shí),將所選范圍內(nèi)的圖像數(shù)據(jù)實(shí)時(shí)返回 if(this.resultX - this.startX > 0 && this.resultY - this.startY > 0) { this.resultImgData = this.inputArea2D.getImageData(this.startX, this.startY, this.resultX - this.startX, this.resultY - this.startY); //canvas to DataURL this.tempCanvas.width = this.resultImgData.width; this.tempCanvas.height = this.resultImgData.height; this.tempCanvas2D.putImageData(this.resultImgData, 0, 0) this.dataURL = this.tempCanvas.toDataURL("image/jpeg", 1.0); } } },
reset方法用于重制鼠標(biāo)點(diǎn)擊狀態(tài),并獲取blob格式的所截圖像數(shù)據(jù),觸發(fā)getImageData事件將數(shù)據(jù)專遞給父組件
//結(jié)束選擇截取范圍,返回所選范圍的數(shù)據(jù),重制鼠標(biāo)狀態(tài),當(dāng)鼠標(biāo)點(diǎn)擊結(jié)束時(shí)觸發(fā) reset() { this.mouseDownFlag = false; //將標(biāo)志置為已抬起狀態(tài) let blob = this.dataURLtoBlob(this.dataURL) this.$emit("getImageData", blob); },
dataURLtoBlob方法的作用是將dataURL對(duì)象轉(zhuǎn)化為Blob對(duì)象,來自Blob/DataURL/canvas/image的相互轉(zhuǎn)換-Lorem
由于在IE中并不支持Canvas.toBlob,所以需要這里走個(gè)彎路,自己寫一下這個(gè)方法
//DataURL to Blob,兼容IE dataURLtoBlob(dataurl) { let arr = dataurl.split(",") let mime = arr[0].match(/:(.*?);/)[1] let bstr = atob(arr[1]) let n = bstr.length let u8arr = new Uint8Array(n) while(n--) { u8arr[n] = bstr.charCodeAt(n) } return new Blob([u8arr], { type: mime }); }其他方法
//關(guān)閉提示信息 closeNotice() { this.noticeFlag = false },
通過監(jiān)聽dataURL的變化,將結(jié)果實(shí)時(shí)返回給父組件以達(dá)到預(yù)覽的目的
watch:{ dataURL:function(newVal,oldVal){ this.$emit("getImageDataUrl", this.dataURL)//將所截圖的dataURL返回給父組件,共預(yù)覽使用 } },應(yīng)用方式
用起來嘛,就很簡(jiǎn)單了
htmljavascript 寫在后面
第一次寫相對(duì)獨(dú)立的組件
從有想法到完全實(shí)現(xiàn)成一個(gè)能用的組件,中間還是有很多路的,而且功能還簡(jiǎn)單的令人發(fā)質(zhì),怎么說呢感覺自己可以進(jìn)步的空間還很大啊
不過令人欣慰的是這個(gè)組件已經(jīng)用在單位的一個(gè)項(xiàng)目中了,可喜可賀
雖然拖了很久,不過還是有成就感的,希望能繼續(xù)下去,誰知道能走到哪呢
歡迎大家挑錯(cuò)提意見,雖然不情愿,但是接受
能看到這的,功能應(yīng)該都實(shí)現(xiàn)了把?!
181031修改之前應(yīng)該是腦子抽了,其實(shí)在drawArea方法中,e.offsetX和e.offsetY就是
parseInt(e.clientX - this.resetX) parseInt(e.clientY - this.resetY)181119修改
增加了選框拖拽功能,并且上傳到了github上,覺得好的給星哦!l-imgupload
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98508.html
摘要:表示不一定是原生形式的數(shù)據(jù)。接口基于,繼承了的功能并將其擴(kuò)展使其支持用戶系統(tǒng)上的文件。讀取操作完成的時(shí)候,會(huì)變成已完成,并觸發(fā)事件,同時(shí)屬性將包含一個(gè)格式的字符串編碼以表示所讀取文件的內(nèi)容。 溫馨提示:這里除了一些幼稚的小組件啥也沒有溫馨提示-續(xù):這是一個(gè)新的系列,寫一些實(shí)際開發(fā)中遇到的一些常用的功能,想法笨拙,代碼亂套 寫在前面 圖片上傳,作為web端一個(gè)常用的功能,在不同的項(xiàng)目中有...
摘要:背景最近在做一個(gè)的項(xiàng)目,接口寫的差不多了,后續(xù)大概要和前端對(duì)接。眾所周知后端和前端的溝通是非常耗時(shí)和費(fèi)力的,這時(shí)候有一個(gè)完善的接口文檔會(huì)帶來很大的幫助。就是一個(gè)非常好的選擇。結(jié)果啟動(dòng)應(yīng)用,訪問查看接口文檔。 背景 最近在做一個(gè)Spring Boot的項(xiàng)目,接口寫的差不多了,后續(xù)大概要和前端對(duì)接。眾所周知后端和前端的溝通是非常耗時(shí)和費(fèi)力的,這時(shí)候有一個(gè)完善的接口文檔會(huì)帶來很大的幫助。Sw...
摘要:發(fā)布應(yīng)用市場(chǎng)的平臺(tái)搶紅包工具紅包精靈開源啦掘金紅包精靈,如果喜歡,點(diǎn)個(gè)開源不易。作者將原素材文章進(jìn)行了新內(nèi)容的添加和重新排列,但是因?yàn)槲恼赂咝У拇a編寫技巧總結(jié)前端掘金本文總結(jié)了代碼編寫技巧,來提升你的和代碼。 收藏安卓開發(fā)中非常實(shí)用優(yōu)秀的庫(kù)! 有圖有真相! - Android - 掘金本來是打算收藏工具類的,但轉(zhuǎn)念一想,已經(jīng)有這么多優(yōu)秀的庫(kù)了,就沒必要再去重復(fù)造輪子了,便歸納工作中比...
摘要:術(shù)作者三畫,阿里巴巴技術(shù)專家,梓敬鵬升和余樂對(duì)此文亦有貢獻(xiàn)。接下來,阿里巴巴技術(shù)專家三畫,將分享自己和團(tuán)隊(duì)在畫好架構(gòu)圖方面的理念和經(jīng)驗(yàn),希望對(duì)你有所幫助。架構(gòu)是結(jié)構(gòu)和愿景。架構(gòu)圖的作用一圖勝千言。 showImg(https://segmentfault.com/img/bVbrpzm?w=1000&h=739);術(shù) 作者 | 三畫,阿里巴巴技術(shù)專家,梓敬、鵬升和余樂對(duì)此文亦有貢獻(xiàn)。...
閱讀 895·2021-11-15 11:38
閱讀 2532·2021-09-08 09:45
閱讀 2831·2021-09-04 16:48
閱讀 2579·2019-08-30 15:54
閱讀 944·2019-08-30 13:57
閱讀 1631·2019-08-29 15:39
閱讀 508·2019-08-29 12:46
閱讀 3534·2019-08-26 13:39