哈哈,趁着平安夜來湊湊熱鬧,其實好久以前就作過一個相似功能的畢業季生成邀請函小應用。這種有關圖像合成的應用其實只須要熟悉h5中canvas的相關API和一點幾何常識就ok了,是很容易實現的,這裏就帶你們探究一下實現的過程。再加上最近會有一大波節日到來,相信這個小工具值得你的擁有,若是以爲不錯,不如點個贊
唄。也可關注個人公衆號BRandF
(有小工具的圖文信息)推薦給朋友用哈,二維碼在文末。html
上傳圖片
自動調整
成合適的尺寸
飾品
圖形邊框
保存/下載
生成的圖片在線製做頭像應用git
初始化
,在這個階段會將預置的飾品和圖形畫到canvas上面去。狀態更改
,在這個階段用戶能更改飾品,圖形邊框和上傳圖片,咱們須要將最新的用戶圖片,飾品圖片,圖形邊框畫到canvas中。結果輸出
,最後將canvas的內容輸出成可保存下載的圖片。嗯沒錯,思路是很是直觀
的。下面會對核心功能詳細解析。github
首先須要明確的是,既然要作頭像
,那麼基本能夠認爲最終用戶須要的圖片是長和寬相等
的。但實際上用戶上傳的圖片極可能並不規整
,這裏有三種狀況,寬大於高,寬小於高,寬等於高,同時又要等比放大or縮小,那應該怎麼辦呢?web
有一個很巧妙的辦法
就是,好比寬大於高時,咱們能夠將原圖的高等比
縮放到canvas畫布的高度,原圖的寬也跟着等比縮放
。就像下圖,這樣既能夠保持原圖的比例
,canvas又不用留白
,雖然會丟失掉原圖的一小部分,可是從實際效果來看仍是能夠接受的。好吧,作這一步就是爲了替(tou)換(lan)掉用戶自定義裁剪這個功能。sql
// 不急,等下講到這兩個函數
const base64Url = await this.file2Base64(sourceImage);
const imgObj = await this.createImage(base64Url);
const CANVANS_SIZE = 256;
const type = imgObj.width - imgObj.height;
// 三種狀況
if (type > 0) {
// 不管寬大於高仍是寬小於高,都會進行等比縮放
const w = imgObj.width * CANVANS_SIZE / imgObj.height;
context.drawImage(imgObj, 0, 0, w, CANVANS_SIZE);
} else if (type < 0) {
const h = imgObj.height * CANVANS_SIZE / imgObj.width;
context.drawImage(imgObj, 0, 0, CANVANS_SIZE, h);
} else {
context.drawImage(imgObj, 0, 0, CANVANS_SIZE, CANVANS_SIZE);
}
複製代碼
使用FileReader
對象讀取用戶上傳的文件,並轉爲base64
格式。因爲讀取圖片的過程是異步
的,這裏使用Promise
封裝了一下。canvas
file2Base64(domFile) {
return new Promise((resolve, rejest) => {
const reader = new FileReader();
reader.readAsDataURL(domFile);
reader.onload = (e) => {
resolve(reader.result);
};
});
}
複製代碼
因爲canvas
繪製圖片時只接受圖片對象
,獲得base64
格式的圖片以後,須要再包裝成圖片對象才能繪製到canvas
中。因爲載入成圖片對象的過程也是異步
的,這裏也使用Promise
封裝了一下後端
createImage(imgUrl) {
return new Promise((resolve, rejest) => {
const imgObj = new Image();
imgObj.src = imgUrl;
imgObj.onload = (e) => {
resolve(imgObj);
};
});
}
複製代碼
就是如何在canvas上繪製幾何圖形,以爲這個處理方式很不錯,這裏借鑑了一下繪製圓角和圓形的方法。 《在Canvas中繪製圓角矩形》安全
將異步操做封裝成Promise的好處在這裏就體現出來了,能很是直觀
的使用同步的編寫形式將異步操做
表達出來。要注意的是用戶圖片須要先繪製到canvas
中,不然飾品和圖形邊框就會被圖片覆蓋。app
/** * @param {string} imgUrl (進過處理的用戶圖片url) * @param {object} decorationCurrent (飾品對象) * @returns imageUrl * @memberof App */
async handleMakeImage(imgUrl, decorationCurrent) {
if (!(imgUrl || decorationCurrent)) { return ''; }
const { border } = this.state;
const { value } = border;
const { source, style } = decorationCurrent;
const { width, height, top, left } = style;
const { canvas } = this.refs;
this.clearCanvas(canvas);
const context = canvas.getContext('2d');
if (imgUrl) {
const bgImg = await this.createImage(imgUrl);
context.drawImage(bgImg, 0, 0, bgImg.width, bgImg.height);
}
this.drawBorder(value, context);
if (decorationCurrent && source) {
const imgObj = await this.createImage(source);
context.drawImage(imgObj, left, top, width, height);
}
const targetUrl = canvas.toDataURL('image/png');
return targetUrl;
}
複製代碼
最後模擬一下a
標籤的點擊事件,完成圖片的下載。若是是移動端用戶,長按圖片便可保存圖片。dom
downloadImage(url) {
if (url) {
const aLink = document.createElement('a');
const evt = document.createEvent('HTMLEvents');
evt.initEvent('click', true, true);
// 指定圖片文件名
aLink.download = 'protrait.png';
aLink.href = url;
aLink.click();
}
}
複製代碼
發現若是按正常的圖片比例去載入圖片,最後生成出來的圖片會出現模糊。有一個簡易的方法就是將canvas的長寬擴大到原來的兩倍,但圖片仍是按原來的比例來顯示,簡單的來講就是將大圖片縮小了顯示。這樣就能有效緩解圖片模糊的問題了。
...
<canvas width="512" height="512" className="shadow d-n" ref="canvas" />
...
<img className="w256 h256" src={targetUrl} alt="" />
複製代碼
ok,就這樣,核心功能代碼就是這些了,一共200行
不到。
因爲接下來各類節日不斷接近,這個小工具的素材也會不斷更新,也歡迎你們貢獻一些有趣的素材哈哈。