200行代碼帶你製做實用的節日頭像定製小工具【可在線製做,快來試試吧】

哈哈,趁着平安夜來湊湊熱鬧,其實好久以前就作過一個相似功能的畢業季生成邀請函小應用。這種有關圖像合成的應用其實只須要熟悉h5中canvas的相關API和一點幾何常識就ok了,是很容易實現的,這裏就帶你們探究一下實現的過程。再加上最近會有一大波節日到來,相信這個小工具值得你的擁有,若是以爲不錯,不如點個唄。也可關注個人公衆號BRandF(有小工具的圖文信息)推薦給朋友用哈,二維碼在文末。html

效果

image

  1. 用戶能上傳圖片
  2. 能將用戶圖片自動調整成合適的尺寸
  3. 能切換飾品
  4. 能切換頭像圖形邊框
  5. 保存/下載生成的圖片

在線定製頭像

在線製做頭像應用git

思路

  1. 初始化,在這個階段會將預置的飾品和圖形畫到canvas上面去。

image

  1. 狀態更改,在這個階段用戶能更改飾品,圖形邊框和上傳圖片,咱們須要將最新的用戶圖片,飾品圖片,圖形邊框畫到canvas中。

image

  1. 結果輸出,最後將canvas的內容輸出成可保存下載的圖片。

image

嗯沒錯,思路是很是直觀的。下面會對核心功能詳細解析。github

實現

首先須要明確的是,既然要作頭像,那麼基本能夠認爲最終用戶須要的圖片是長和寬相等的。但實際上用戶上傳的圖片極可能並不規整,這裏有三種狀況,寬大於高,寬小於高,寬等於高,同時又要等比放大or縮小,那應該怎麼辦呢?web

有一個很巧妙的辦法就是,好比寬大於高時,咱們能夠將原圖的高等比縮放到canvas畫布的高度,原圖的寬也跟着等比縮放。就像下圖,這樣既能夠保持原圖的比例,canvas又不用留白,雖然會丟失掉原圖的一小部分,可是從實際效果來看仍是能夠接受的。好吧,作這一步就是爲了替(tou)換(lan)掉用戶自定義裁剪這個功能。sql

image

調整用戶圖片尺寸

// 不急,等下講到這兩個函數
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);
}
複製代碼

圖片轉換成Base64格式

使用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;
}
複製代碼

保存or下載

最後模擬一下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行不到。

有興趣的話能夠看源碼瞭解更多細節

源碼&&在線應用

結束

因爲接下來各類節日不斷接近,這個小工具的素材也會不斷更新,也歡迎你們貢獻一些有趣的素材哈哈。

歡迎關注公衆號獲取這個小工具的圖文信息

image

或者你感興趣的內容

Re從零開始系列

web安全系列

相關文章
相關標籤/搜索