[填坑手冊]小程式Canvas生成海報(一)---完整流程

前端智酷方程發表於2019-05-23

海報生成示例

海報生成示例

最近智酷君在做[小程式]canvas生成海報的專案中遇到一些棘手的問題,在網上查閱了各種資料,也踩扁了各種坑,智酷君希望把這些“填坑”經驗整理一下分享出來,避免後來的兄弟重複“掉坑”。

原型圖

原型圖

這是一個大致的原型圖,下面來看下如何製作這個海報,以及整體的思路。

原型圖

海報生成流程

[程式碼片段]Canvas生成海報實戰demo

demo的微信路徑:developers.weixin.qq.com/s/Q74OU3m57…

demo的ID:Q74OU3m57c9x

如果你裝了IDE工具,可以直接訪問上面的demo路徑

通過程式碼片段將demo的ID輸入進去也可新增:

開啟程式碼片段

下面分享下主要的程式碼內容和“填坑現場”

一、新增字型

developers.weixin.qq.com/miniprogram…

canvasContext.font = value //示例
ctx.font = `normal bold 20px sans-serif`//設定字型大小,預設10
ctx.setTextAlign('left');
ctx.setTextBaseline("top");
ctx.fillText("《智酷方程式》專注研究和分享前端技術", 50, 15, 250)//繪製文字
複製程式碼

符合 CSS font 語法的 DOMString 字串,至少需要提供字型大小和字型族名。預設值為 10px sans-serif

文字過長在canvas下換行問題處理(最多兩行,超過“...”代替)

ctx.setTextAlign('left');
ctx.setFillStyle('#000');//文字顏色:預設黑色
ctx.font = `normal bold 18px sans-serif`//設定字型大小,預設10
let canvasTitleArray = canvasTitle.split("");
let firstTitle = ""; //第一行字
let secondTitle = ""; //第二行字
for (let i = 0; i < canvasTitleArray.length; i++) {
    let element = canvasTitleArray[i];
    let firstWidth = ctx.measureText(firstTitle).width;
    //console.log(ctx.measureText(firstTitle).width);
    if (firstWidth > 260) {
        let secondWidth = ctx.measureText(secondTitle).width;
        //第二行字數超過,變為...
        if (secondWidth > 260) {
            secondTitle += "...";
            break;
        } else {
            secondTitle += element;
        }
    } else {
        firstTitle += element;
    }
}
//第一行文字
ctx.fillText(firstTitle, 20, 278, 280)//繪製文字
//第二行問題
if (secondTitle) {
    ctx.fillText(secondTitle, 20, 300, 280)//繪製文字
}
複製程式碼

通過 ctx.measureText 這個方法可以判斷文字的寬度,然後進行切割。 (一行字允許寬度為280時,判斷需要寫小點,比如260)

二、獲取臨時地址並設定圖片

let mainImg = "https://demo.com/url.jpg";
wx.getImageInfo({
    src: mainImg,//伺服器返回的圖片地址
    success: function (res) {
        //處理圖片縱橫比例過大或者過小的問題!!!
        let h = res.height;
        let w = res.width;
        let setHeight = 280, //預設源圖擷取的區域
            setWidth = 220; //預設源圖擷取的區域
        if (w / h > 1.5) {
            setHeight = h;
            setWidth = parseInt(280 / 220 * h);
        } else if (w / h < 1) {
            setWidth = w;
            setHeight = parseInt(220 / 280 * w);
        } else {
            setHeight = h;
            setWidth = w;
        };
        console.log(setWidth, setHeight)
        ctx.drawImage(res.path, 0, 0, setWidth, setHeight, 20, 50, 280, 220);
        ctx.draw(true);
    },
    fail: function (res) {
        //失敗回撥
    }
});
複製程式碼

在開發過程中如果封面圖無法按照約定的比例(280x220)給到:
那麼我們就需要處理預設封面圖過大或者過小的問題,大致思路是:程式碼中通過比較縱橫比(280/220=1.27)正比例放大或者縮小原圖,然後從左上切割,竟可能保證過高的圖是寬度100%,過寬的圖是高度100%。
在canvas中draw圖片,必須是一個(相對)本地路徑,我們可以通過將圖片儲存在本地後生成的臨時路徑
微信官方提供兩個API:
wx.downloadFile(OBJECT)和wx.getImageInfo(OBJECT)。都需先配置download域名才能生效。

三、裁切“圓形”頭像畫圖

ctx.save();  //儲存畫圖板
ctx.beginPath()//開始建立一個路徑
ctx.arc(35, 25, 15, 0, 2 * Math.PI, false)//畫一個圓形裁剪區域
ctx.clip()//裁剪
ctx.closePath();
ctx.drawImage(headImageLocal, 20, 10, 30, 30);
ctx.draw(true);
ctx.restore()//恢復之前儲存的繪圖上下文
複製程式碼

使用圖形上下文的不帶引數的clip()方法來實現Canvas的影像裁剪功能。該方法使用路徑來對Canvas話不設定一個裁剪區域。因此,必須先建立好路徑。建立完整後,呼叫clip()方法來設定裁剪區域。
需要注意的是裁剪是對畫布進行的,裁切後的畫布不能恢復到原來的大小,也就是說畫布是越切越小的,要想保證最後仍然能在canvas最初定義的大小下繪圖需要注意save()和restore()。畫布是先裁切完了再進行繪圖。並不一定非要是圖片,路徑也可以放進去~

小程式 canvas 裁切BUG

ctx.setFillStyle("#fff");
ctx.fillRect(0, 0, 320, 500);  //第一個填充矩形
wx.downloadFile({
    url: headUri,
    success(res) {
        ctx.beginPath()
        ctx.arc(50, 50, 25, 0, 2 * Math.PI)
        ctx.clip()
        ctx.drawImage(res.tempFilePath, 25, 25); //第二個填充圖片
        ctx.draw()
        ctx.restore()
        ctx.setFillStyle("#fff");
        ctx.fillRect(0, 0, 320, 500);
        ctx.draw(true)
        ctx.restore()
    }
})
複製程式碼

clip裁切這個功能,如果有超過一張圖片/背景疊加,則裁切效果失效。
錯誤參考:http://html51.com/info-38753-1/

四、將canvas匯出成虛擬地址

wx.canvasToTempFilePath({
    fileType: 'jpg',
    canvasId: 'customCanvas',
    success: (res) => {
        console.log(res.tempFilePath) //為canvas的虛擬地址
    }
})

res:
{
    errMsg: "canvasToTempFilePath:ok", 
    tempFilePath: "http://tmp/wx02935bb29080a7b4.o6zAJswFAuZuKQ5NZfPr….cGnD1a02PlVC0b3284be3a41d08986c2477579a5fd8e.jpg"
}
複製程式碼

這裡需要把canvas裡面的內容,匯出成一個臨時地址才能儲存在相簿,比如: http://tmp/wx02935bb29080a7b4.o6zAJswFAuZuKQ5NZfPr5UfJVR4k.cGnD1a02PlVC0b3284be3a41d08986c2477579a5fd8e.jpg

五、詢問並獲取訪問手機本地相簿許可權

wx.getSetting({
    success(res) {
        console.log(res)
        if (!res.authSetting['scope.writePhotosAlbum']) { //判斷許可權
            wx.authorize({ //獲取許可權
                scope: 'scope.writePhotosAlbum',
                success() {
                    console.log('授權成功')
                    //轉化路徑

                    self.saveImg();
                }
            })
        } else {
            self.saveImg();
        }
    }
})
複製程式碼

判斷是否有訪問相簿的許可權,如果沒有,則請求許可權。

六、儲存到使用者手機本地相簿

wx.saveImageToPhotosAlbum({
    filePath: res.tempFilePath,
    success: function (data) {
        wx.showToast({
            title: '儲存到系統相簿成功',
            icon: 'success',
            duration: 2000
        })
    },
    fail: function (err) {
        console.log(err);
        if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") {
            console.log("當初使用者拒絕,再次發起授權")
            wx.openSetting({
                success(settingdata) {
                    console.log(settingdata)
                    if (settingdata.authSetting['scope.writePhotosAlbum']) {
                        console.log('獲取許可權成功,給出再次點選圖片儲存到相簿的提示。')
                    } else {
                        console.log('獲取許可權失敗,給出不給許可權就無法正常使用的提示')
                    }
                }
            })
        } else {
            wx.showToast({
                title: '儲存失敗',
                icon: 'none'
            });
        }
    },
    complete(res) {
        console.log(res);
    }
})
複製程式碼

儲存到本地需要一定的時間,需要加一個loading的狀態。

七、關於元件中引用canvas

let ctx = wx.createCanvasContext('posterCanvas',this); //需要加this
複製程式碼

在components中canvas無法選中的問題:
在components自定義元件下,當前元件例項的this,表示在這個自定義元件下查詢擁有 canvas-id 的 <canvas> ,如果省略則不在任何自定義元件內查詢。

分隔符

如果有什麼疑問或者糾錯可以在下面給智酷君留言。

如果智酷君的分享能夠幫助到你,或者想持續獲得最新的全棧攻略

可以關注我的掘金號“ 智酷方程式
也可在微信 搜尋“ Geek_Club ”或者“ 智酷方程式

掃描二維碼關注公眾號喲?

公眾號圖片

相關文章