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

海報生成示例

海報生成示例

最近智酷君在作[小程序]canvas生成海報的項目中遇到一些棘手的問題,在網上查閱了各類資料,也踩扁了各類坑,智酷君但願把這些「填坑」經驗整理一下分享出來,避免後來的兄弟重複「掉坑」。html

原型圖

原型圖

這是一個大體的原型圖,下面來看下如何製做這個海報,以及總體的思路。前端

原型圖

海報生成流程

[代碼片斷]Canvas生成海報實戰demo

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

demo的ID:Q74OU3m57c9xcanvas

若是你裝了IDE工具,能夠直接訪問上面的demo路徑小程序

經過代碼片斷將demo的ID輸入進去也可添加:api

打開代碼片斷

下面分享下主要的代碼內容和「填坑現場」bash

1、添加字體

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)工具

2、獲取臨時地址並設置圖片

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域名才能生效。

3、裁切「圓形」頭像畫圖

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/

4、將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

5、詢問並獲取訪問手機本地相冊權限

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();
        }
    }
})
複製代碼

判斷是否有訪問相冊的權限,若是沒有,則請求權限。

6、保存到用戶手機本地相冊

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的狀態。

7、關於組件中引用canvas

let ctx = wx.createCanvasContext('posterCanvas',this); //須要加this
複製代碼

在components中canvas沒法選中的問題:
在components自定義組件下,當前組件實例的this,表示在這個自定義組件下查找擁有 canvas-id 的 <canvas> ,若是省略則不在任何自定義組件內查找。

分隔符

若是有什麼疑問或者糾錯能夠在下面給 智酷君留言。

若是智酷君的分享可以幫助到你,或者想持續得到最新的全棧攻略

能夠關注個人掘金號「 智酷方程式
也可在微信 搜索「 Geek_Club 」或者「 智酷方程式

掃描二維碼關注公衆號喲👇

公衆號圖片
相關文章
相關標籤/搜索