關於vue項目中移動端實現用戶選擇照片、照片裁剪、一次上傳多張圖片功能(仿微信發朋友圈)。
最終要實現的效果如下圖所示:
涉及的功能有
1、圖片的選擇
2、圖片從手機相機選擇、拍照
3、圖片選擇後的裁剪
4、圖片在頁面的顯示效果
5、圖片的刪除
6、base64圖片轉化爲file類型的文件
7、圖片的上傳
首先安裝cropperjs 和exif-js 裁剪依賴這兩個包
cnpm install --save cropperjs exif-js
圖片的上傳相關代碼
圖片從手機相機選擇、拍照
圖片選擇後的裁剪
initilize(opt) {
let self = this;
this.options = opt;
//創建dom
this.createElement();
this.resultObj = opt.resultObj;
//初始化裁剪對象
this.cropper = new Cropper(this.preview, {
aspectRatio: opt.aspectWithRatio / opt.aspectHeightRatio,
autoCropArea: opt.autoCropArea || 0.8,
viewMode: 2,
guides: true,
cropBoxResizable: true, //是否通過拖動來調整剪裁框的大小
cropBoxMovable: true, //是否通過拖拽來移動剪裁框。
dragCrop: false,
dragMode: "move", //‘crop’: 可以產生一個新的裁剪框3 ‘move’: 只可以移動3 ‘none’: 什麼也不處理
center: true,
zoomable: true, //是否允許放大圖像。
zoomOnTouch: true, //是否可以通過拖動觸摸來放大圖像。
scalable: true,
// minCropBoxHeight: 750,
// minCropBoxWidth: 750,
background: false,
checkOrientation: true,
checkCrossOrigin: true,
zoomable: false,
zoomOnWheel: false,
center: false,
toggleDragModeOnDblclick: false,
ready: function() {
if (opt.aspectRatio == "Free") {
let cropBox = self.cropper.cropBox;
cropBox.querySelector("span.cropper-view-box").style.outline =
"none";
self.cropper.disable();
}
}
});
},
//創建一些必要的DOM,用於圖片裁剪
createElement() {
//初始化圖片爲空對象
this.preview = null;
let str =
'<div><img id="clip_image" src="originUrl"></div><button type="button" id="cancel_clip">取消</button><button type="button" id="clip_button">確定</button>';
str +=
'<div class="crop_loading"><div class="crop_content"><div class="crop_text">圖片修剪中...</div></div></div>';
str +=
'<div class="crop_success"><div class="crop_success_text">上傳成功</div></div></div>';
let body = document.getElementsByTagName("body")[0];
this.reagion = document.createElement("div");
this.reagion.id = "clip_container";
this.reagion.className = "container";
this.reagion.innerHTML = str;
body.appendChild(this.reagion);
this.preview = document.getElementById("clip_image");
//綁定一些方法
this.initFunction();
},
//初始化一些函數綁定
initFunction() {
let self = this;
this.clickBtn = document.getElementById("clip_button");
this.cancelBtn = document.getElementById("cancel_clip");
//確定事件
this.addEvent(this.clickBtn, "click", function() {
self.crop();
});
//取消事件
this.addEvent(this.cancelBtn, "click", function() {
self.destoried();
});
//清空input的值
this.addEvent(this.fileObj, "click", function() {
this.value = "";
});
},
//外部接口,用於input['file']對象change時的調用
clip(e, opt) {
let self = this;
this.fileObj = e.srcElement;
let files = e.target.files || e.dataTransfer.files;
if (!files.length) return false; //不是圖片直接返回
//調用初始化方法
this.initilize(opt);
//獲取圖片文件資源
this.picValue = files[0];
//調用方法轉成url格式
this.originUrl = this.getObjectURL(this.picValue);
//每次替換圖片要重新得到新的url
if (this.cropper) {
this.cropper.replace(this.originUrl);
}
},
//圖片轉碼方法
getObjectURL(file) {
let url = null;
if (window.createObjectURL != undefined) {
// basic
url = window.createObjectURL(file);
} else if (window.URL != undefined) {
// mozilla(firefox)
url = window.URL.createObjectURL(file);
} else if (window.webkitURL != undefined) {
// webkit or chrome
url = window.webkitURL.createObjectURL(file);
}
return url;
},
//點擊確定進行裁剪
crop() {
let self = this;
let image = new Image();
let croppedCanvas;
let roundedCanvas;
// Crop
document.querySelector(".crop_loading").style.display = "block";
setTimeout(function() {
croppedCanvas = self.cropper.getCroppedCanvas();
// Round
roundedCanvas = self.getRoundedCanvas(croppedCanvas);
let imgData = roundedCanvas.toDataURL();
image.src = imgData;
//判斷圖片是否大於100k,不大於直接上傳,反之壓縮
if (imgData.length < 100 * 1024) {
// self.resultObj.src = imgData;
//圖片上傳
self.postImg(imgData);
} else {
image.onload = function() {
//壓縮處理
let data = self.compress(image, self.Orientation);
// self.resultObj.src = data;
//圖片上傳
self.postImg(data);
};
}
}, 20);
},
//獲取裁剪圖片資源
getRoundedCanvas(sourceCanvas) {
let canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
let width = sourceCanvas.width;
let height = sourceCanvas.height;
canvas.width = width;
canvas.height = height;
context.imageSmoothingEnabled = true;
context.drawImage(sourceCanvas, 0, 0, width, height);
context.globalCompositeOperation = "destination-in";
context.beginPath();
context.rect(0, 0, width, height);
context.fill();
return canvas;
},
//銷燬原來的對象
destoried() {
let self = this;
//移除事件
this.removeEvent(this.clickBtn, "click", null);
this.removeEvent(this.cancelBtn, "click", null);
this.removeEvent(this.fileObj, "click", null);
//移除裁剪框
this.reagion.parentNode.removeChild(this.reagion);
//銷燬裁剪對象
this.cropper.destroy();
this.cropper = null;
},
//圖片上傳
postImg(imageData) {
console.log(imageData);
this.$emit("callback", imageData);
//這邊寫圖片的上傳
let self = this;
self.destoried();
this.imgList.push(imageData);
},
//圖片旋轉
rotateImg(img, direction, canvas) {
//最小與最大旋轉方向,圖片旋轉4次後回到原方向
const min_step = 0;
const max_step = 3;
if (img == null) return;
//img的高度和寬度不能在img元素隱藏後獲取,否則會出錯
let height = img.height;
let width = img.width;
let step = 2;
if (step == null) {
step = min_step;
}
if (direction == "right") {
step++;
//旋轉到原位置,即超過最大值
step > max_step && (step = min_step);
} else {
step--;
step < min_step && (step = max_step);
}
//旋轉角度以弧度值爲參數
let degree = (step * 90 * Math.PI) / 180;
let ctx = canvas.getContext("2d");
switch (step) {
case 0:
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0);
break;
case 1:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, 0, -height);
break;
case 2:
canvas.width = width;
canvas.height = height;
ctx.rotate(degree);
ctx.drawImage(img, -width, -height);
break;
case 3:
canvas.width = height;
canvas.height = width;
ctx.rotate(degree);
ctx.drawImage(img, -width, 0);
break;
}
},
//圖片壓縮
compress(img, Orientation) {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
//瓦片canvas
let tCanvas = document.createElement("canvas");
let tctx = tCanvas.getContext("2d");
let initSize = img.src.length;
let width = img.width;
let height = img.height;
//如果圖片大於四百萬像素,計算壓縮比並將大小壓至400萬以下
let ratio;
if ((ratio = (width * height) / 4000000) > 1) {
console.log("大於400萬像素");
ratio = Math.sqrt(ratio);
width /= ratio;
height /= ratio;
} else {
ratio = 1;
}
canvas.width = width;
canvas.height = height;
// 鋪底色
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
//如果圖片像素大於100萬則使用瓦片繪製
let count;
if ((count = (width * height) / 1000000) > 1) {
count = ~~(Math.sqrt(count) + 1); //計算要分成多少塊瓦片
// 計算每塊瓦片的寬和高
let nw = ~~(width / count);
let nh = ~~(height / count);
tCanvas.width = nw;
tCanvas.height = nh;
for (let i = 0; i < count; i++) {
for (let j = 0; j < count; j++) {
tctx.drawImage(
img,
i * nw * ratio,
j * nh * ratio,
nw * ratio,
nh * ratio,
0,
0,
nw,
nh
);
ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
}
}
} else {
ctx.drawImage(img, 0, 0, width, height);
}
//修復ios上傳圖片的時候 被旋轉的問題
if (Orientation != "" && Orientation != 1) {
switch (Orientation) {
case 6: //需要順時針(向左)90度旋轉
this.rotateImg(img, "left", canvas);
break;
case 8: //需要逆時針(向右)90度旋轉
this.rotateImg(img, "right", canvas);
break;
case 3: //需要180度旋轉
this.rotateImg(img, "right", canvas); //轉兩次
this.rotateImg(img, "right", canvas);
break;
}
}
//進行最小壓縮
let ndata = canvas.toDataURL("image/png", 0.1);
console.log("壓縮前:" + initSize);
console.log("壓縮後:" + ndata.length);
console.log(
"壓縮率:" + ~~((100 * (initSize - ndata.length)) / initSize) + "%"
);
tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
return ndata;
},
//添加事件
addEvent(obj, type, fn) {
if (obj.addEventListener) {
obj.addEventListener(type, fn, false);
} else {
obj.attachEvent("on" + type, fn);
}
},
//移除事件
removeEvent(obj, type, fn) {
if (obj.removeEventListener) {
obj.removeEventListener(type, fn, false);
} else {
obj.detachEvent("on" + type, fn);
}
}
圖片在頁面的顯示效果
暫無
圖片的刪除
base64圖片轉化爲file類型的文件
bytes = window.atob(this.imgList[i].split(',')[1]);
var array = [];
array.push(bytes.charCodeAt(j));
var blob = new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
form.append("file", blob, Date.now() + '.jpg');
圖片的上傳
暫無
最終效果:
圖片裁剪參考:https://blog.csdn.net/ch83430...
ps: 需要源碼的請聯繫我(請尊重一下別人的勞動成果,源碼需要付費10元哦。):