1.背景
之前做過一個營銷類移動端h5專案-海報生成器,上傳使用者本地圖片合成海報並支援下載,這次有時間了整理整理。
2.幾個重點
-
上傳本地圖片並支援預覽
-
處理ios照片翻轉
-
使用canvas對圖片等比拉伸縮放並居中裁剪
-
使用canvas繪製圖片以及文字
-
輸出base64並支援下載
3.上傳圖片
廢話不多說,使用html的 <input type="file" />
標籤可以支援檔案上傳,前端上傳驗證的話,設定accept="image/*"
將檔案型別限制為影象。
<div className="btnUpload" style={{'display':this.state.hasFile && this.state.isUploaded?'none':'block'}}>
<input className="upload" type="file" accept="image/ * " ref="imgInput" onChange={this.uploadImg}/>
<svg className="cameraIcon">
<use xlinkHref="#camera"></use>
</svg >
</div>
複製程式碼
獲取當前上傳的圖片。
var file = event.target.files[0];
複製程式碼
要完成本地預覽圖片,需要使用FileReader物件讀取所要處理的檔案資料,使用readAsDataURL將檔案以Data URL形式進行讀入頁面。同時為了處理ios下照片翻轉角的問題,需要先對翻轉的照片進行一個修正。使用exif-js這個庫可以獲取照片的資訊,獲得翻轉角,然後使用canvas畫布對圖片進行翻轉矯正。
//上傳圖片
uploadImg = event => {
var that = this;
var file = event.target.files[0];
if (!file) {
return;
}
var orientation = "";
//EXIF js 可以讀取圖片的元資訊 https://github.com/exif-js/exif-js
EXIF.getData(file, function () {
orientation = EXIF.getTag(this, 'Orientation');
});
var reader = new FileReader();
// 將檔案以Data URL形式進行讀入頁面
reader.readAsDataURL(file);
reader.onload = function () {
var sourceImg = new Image();
sourceImg.onload = function () {
var imgRadio=sourceImg.width/sourceImg.height;
var imgStyle={
'width':imgRadio>1?'100%':'auto',
'height':imgRadio<1?'100%':'auto'
}
that.setState({isUploaded: true, sourceImg: sourceImg, hasFile: true,previewImgStyle:imgStyle})
};
//處理iOS照片旋轉
(async function (context) {
sourceImg.src = orientation && orientation != "1"
? await that.rotateImage(context.result, orientation)
: context.result;
})(this);
};
}
//處理ios照片翻轉
rotateImage = (img, dir) => {
return new Promise((resolve, reject) => {
var image = new Image();
var that = this;
image.onload = function () {
var degree = 0,
drawWidth=this.naturalWidth,
drawHeight=this.naturalHeight,
width,
height;
var canvas = document.createElement('canvas');
canvas.width = width = drawWidth;
canvas.height = height = drawHeight;
var context = canvas.getContext('2d');
//判斷圖片方向,重置canvas大小,確定旋轉角度,iphone預設的是home鍵在右方的橫屏拍攝方式
switch (dir) {
//iphone橫屏拍攝,此時home鍵在左側
case 3:
degree = 180;
drawWidth = -width;
drawHeight = -height;
break;
//iphone豎屏拍攝,此時home鍵在下方(正常拿手機的方向)
case 6:
canvas.width = height;
canvas.height = width;
degree = 90;
drawWidth = width;
drawHeight = -height;
break;
//iphone豎屏拍攝,此時home鍵在上方
case 8:
canvas.width = height;
canvas.height = width;
degree = 270;
drawWidth = -width;
drawHeight = height;
break;
}
//使用canvas旋轉校正
context.rotate(degree * Math.PI / 180);
context.drawImage(this, 0, 0, drawWidth, drawHeight);
//返回校正圖片
resolve(canvas.toDataURL("image/jpeg", 1));
}
image.src = img;
});
}
複製程式碼
html
<div className="preview-box" style={{'display':this.state.hasFile && this.state.isUploaded?'block':'none'}} onClick={this.changeImg}>
<img src={this.state.sourceImg.src} style={this.state.previewImgStyle} className="db-img"/>
</div>
複製程式碼
- 效果:
4.裁剪圖片
到此完成了圖片的上傳和預覽,接下來處理海報合成,背景圖是640*1136大小的,但是上傳的圖片可以是五花八門的,有可能是方的,也有可能是非常長的- -,需要保證圖片不變形並顯示中心部分,因此需要比較照片的寬高比與背景的寬高比,並拉伸壓縮至寬度或高度與背景圖片相同,然後裁剪中間部分。
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 640;
canvas.height = 1136;
//處理等比拉伸壓縮使用者圖片並居中裁剪
var imgRatio = canvas.width / canvas.height; //目標圖片的寬高比
var userimgRatio = that.state.sourceImg.width / that.state.sourceImg.height; //原始圖片的寬高比
var r = (userimgRatio > imgRatio)
? (canvas.height / that.state.sourceImg.height)
: (canvas.width / that.state.sourceImg.width);
var drawObj = {
sx: userimgRatio > imgRatio
? (that.state.sourceImg.width - canvas.width / r) / 2
: 0,
sy: userimgRatio > imgRatio
? 0
: (that.state.sourceImg.height - canvas.height / r) / 2,
sWidth: canvas.width / r,
sHeight: canvas.height / r,
dx: 0,
dy: 0,
dWidth: canvas.width,
dHeight: canvas.height
};
//圖片居中裁剪
ctx.drawImage(that.state.sourceImg, drawObj.sx, drawObj.sy, drawObj.sWidth, drawObj.sHeight, drawObj.dx, drawObj.dy, drawObj.dWidth, drawObj.dHeight);
複製程式碼
canvas的drawImage方法總共有9個引數
如圖示,上傳了一張尺寸小且為正方形的圖片,先根據背景圖片計算寬高比,在使用者圖片中裁剪出一個寬高比與背景圖片寬高比一樣的最大範圍,並且裁剪圖片中心部分,獲得開始裁剪的點,左上角這個點x座標為(that.state.sourceImg.width - canvas.width / r) / 2,y座標為0,對應sx和sy引數,被剪下影象的寬高是中間部分的寬高(四個黃點所圍),即引數中的swidth為canvas.width / r,sheight為canvas.height / r,最終圖片放置位置為鋪滿canvas,即x=0,y=0,最終圖片大小即為canvas的大小,會拉伸圖片鋪滿canvas,最終實現了“獲取使用者圖片中心部分並鋪滿背景的效果”
5.繪製圖片
最後將背景圖與上傳的圖片繪製在一起,並生成base64格式,可以長按儲存。
var newimg = new Image();
newimg.src = "/src/images/bg.png";
newimg.onload = function () {
ctx.drawImage(newimg, 0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#fff";
ctx.font = 36 + "px sans-serif";
ctx.fillText(that.state.slogan1, 50, 1000);
ctx.fillText(that.state.slogan2, 50, 1070);
that.setState({
resultImgUrl: canvas.toDataURL("image/jpeg", .5),
isGenerated: true
})
};
複製程式碼