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