摘要:表示不一定是原生形式的數(shù)據(jù)。接口基于,繼承了的功能并將其擴(kuò)展使其支持用戶系統(tǒng)上的文件。讀取操作完成的時候,會變成已完成,并觸發(fā)事件,同時屬性將包含一個格式的字符串編碼以表示所讀取文件的內(nèi)容。
溫馨提示:這里除了一些幼稚的小組件啥也沒有
溫馨提示-續(xù):這是一個新的系列,寫一些實際開發(fā)中遇到的一些常用的功能,想法笨拙,代碼亂套
圖片上傳,作為web端一個常用的功能,在不同的項目中有不同的需求,在這里實現(xiàn)一個比價基本的上傳圖片插件,主要能實現(xiàn)圖片的瀏覽,剪裁,上傳這三個功能,同時也是為了讓自己對圖片/文件上傳和HTML5中名聲在外的canvas相關(guān)能夠有一些了解
上傳到了github上,覺得好的給星哦!l-imgupload //181119
我就要自行車 - 需求整理放眼WWW,一般的圖片上傳模塊,主要就是實現(xiàn)了三個功能,圖片的預(yù)覽,圖片的剪裁及預(yù)覽,圖片的上傳,那我也就整這么一個吧,再細(xì)化一下需求
圖片的預(yù)覽用戶使用:用戶點擊“選擇圖片”,彈出文件瀏覽器,可以選擇本地的圖片,點擊確認(rèn)后,所選圖片會按照原始比例出現(xiàn)在頁面的瀏覽區(qū)域中
組件調(diào)用:開發(fā)者可以自己定義圖片預(yù)覽區(qū)域的大小,并限定所傳圖片的文件大小和尺寸大小
用戶使用:用戶根據(jù)提示,在預(yù)覽區(qū)域的圖片上拖動鼠標(biāo)框出想要上傳的圖片區(qū)域,并且能在結(jié)果預(yù)覽區(qū)域看到自己的剪裁結(jié)果
組件調(diào)用:開發(fā)者可以自定義是否剪裁圖片,并可以定義是否限定剪裁圖片的大小及比例,并且設(shè)定具體大小及比例
用戶使用:用戶點擊“圖片上傳”,圖片開始上傳,現(xiàn)實“上傳中…”,完成后顯示“上傳完成”
組件調(diào)用:開發(fā)者得到base64格式的urlData圖片,自己編寫調(diào)用Ajax的函數(shù)及其回調(diào)函數(shù)
作為設(shè)計師,扔圖是我的最愛,畫了一套全功能,包含剪裁及剪裁瀏覽的原型圖
state-1:初始狀態(tài)
state-2:點擊"選擇圖片",瀏覽本地后載入圖片
state-3:剪裁,在圖片區(qū)域上拖動鼠標(biāo)選擇要剪裁的部分,確認(rèn)要上傳的部分
自打干上web開發(fā)這活,就都是在搗鼓瀏覽器內(nèi)部這點事,從沒想過跟瀏覽器之外計算機(jī)本地的一些文件能發(fā)生什么關(guān)系。但是該來的總要來,既然要上傳圖片,就肯定要從計算機(jī)本地來選擇文件并在瀏覽器內(nèi)打開,這歷史性的對話就要這么開啟了…
圖片的選擇其實在HTML中的標(biāo)簽就提供了瀏覽本地文件的功能,前提是type="file",真是很講道理… 試過就知道一點擊就會打開文件瀏覽器
但這么做有兩個經(jīng)典的問題:
第一,會有一個輸入框傻乎乎的在那里…
第二,我用的是Ajax,怎么才能get到表單當(dāng)中的文件呢
對于問題一,很好解決直接各種方式hide這個input標(biāo)簽即可,再主動觸發(fā)click()
var imgFrom = document.getElementById("inputArea"); function loadImg(){ imgFrom.click(); }
對于問題二,這就要介紹一下FormData對象了
XMLHttpRequest Level 2添加了一個新的接口FormData.利用FormData對象,我們可以通過JavaScript用一些鍵值對來模擬一系列表單控件,我們還可以使用XMLHttpRequest的send()方法來異步的提交這個"表單".比起普通的ajax,使用FormData的最大優(yōu)點就是我們可以異步上傳一個二進(jìn)制文件.
摘自MDN Web docs - Web技術(shù)文檔/Web API 接口/FormData
正如上面的文檔所說FormData對象可以干的事無非就是用javascript模擬表單控件,也正因為如此所以可以在模擬的表單中放入一個文件
var myFrom = new FormData(); var imageData = imgFrom.files[0];//獲取表單中第一個文件 myFrom.append("image",imageDate);//向表單中添加一個鍵值對 console.log(myFrom.getAll("image"));//獲取表單中image字段對應(yīng)的值,結(jié)果見下圖
正如我們所見,文件我們已經(jīng)通過Web拿到手了
既然是要上傳圖片,我們肯定得知道自己傳的是啥圖片啊,所以下一步就是如何把讀取的圖片展現(xiàn)在頁面上了,正如上圖中的顯示,我的得到的圖片是一個File對象,而File對象是特殊的Blob對象,那Blob對象又是個啥呢…
Blob 對象表示不可變的類似文件對象的原始數(shù)據(jù)。Blob表示不一定是JavaScript原生形式的數(shù)據(jù)。File 接口基于Blob,繼承了 blob的功能并將其擴(kuò)展使其支持用戶系統(tǒng)上的文件。
摘自MDN Web docs - Web技術(shù)文檔/Web API 接口/Blob
說實話,真是懵逼
但仔細(xì)理解下大概意思就是Blob對象是用來表示/承載文件對象的原始數(shù)據(jù)(二進(jìn)制)的,借助一些博文會有助于理解
js中關(guān)于Blob對象的介紹與使用 - 可樂Script
HTML5 Blob對象 - zdy0_2004
說到底,重點不在這,了解一下有個概念即可,重點在于我們怎么展示這個File對象
這就要請出FileReader對象了
FileReader 對象允許Web應(yīng)用程序異步讀取存儲在用戶計算機(jī)上的文件(或原始數(shù)據(jù)緩沖區(qū))的內(nèi)容,使用 File 或 Blob 對象指定要讀取的文件或數(shù)據(jù)。
摘自MDN Web docs - Web技術(shù)文檔/Web API 接口/FileReader
不難看出,FileReader對象就是用來讀取本地文件的,而這其方法readAsDataURL()就是我們要用的東西啦
該方法會讀取指定的 Blob 或 File 對象。讀取操作完成的時候,readyState 會變成已完成(DONE),并觸發(fā) loadend 事件,同時 result 屬性將包含一個data:URL格式的字符串(base64編碼)以表示所讀取文件的內(nèi)容。
摘自MDN Web docs - Web技術(shù)文檔/Web API 接口/FileReader/FileReader.readAsDataURL()
這里面又提到一個新名詞data:URL,也就是說readAsDataURL()的作用就是能把文件轉(zhuǎn)換為data:URL,不過這個data:URL又是什么呢,執(zhí)行來看看
var reader = new FileReader(); //調(diào)用FileReader對象 reader.readAsDataURL(imgData); //通過DataURL的方式返回圖像 reader.onload = function(e) { console.log(e.target.result);//看看你是個啥 }
控制臺的結(jié)果全臉懵逼
可以通過這篇文章去大概了解一下DATA URL簡介及DATA URL的利弊 - 薛陳磊
說到底這dataURL我就粗略的理解它為URL形式的data,也就是說這段URL并不是與普通的URL一樣指向某個地址,而是它本身就是數(shù)據(jù),我們試著把這一堆字符粘到一個的src屬性中
終于看到了,結(jié)果正如所料,將這段包含了數(shù)據(jù)的URL賦給一個確實可以讓數(shù)據(jù)被展現(xiàn)為圖片
至此,我們實現(xiàn)了本地文件的讀取及展現(xiàn)
溫馨提示-亂入:看明白這里需要對canvas有基本的了解MDN Web docs - Web技術(shù)文檔/Web API接口/Canvas/Canvas教程
在Web上對圖像進(jìn)行操作,沒有比canvas相關(guān)技術(shù)更合適的了,所以本文用canvas技術(shù)來實現(xiàn)對圖片的截取
canvas中的圖片展現(xiàn)在上文中,我們利用展現(xiàn)出了我們選擇的圖片,但是我們的圖片截取功能可是要利用來實現(xiàn)的,所以怎么在中展現(xiàn)我們剛才獲取的圖片就是下一步要干的事情了
canvas的API中自帶drawImage()函數(shù),其作用就是在中渲染一張圖片出來,其可以支持多種圖片來源見MDN Web docs - Web技術(shù)文檔/Web API接口/CanvasRenderingContext2D/CanvasRenderingContext2D.drawImage()
最簡單的,我們直接把剛剛顯示圖片的那個傳入是不是就可以呢
var theCanvas = document.getElementById("imgCanvas"); var canvasImg = theCanvas.getContext("2d");//獲取2D渲染背景 var img = document.getElementById("image"); img.onload = function(){//確認(rèn)圖片已載入 canvasImg.drawImage(img,0,0); }
結(jié)果如下
從圖中看,左側(cè)是之前的"",右側(cè)是渲染了圖片信息的
這么看來雖然成功?在中渲染出了圖片但是有兩個明顯的問題
1.左邊的""留著干啥?
2.右邊看上去是不是有點不一樣?
這倆問題其實都好辦,針對第一個問題,我們其實可以根本不用實體的""直接利用"Image"對象即可,第二個問題明顯是因為的大小與獲取到的圖片大小不一致所產(chǎn)生的,綜合這兩點,對代碼進(jìn)行進(jìn)化!
var theCanvas = document.getElementById("imgCanvas"); var canvasImg = theCanvas.getContext("2d"); var img = new Image();//創(chuàng)建img對象 reader.onload = function(e) { img.src = e.target.result; } img.onload = function(){ theCanvas.Width = img.width;//將img對象的長款賦給canvas標(biāo)簽 theCanvas.height = img.height; canvasImg.drawImage(img,0,0); }
結(jié)果與我們所期待的一樣,至此我們成功的在"
canvas中圖片的截取其實截圖,說白了就是在一個圖像上,獲取某個區(qū)域中的圖像信息
canvas作為專門用來處理圖像及像素相關(guān)的一套API,獲取區(qū)域中的相關(guān)圖像信息可以說是再簡單不過的事情,利用getImageData()函數(shù)即可 //詳情,當(dāng)然我們不光要把圖像信息獲取到,最好還能展現(xiàn)出來我們的截圖結(jié)果,這里就要用到與之相對的putImageData()函數(shù) //詳情
var resultCanvas = document.getElementById("resultCanvas"); var resultImg = resultCanvas.getContext("2d"); var cutData = canvasImg.getImageData(100,100,200,200); resultImg.putImageData(cutData,0,0);
結(jié)果如圖
既然這個工具是面向用戶的,截圖的過程肯定是要所見即所得的,在函數(shù)getImageData()中有4個參數(shù),分別是截圖起點的兩個坐標(biāo)和區(qū)域的寬度及高度,所以問題就變成了如何更合理的讓用戶輸入這4個值。
其實現(xiàn)存的主流解決方案就做的非常好了:在圖上拖動鼠標(biāo),拉出一個框,這個框內(nèi)就是用戶希望截取的區(qū)域。
在畫布上畫出一個框很簡單,只需用到strokeRect()函數(shù) //詳情
但是讓用戶自己拖出一個框就比較復(fù)雜了,先分析一下用戶的一套動作都有什么
用戶選定起始點,點下鼠標(biāo)左鍵
用戶選定截圖區(qū)域的大小,保持鼠標(biāo)左鍵不抬起,同時移動鼠標(biāo)選擇
用戶完成選擇,抬起鼠標(biāo)左鍵
回過頭再來看程序需要干什么
獲取起始點的坐標(biāo),并記錄為已點擊狀態(tài)
判斷一下如果為已點擊狀態(tài)那么,獲取每一次移動/幀的鼠標(biāo)坐標(biāo),并計算出與起始點之間的橫縱坐標(biāo)距離,而這距離就是所畫框的長度和寬度,清除上一幀的整個畫面,再繪制一個新的圖片再畫一個新的框,同時按照框的起始坐標(biāo)及寬高,截取圖像信息,再清除預(yù)覽區(qū)域的上一幀的畫布,再將這一幀的圖像信息載入
鼠標(biāo)抬起后,停止記錄及繪制,保持最終一幀的框停留在畫面上
在這里,要說明一下,為什么非要清除整個畫面不可,其實可以把通過canvas.getContext("2d")獲取到的2D 畫布的渲染上下文 //詳情 就當(dāng)作一塊畫布,已經(jīng)渲染出來的東西就已經(jīng)留在了上面,無法再修改,如果想要更改畫面上已經(jīng)存在的元素的大小位置形狀等等屬性,那么在程序?qū)用?,?strong>只能(個人理解,不一定對,如果有問題請一定跟我嘮嘮)把之前的畫布清空再重新渲染。
這個思路與我們之前端開發(fā)中動畫相關(guān)的開發(fā)思路不同,并不是像之前那樣直接操作現(xiàn)有元素屬性就可以改變該元素在畫面上的呈現(xiàn)結(jié)果的,而在這里其實更像是在現(xiàn)實生活中的動畫制作原理就是
每一幀都需要重新繪制整張畫面
而其實這是任何動畫渲染方式的最底層思路與行為
話說回來按照上文相關(guān)的開發(fā)思路,實現(xiàn)這個功能的代碼如下
var flag = false;//記錄是否為點擊狀態(tài)的標(biāo)記 var W = img.width; var H = img.height; var startX = 0; var startY = 0; //當(dāng)鼠標(biāo)被按下 theCanvas.addEventListener("mousedown", e => { flag = true;//改變標(biāo)記狀態(tài),置為點擊狀態(tài) //startX = e.clientX;//獲得起始點橫坐標(biāo) //startY = e.clientY;//獲得起始點縱坐標(biāo) //添加于2018.3.6: //這里有些問題,在本文的條件下e.clientX是對的,可是其實是應(yīng)該為相對對象的坐標(biāo)而不是瀏覽器,所以應(yīng)該為e.offsetX 感謝 @高遠(yuǎn) 同學(xué)提醒 startX = e.offsetX;//獲得起始點橫坐標(biāo) startY = e.offsetY;//獲得起始點縱坐標(biāo) }) //當(dāng)鼠標(biāo)在移動 theCanvas.addEventListener("mousemove", e => { if(flag){//判斷鼠標(biāo)是否被拖動 canvasImg.clearRect(0,0,W,H);//清空整個畫面 canvasImg.drawImage(img,0,0);//重新繪制圖片 canvasImg.strokeRect(startX, startY, e.clientX - startX, e.clientY - startY);//繪制黑框 resultImg.clearRect(0,0,cutData.width,cutData.height);//清空預(yù)覽區(qū)域 cutData = canvasImg.getImageData(startX, startY, e.clientX - startX, e.clientY - startY);//截取黑框區(qū)域圖片信息 resultImg.putImageData(cutData,0,0);//將圖片信息賦給預(yù)覽區(qū)域 } }) //當(dāng)鼠標(biāo)左鍵抬起 theCanvas.addEventListener("mouseup", e => { flag = false;//將標(biāo)志置為已抬起狀態(tài) })
結(jié)果如圖
主要吧,這個黑框太丑了,透露著一種原始和狂野,以及來自工科男審美的粗糙感…
能不能弄的好看點,起碼讓它看上去是一個工具不是一個實驗
我的想法是這樣的,待被截取的圖片上應(yīng)該蒙上一層半透明白色遮罩,用戶框選出的部分是沒有遮罩的,這樣效果可以為功能增加視覺上的材質(zhì)感及舒適感,同時顯得高端
具體效果是這樣的-下圖來自ps
是不是稍微好些了
可是,怎么實現(xiàn)?
簡單來說,就是在原有的畫布上再蒙半透明的一層畫布,然后讓這一層有一部分是沒有的就可以實現(xiàn)了,總的來說就是蒙版和遮罩的思路,在canvas中也有相關(guān)的api,但是我愣是沒看明白
負(fù)責(zé)任的貼一個鏈接
不過開發(fā)就是這樣,條條大路出bug
我想到這個功能的瞬間腦子像抽了一樣,出現(xiàn)了這么一種實現(xiàn)方法
見下圖
mask層可以分為A,B,C,D四個矩形區(qū)域,在圖中兩個藍(lán)色的點是已知的(用戶自己畫出來的),在下層圖片大小已知的前提下,這四個矩形區(qū)域的四個點都是可以計算出來的,從而其高度和寬度也可以計算出來,這樣就可以利用這些數(shù)據(jù)畫出一個半透明的矩形,將四個半透明矩形都畫出來后,就能夠?qū)崿F(xiàn)之前設(shè)計出的效果了,具體代碼如下
theCanvas.addEventListener("mousemove", e => { if(flag){ canvasImg.clearRect(0,0,W,H); resultImg.clearRect(0,0,cutData.width,cutData.height); canvasImg.drawImage(img,0,0); canvasImg.fillStyle = "rgba(255,255,255,0.6)";//設(shè)定為半透明的白色 canvasImg.fillRect(0, 0, e.clientX, startY);//矩形A canvasImg.fillRect(e.clientX, 0, W, e.clientY);//矩形B canvasImg.fillRect(startX, e.clientY, W-startX, H-e.clientY);//矩形C canvasImg.fillRect(0, startY, startX, H-startY);//矩形D cutData = canvasImg.getImageData(startX, startY, e.clientX - startX, e.clientY - startY); resultImg.putImageData(cutData,0,0); } })
效果如圖
沒有什么把自己的腦殘想法實現(xiàn)更爽的了
至此,截圖的基本功能都實現(xiàn)了,但還差最后一步
另一次歷史性的對話 - 圖片上傳圖片已經(jīng)截出來了,下一步就是怎么上傳了,通過Ajax上傳,需要將圖像數(shù)據(jù)轉(zhuǎn)化為File,而在canvas的API中自帶toBlob()函數(shù) //詳情
var resultFile = {} theCanvas.addEventListener("mouseup", e => { resultCanvas.toBlob(blob => { resultFile = blob; console.log(blob);//Blob(1797) {size: 1797, type: "image/png"} } }) flag = false; })
然后就可以用Ajax上傳拉,具體怎么上傳就需要具體問題具體分析了
至此,整個插件的思路及需要用到相關(guān)技術(shù)都捋清楚了,接下來就可以開始按照上文的需求進(jìn)行開發(fā)了,而這是下一篇文章要講的事情了
能看到這的絕對很閑
這篇文章的長度讓我想起讀研時被畢業(yè)論文統(tǒng)治的恐懼
本來想著連同組件開發(fā)一起在一篇內(nèi)寫完呢,但是實在太長還是放棄了
身體和家人都是最重要的,今年還沒過一個月就被上了很多課
前端時間寫完了實踐是檢驗程序員的唯一標(biāo)準(zhǔn)02:用戶不想跟你說話并向你扔出一張圖片 - 圖片上傳組件開發(fā)【開發(fā)篇】,逐行代碼說明的- -
增加了選框拖拽功能,并且上傳到了github上,覺得好的給星哦!l-imgupload
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/107031.html
摘要:溫馨提示這里除了一些幼稚的小組件啥也沒有寫在前面距離寫完上一篇實踐是檢驗程序員的唯一標(biāo)準(zhǔn)用戶不想跟你說話并向你扔出一張圖片圖片上傳組件開發(fā)思路篇過去了大半年,才開始寫開發(fā)篇真的是令人悲哀,不過有句話說的好,開始做一件事最好的時間是大半年前 溫馨提示:這里除了一些幼稚的小組件啥也沒有 寫在前面 距離寫完上一篇實踐是檢驗程序員的唯一標(biāo)準(zhǔn)01:用戶不想跟你說話并向你扔出一張圖片 - 圖片上傳...
摘要:背景最近在做一個的項目,接口寫的差不多了,后續(xù)大概要和前端對接。眾所周知后端和前端的溝通是非常耗時和費力的,這時候有一個完善的接口文檔會帶來很大的幫助。就是一個非常好的選擇。結(jié)果啟動應(yīng)用,訪問查看接口文檔。 背景 最近在做一個Spring Boot的項目,接口寫的差不多了,后續(xù)大概要和前端對接。眾所周知后端和前端的溝通是非常耗時和費力的,這時候有一個完善的接口文檔會帶來很大的幫助。Sw...
摘要:發(fā)布應(yīng)用市場的平臺搶紅包工具紅包精靈開源啦掘金紅包精靈,如果喜歡,點個開源不易。作者將原素材文章進(jìn)行了新內(nèi)容的添加和重新排列,但是因為文章高效的代碼編寫技巧總結(jié)前端掘金本文總結(jié)了代碼編寫技巧,來提升你的和代碼。 收藏安卓開發(fā)中非常實用優(yōu)秀的庫! 有圖有真相! - Android - 掘金本來是打算收藏工具類的,但轉(zhuǎn)念一想,已經(jīng)有這么多優(yōu)秀的庫了,就沒必要再去重復(fù)造輪子了,便歸納工作中比...
摘要:我們參考小程序的設(shè)計思路進(jìn)行了優(yōu)化升級,為每一個需要特有化配置的頁面添加一個格式的配置文件,配置文件包括導(dǎo)航欄的配置頁面級別的配置跳轉(zhuǎn)的配置等,將配置工程化標(biāo)準(zhǔn)化。設(shè)置導(dǎo)航欄按鈕包含按鈕樣式的數(shù)組通過完成按鈕事件的回調(diào)。一、背景1.為什么是Weex在公司快速發(fā)展的大環(huán)境下,App的更新迭代高速、高頻,技術(shù)團(tuán)隊平均兩周便可誕生一款中型App,但App團(tuán)隊只有6個人(iOS 、Android各3...
摘要:術(shù)作者三畫,阿里巴巴技術(shù)專家,梓敬鵬升和余樂對此文亦有貢獻(xiàn)。接下來,阿里巴巴技術(shù)專家三畫,將分享自己和團(tuán)隊在畫好架構(gòu)圖方面的理念和經(jīng)驗,希望對你有所幫助。架構(gòu)是結(jié)構(gòu)和愿景。架構(gòu)圖的作用一圖勝千言。 showImg(https://segmentfault.com/img/bVbrpzm?w=1000&h=739);術(shù) 作者 | 三畫,阿里巴巴技術(shù)專家,梓敬、鵬升和余樂對此文亦有貢獻(xiàn)。...
閱讀 3208·2021-11-22 14:45
閱讀 3372·2019-08-29 13:11
閱讀 2374·2019-08-29 12:31
閱讀 976·2019-08-29 11:21
閱讀 3048·2019-08-29 11:09
閱讀 3676·2019-08-28 18:11
閱讀 1488·2019-08-26 13:58
閱讀 1349·2019-08-26 13:27