最近智酷君在作[小程序]canvas生成海報的項目中遇到一些棘手的問題,在網上查閱了各類資料,也踩扁了各類坑,智酷君但願把這些「填坑」經驗整理一下分享出來,避免後來的兄弟重複「掉坑」。html
這是一個大體的原型圖,下面來看下如何製做這個海報,以及總體的思路。前端
demo的微信路徑:developers.weixin.qq.com/s/Q74OU3m57…html5
demo的ID:Q74OU3m57c9xcanvas
若是你裝了IDE工具,能夠直接訪問上面的demo路徑小程序
經過代碼片斷將demo的ID輸入進去也可添加:api
下面分享下主要的代碼內容和「填坑現場」:bash
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微信
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()。畫布是先裁切完了再進行繪圖。並不必定非要是圖片,路徑也能夠放進去~
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/
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的狀態。
let ctx = wx.createCanvasContext('posterCanvas',this); //須要加this
複製代碼
在components中canvas沒法選中的問題:
在components自定義組件下,當前組件實例的this,表示在這個自定義組件下查找擁有 canvas-id 的 <canvas> ,若是省略則不在任何自定義組件內查找。
若是智酷君的分享可以幫助到你,或者想持續得到最新的全棧攻略
能夠關注個人掘金號「 智酷方程式 」
也可在微信 搜索「 Geek_Club 」或者「 智酷方程式 」
掃描二維碼關注公衆號喲👇