近日剛做的一個功能,要在app里使用內嵌頁面進行圖像的上傳。
從功能上看,原生的實現應該是最好的。畢竟頁面上所有的東西都隔着一個瀏覽器,所有的實現都要依賴瀏覽器提供的接口,不同的瀏覽器對接口的實現又有差異……到最后又會陷入兼容性的大坑!
吐槽歸吐槽,但是折騰的勁頭不能丟!
使用input file[camera]屬性調用相機
簡直So easy!
<input type="file" accept="image/*;" capture="camera" >
只需要這么一條簡單的代碼,在手機瀏覽器點擊就可以打開相機了。
capture
是什么?其實就是對打開方式的設置。
<!-- capture=camcorder,調用手機攝像功能 --> <input type="file" accept="video/*" capture="camcorder" > <!-- capture=microphone,調用手機錄音功能 --> <input type="file" accept="audio/*" capture="microphone" >
魅族MX5測試結果:
- 谷歌瀏覽器可以打開相機和攝像功能,其他方式均為相機、圖庫、文件管理器等混合選擇項。
- 自帶瀏覽器打開均為文件管理器。
由此說明此屬性兼容性還是個問題。不過這並不能阻止我繼續折騰下去!
圖片壓縮
在如今這個手機普遍千萬像素的時代,一張照片動輒5M的大小。作為一個良心的開發者,我們是要為用戶的流量負責的。
該怎么做?我也不知道。大家都在用canvas
實現,我也就用了。
document.getElementById('file').addEventListener('change', function() { var reader = new FileReader(); reader.onload = function (e) { compress(this.result); }; reader.readAsDataURL(this.files[0]); }, false);
不管文件域是用何種方式打開的,都可以在 change
事件中獲取到選擇的文件或拍攝的照片。
創建一個FileReader
對象,我們需要調用readAsDataURL
把文件轉換為base64
圖像編碼,如data:image/jpeg;base64……
這種格式。onload
是一個異步回調,當文件讀取完執行該方法內代碼。this.result
記錄讀取結果,如果讀取失敗,該值為null
。在這里進行圖片壓縮的具體操作。
var compress = function (res) {
var img = new Image(), maxH = 160; img.onload = function () { var cvs = document.createElement('canvas'), ctx = cvs.getContext('2d'); if(img.height > maxH) { img.width *= maxH / img.height; img.height = maxH; } cvs.width = img.width; cvs.height = img.height; ctx.clearRect(0, 0, cvs.width, cvs.height); ctx.drawImage(img, 0, 0, img.width, img.height); var dataUrl = cvs.toDataURL('image/jpeg', 0.6); // 上傳略 } img.src = res; }
創建一個Image
對象,給src
屬性賦值為讀取結果,同樣在onload
異步回調中編寫處理圖片的代碼。
這里就要開始使用canvas
進行圖片壓縮了。
首先是尺寸按比例縮放,然后把圖片繪到畫布上,最后調用toDataURL
方法壓縮圖像質量。
context.toDataURL('MIME類型', 圖像質量0-1); // 該方法返回base64圖像編碼
代碼里省略了一些校監操作,如文件類型約束和文件大小判斷(小於一定值可以不壓縮)。
最后就是把數據發送到后端的操作,這里就不說了。
Html5調用攝像頭
通過以上的代碼已經可以實現調用手機相機拍照、壓縮、上傳這一整套流程了。
不過在折騰的過程中也發現了一種調用攝像頭的方法。注意,是攝像頭
!使用input調用的是相機。其中的差別就是攝像頭是只捕獲畫面,相機還包括原生的一些拍照、設置等控件。
通過對攝像頭的調用可以做很多有趣的事,比如拍照美化、濾鏡等。可以說實現一個第三方相機是沒問題的。
之前下載過一款安卓相機APP,不到100K的大小,可以實現拍照的一些風格化,也許就是Html5實現的呢。
需要用到的是 getUserMedia API,具體的實現這里就不貼了。
【用到的HTML5標簽】
1
|
<
input
type
=
"file"
capture
=
"camera"
accept
=
"image/*"
id
=
"cameraInput"
name
=
"cameraInput"
class
=
"sign_file"
/>
|
【等比縮放圖片】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
function
drawOnCanvas(file) {
var
reader =
new
FileReader();
reader.onload =
function
(e) {
var
dataURL = e.target.result,
canvas = document.querySelector(
'canvas'
),
ctx = canvas.getContext(
'2d'
),
img =
new
Image();
img.onload =
function
() {
var
square = 320;
canvas.width = square;
canvas.height = square;
var
context = canvas.getContext(
'2d'
);
context.clearRect(0, 0, square, square);
var
imageWidth;
var
imageHeight;
var
offsetX = 0;
var
offsetY = 0;
if
(
this
.width >
this
.height) {
imageWidth = Math.round(square *
this
.width /
this
.height);
imageHeight = square;
offsetX = -Math.round((imageWidth - square) / 2);
}
else
{
imageHeight = Math.round(square *
this
.height /
this
.width);
imageWidth = square;
offsetY = -Math.round((imageHeight - square) / 2);
}
context.drawImage(
this
, offsetX, offsetY, imageWidth, imageHeight);
var
base64 = canvas.toDataURL(
'image/jpeg'
, 0.5);
$(
'#j_thumb'
).val(base64.substr(22));
};
img.src = dataURL;
};
reader.readAsDataURL(file);
}
|
FileReader對象是用來解析file控件獲取的本地圖片地址的,具體介紹請百度一下。把解析好的地址設置給IMG標簽的SRC屬性,然后通過canvas對象把圖片繪制; 在這過程中就有個等比縮放的算法,再用drawImage方法把圖像畫到canvas中。
【如何獲取畫好的圖片數據傳到后端處理】
通過 canvas.toDataURL(‘image/jpeg’,0.5)就可以獲取到base64編碼值,然后你就可以按照傳統的POST或者AJAX方式處理了。
【讓圖片顯示】
1
2
3
4
|
document.querySelector(
'input[type=file]'
).onchange =
function
() {
var
file = input.files[0];
drawOnCanvas(file);
};
|
【后台處理方式】
1
2
3
|
$base64
=
$_POST
[
'formFile'
];
$IMG
=
base64_decode
(
$base64
);
file_put_contents
(
'1.png'
,
$IMG
);
|
根傳統的上傳圖片不同,這時候后台需要用base64_decode解碼