需求:有用戶頭像,用戶暱稱,活動標題,商品主圖,商品標題,商品價格,小程序二維碼
思路:寫成一個js文件,就可以在需要的組件中混入使用(mixins),調用繪製函數:createCanvasImage('posterCanvas'),保存圖片函數:saveCanvasImage。
方法:canvas的dom元素的top寫很大,相當於隱藏。canvas畫好後,調用getCanvasImage函數得到圖片(posterImg)顯示。
調用的組件dom代碼:<canvas class="canvas" canvas-id="posterCanvas"></canvas> <image :src="posterImg" class="img" />
註釋:<!-- canvas 要放在調用的組件裏(外面),不能放在封裝組件裏面 會找不到 canvas-->
js代碼:
export default {
data() {
return {
posterObj: {
url: "", //商品主圖
userImg: "", //用戶頭像
nickname: "", //用戶暱稱
dec: "", //介紹
title: "", //標題
discountPrice: "", //折後價格
orignPrice: "", //原價
code: "", //小程序碼
},
canvasFlag: true, // 是否顯示canvas
posterImg: "",//canvas繪製後得到的圖片
x: 25, // 繪製起點x
y: 0, // 繪製起點y
w: 270, // 繪製寬度
h: 345, // 繪製高度
radius: 10, // 圓角
padding: 10, // 邊距
imgW: 187.5, // 主圖片寬
imgH: 187.5, //主圖片高
imgBg: "#f7f7f7", //主圖片背景色
};
},
methods: {
// 畫圓角矩形 ctx、x起點、y起點、w寬度、h高度、r圓角半徑、fillColor填充顏色、strokeColor邊框顏色
roundRect(ctx, x, y, w, h, r, fillColor, strokeColor, btn) {
// 開始繪製
ctx.beginPath();
// 繪製左上角圓弧 Math.PI = 180度
// 圓心x起點、圓心y起點、半徑、以3點鐘方向順時針旋轉後確認的起始弧度、以3點鐘方向順時針旋轉後確認的終止弧度
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
// 繪製border-top
// 移動起點位置 x終點、y終點
ctx.moveTo(x + r, y);
// 畫一條線 x終點、y終點
ctx.lineTo(x + w - r, y);
// 繪製右上角圓弧
ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
// 繪製border-right
ctx.lineTo(x + w, y + h - r);
// 繪製右下角圓弧
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);
// 繪製左下角圓弧
ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);
// 繪製border-left
ctx.lineTo(x, y + r);
if (btn == "btn") {
const grd = ctx.createLinearGradient(0, 0, 200, 0); //漸變色
grd.addColorStop(0, fillColor);
grd.addColorStop(1, strokeColor);
// 因爲邊緣描邊存在鋸齒,最好指定使用 transparent 填充
ctx.setFillStyle(grd);
// 對繪畫區域填充
ctx.fill();
} else {
if (fillColor) {
// 因爲邊緣描邊存在鋸齒,最好指定使用 transparent 填充
ctx.setFillStyle(fillColor);
// 對繪畫區域填充
ctx.fill();
}
if (strokeColor) {
// 因爲邊緣描邊存在鋸齒,最好指定使用 transparent 填充
ctx.setStrokeStyle(strokeColor);
// 畫出當前路徑的邊框
ctx.stroke();
}
}
// 關閉一個路徑
ctx.closePath();
// 剪切,剪切之後的繪畫繪製剪切區域內進行,需要save與restore 這個很重要 不然沒辦法保存
ctx.clip();
// console.log("畫圓角");
},
/**
* canvas繪圖相關,把文字轉化成只能行數,多餘顯示省略號
* ctx: 當前的canvas
* text: 文本
* contentWidth: 文本最大寬度
* lineNumber: 顯示幾行
*/
canvasMultiLineText(ctx, text, contentWidth, lineNumber) {
var textArray = text.split(""); // 分割成字符串數組
var temp = "";
var row = [];
for (let i = 0; i < textArray.length; i++) {
if (ctx.measureText(temp).width < contentWidth) {
temp += textArray[i];
} else {
i--; // 這裏添加i--是爲了防止字符丟失
row.push(temp);
temp = "";
}
}
row.push(temp);
// 如果數組長度大於2,則截取前兩個
if (row.length > lineNumber) {
var rowCut = row.slice(0, lineNumber);
var rowPart = rowCut[1];
var test = "";
var empty = [];
for (var a = 0; a < rowPart.length; a++) {
if (ctx.measureText(test).width < contentWidth) {
test += rowPart[a];
} else {
break;
}
}
empty.push(test); // 處理後面加省略號
var group = empty[0] + "...";
rowCut.splice(lineNumber - 1, 1, group);
row = rowCut;
}
return row;
},
// 繪製圖片 : 當前canvas 圖 x起點 y起點 w寬度 h高度 r圓角 c1填充顏色 c2邊框顏色
drawImg(ctx, img, x, y, w, h, r, c1, c2) {
let _this = this;
//將網絡圖片轉成本地路徑
ctx.restore();
uni.getImageInfo({
src: img,
success(res) {
ctx.save();
//覆蓋繪製
//問題:在微信小程序使用canvas繪製圓角圖片時,微信調試工具正常顯示,android真機都不顯示。
// 原因:因爲ctx.clip()剪切區域使用的填充顏色是透明的,所以圖片沒出來。
// 解決方案:將剪切區域設置成實體顏色就好了。
_this.roundRect(ctx, x, y, w, h, r, c1, c2); //繪製圖片圓角背景
ctx.drawImage(res.path, x, y, w, h, r); //繪製圖
ctx.restore(); //恢復之前保存的繪圖上下文 恢復之前保存的繪圖上下午即狀態 可以繼續繪製
ctx.draw(true);
},
fail() {
_this.canvasFlag = true;
uni.showToast({ title: "img生成失敗", duration: 2000, icon: "none" });
},
});
},
// 繪製文本 : 當前canvas 文本 x起點 y起點 c填充顏色 s樣式
drawText(ctx, text, x, y, c, s) {
ctx.setFillStyle(c);
ctx.font = s;
ctx.fillText(text, x, y);
ctx.draw(true);
},
// 繪製兩行標題 : 當前canvas 文本 x起點 y起點 c填充顏色 s樣式
drawTitle(ctx, text, x, y, c, s) {
ctx.setGlobalAlpha(1); //不透明
ctx.setFillStyle(c); //文字顏色:默認黑色
ctx.font = s;
let row = this.canvasMultiLineText(ctx, text, 180, 2); //計算繪製的2行文本
let leftSpace = x + 10; // 這段文字起始的X位置
let textLineHeight = 18; // 一行文字加一行行間距
for (let b = 0; b < row.length; b++) {
//一行一行繪製文本
ctx.fillText(row[b], leftSpace, y + textLineHeight * b - 15, 180);
ctx.draw(true);
}
},
// 繪製價格 : 當前canvas 現價 原價 x起點 y起點
drawPrice(ctx, zpPrice, orignPrice, x, y) {
ctx.setFillStyle("#FF354D"); //文字顏色:默認黑色
ctx.setFontSize(21); //設置字體大小,默認10
let zpPriceW = ctx.measureText(zpPrice).width; //文本的寬度
ctx.fillText(`${zpPrice}`, x, y + 30, zpPriceW);
ctx.beginPath(); //開始一個新的路徑
ctx.setFontSize(14); //設置字體大小,默認10
ctx.setFillStyle("#999"); //文字顏色:默認黑色
let orignPriceW = ctx.measureText(orignPrice).width; //去掉市場價
ctx.fillText(`¥${orignPrice}`, x + zpPriceW, y + 30, orignPriceW); //5價格間距
ctx.moveTo(x + zpPriceW, y + 25); //設置線條的起始路徑座標
ctx.lineTo(x + zpPriceW + orignPriceW, y + 25); //設置線條的終點路徑座標
ctx.setStrokeStyle("#999");
ctx.stroke(); //對當前路徑進行描邊
ctx.closePath(); //關閉當前路徑
},
// 生成海報
createCanvasImage(id) {
// console.log(this.posterObj, "posterObj");
uni.showLoading({
title: "海報生成中...",
});
let _this = this;
const ctx = uni.createCanvasContext(id);
ctx.draw(); //清空原來的畫圖內容
ctx.save();
this.roundRect(ctx, this.x, this.y, this.w, this.h, this.radius, "#fff", "#fff"); //繪製海報圓角背景白色的
ctx.restore(); //恢復之前保存的繪圖上下文 恢復之前保存的繪圖上下午即狀態 可以繼續繪製
ctx.save();
//將網絡圖片轉成本地路徑 用戶頭像
this.drawImg(
ctx,
this.posterObj.userImg,
this.x + this.padding,
this.y + this.padding,
50,
50,
25,
this.imgBg,
this.imgBg
);
// 用戶暱稱
this.drawText(ctx, this.posterObj.nickname, this.x + 70, this.y + 30, "#333", "normal bold 18px sans-serif");
// dec介紹
this.drawText(ctx, this.posterObj.dec, this.x + 70, this.y + 55, "#666", "normal 14px sans-serif");
//將網絡圖片轉成本地路徑 商品圖片
this.drawImg(
ctx,
this.posterObj.url,
this.x + 41,
this.y + 70,
this.imgW,
this.imgH,
this.radius,
this.imgBg,
this.imgBg
);
// 海報商品title
let contentTextY = this.y + 295; // 這段文字起始的y位置
_this.drawTitle(ctx, this.posterObj.title, this.x, contentTextY, "#333", "normal bold 14px sans-serif");
//繪製價格
this.drawPrice(ctx, this.posterObj.discountPrice, this.posterObj.orignPrice, this.x + this.padding, contentTextY);
// 二維碼 圖標
this.drawImg(ctx, this.posterObj.code, this.x + 200, this.y + 271, 60, 60, 0, "#ffffff", this.imgBg);
setTimeout(() => {
this.getCanvasImage(id);
setTimeout(() => {
uni.hideLoading();
}, 300);
}, 300);
},
getCanvasImage(id) {
let _this = this;
// 把畫布轉化成臨時文件
uni.canvasToTempFilePath({
x: _this.x,
y: _this.y,
quality: 1,
width: this.w,
height: this.h,
destWidth: this.w * 10,
destHeight: this.h * 10,
canvasId: id,
success(res) {
_this.posterImg = res.tempFilePath;
},
fail() {
uni.showToast({ title: "生成失敗,稍後再試", duration: 2000, icon: "none" });
},
});
},
// 保存到系統相冊
saveCanvasImage() {
uni.showLoading({
title: "保存中...",
});
let _this = this;
// 保存圖片至相冊
uni.saveImageToPhotosAlbum({
filePath: _this.posterImg,
success(res2) {
uni.hideLoading();
uni.showToast({ title: "圖片保存成功,可以去分享啦~", icon: "none", duration: 2000 });
_this.canvasCancelEvn();
},
fail() {
uni.showToast({ title: "保存失敗,稍後再試", duration: 2000, icon: "none" });
uni.hideLoading();
},
});
},
// 取消海報
canvasCancelEvn() {
// console.log();
this.$emit("cancel", true);
},
},
};