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