微信小程序分享之canvas绘图
![](https://img.haomeiwen.com/i7063800/235253e1c53f89f5.jpg)
使用场景
我们在微信小程序中分享页面时使用了onShareAppMessage方法,该方法返回一个对象,包括:
- title 分享的卡片标题
- imageUrl 自定义图片路径(可以是本地文件路径、代码包文件路径或者网络图片路径。支持PNG及JPG。显示图片长宽比是 5:4。)
- path 转发路径(当前页面 path ,必须是以 / 开头的完整路径)
// 分享
onShareAppMessage() {
return {
title: this.cruiseLineInfo.title, // 分享的卡片标题
imageUrl: this.shareImageUrl, // 分享的图片(如果不传默认本页面截图)
path: `/pages/cruise/cruisedetail/main?lineId=...` // 分享给好友的路径
};
}
如何自定义图片内容?
那么就要用到canvas了~
1. 拿到一些基本的数据
-
img
:背景图片(这里用的是邮轮船队的大图,用resizeImage将图片转换为5:4的比例) -
title
邮轮 | 222101 -
price
价格 -
isDiscount
是否是优惠线路 -
info
其他信息:“8888人购买 98%满意度” 或 “新产品推荐”
2. 在页面中放入canvas
<canvas
canvas-id="shareCanvas"
style="width: 450px; height: 360px;"
hidden="true"
></canvas>
值得注意的是 canvas本身并不是我们所需要的,我们需要的是绘制完的canvas转化后的临时路径,所以canvas需要隐藏。
但是canvas如果直接用 display:none
或者 wx:if
来隐藏,会导致canvs无法进行工作。
我们可以使用 hidden="true"
或者直接用 hidden
进行隐藏,但是这种方法在微信开发者工具上会导致无法绘制,但是真机上都可以。
也或者,可以使用一个view包裹住canvas,设置包裹view的样式为 position:fixed; top:-9999px;z-index:-1; 将它“移出”屏幕即可。
3. 网络图片记得转为本地图片
因为真机上网络图片无法用于绘制canvas,我们需要使用getImageInfo拿到网络图片的本地临时路径,用Promise来避免回调嵌套
// 处理网络图片变为本地图片
dealImg() {
let that = this;
Promise.all([
// 背景图片
new Promise(resolve => {
wx.getImageInfo({
src: that.data.img,
success: (re) => {
resolve(re.path);
}
});
}),
// 折扣图片
new Promise(resolve => {
wx.getImageInfo({
src: "https://file.40017.cn/hubble/pic/discount.png",
success: (re) => {
resolve(re.path);
}
});
})
]).then(result => {
that.setData({
img: result[0],
discountImg: result[1]
})
that.drawCanvas();
});
},
canvas上场
为什么要 ctx.save() ctx.restore()
- save表示保存save函数之前的状态,restore表示获取save保存的状态
- 每一次调用 save 方法,当前的状态就会被推入堆中保存起来, restore 的时候释放出来
图片
ctx.save();
ctx.drawImage(imgUrl, 0, 0, 450, 360);
ctx.restore();
写字
let ctx = wx.createCanvasContext('shareCanvas')
// ctx.translate(20, 20)
// ctx.setFontSize(20)
// ctx.setFillStyle('black')
// ctx.setTextAlign('left')
ctx.fillText("哈哈哈", 20, 20, 200)
ctx.draw()
线
const ctx = wx.createCanvasContext('shareCanvas')
ctx.moveTo(10, 10)
ctx.lineTo(100, 10)
ctx.lineTo(100, 100)
ctx.stroke()
ctx.draw()
实线矩形
const ctx = wx.createCanvasContext('shareCanvas')
ctx.setStrokeStyle('red')
ctx.strokeRect(10, 10, 150, 75)
ctx.draw()
填充矩形
const ctx = wx.createCanvasContext('shareCanvas')
ctx.setFillStyle('red')
ctx.fillRect(10, 10, 150, 75)
ctx.draw()
圆
const ctx = wx.createCanvasContext('shareCanvas')
// Draw arc
ctx.beginPath()
ctx.arc(100, 75, 50, 0, 2 * Math.PI)
ctx.setStrokeStyle('#333333')
ctx.stroke()
ctx.draw()
实线圆角矩形
填充圆角矩形
渐变色
const ctx = wx.createCanvasContext('shareCanvas')
let grd = ctx.createLinearGradient(0, 0, 0, 360);
grd.addColorStop(0, 'rgba(0,0,0,0)');
grd.addColorStop(0.5, 'rgba(0,0,0,0.05)');
grd.addColorStop(0.7, 'rgba(0,0,0,0.5)');
grd.addColorStop(1, 'rgba(0,0,0,0.9)');
ctx.setFillStyle(grd);
ctx.fillRect(0, 0, 450, 360);
ctx.draw()
全部代码
<canvas
canvas-id="shareCanvas"
style="width: 450px; height: 360px;"
hidden="true"
></canvas>
Page({
/**
* 页面的初始数据
*/
data: {
img: "https://pic5.40017.cn/02/000/6f/69/rBANDFk5ELWAAtBcAAgAAHoheBo892_450x360_00.png",
title: "邮轮 | 222101",
titleL: 0,
price: 4699,
priceL: 0,
isDiscount: true,
info: "8888人购买 98%满意度",
shareImageUrl: "",
discountImg: ""
},
onLoad: function (options) {
this.setData({
titleL: this.data.title.length * 15,
priceL: (this.data.price + '').length
});
this.dealImg();
},
// 处理网络图片变为本地图片
dealImg() {
let that = this;
Promise.all([
// 背景图片
new Promise(resolve => {
wx.getImageInfo({
src: that.data.img,
success: (re) => {
resolve(re.path);
}
});
}),
// 折扣图片
new Promise(resolve => {
wx.getImageInfo({
src: "https://file.40017.cn/hubble/pic/discount.png",
success: (re) => {
resolve(re.path);
}
});
})
]).then(result => {
that.setData({
img: result[0],
discountImg: result[1]
})
that.drawCanvas();
});
},
// 绘制canvas
drawCanvas() {
let that = this;
// 创建 canvas 的绘图上下文 CanvasContext 对象
let ctx = wx.createCanvasContext('shareCanvas');
ctx.save();// 保存绘图上下文
// 绘制背景图片
ctx.drawImage(that.data.img, 0, 0, 450, 360);
ctx.restore();// 恢复之前保存的绘图上下文
//圆角填充矩形 起始坐标20,20 宽高titleL*36 圆角半径4
that.fillRoundRect(ctx, 20, 20, that.data.titleL, 36, 4, 'rgba(0,0,0,0.6)');
//写字(邮轮 | 222101) 起始坐标 titleL / 2, 26 最大长度titleL
ctx.save();
ctx.translate(20, 20);
ctx.setFontSize(22);
ctx.setFillStyle('white');
ctx.setTextAlign('center');
ctx.fillText(that.data.title, that.data.titleL / 2, 26, that.data.titleL);
ctx.restore();
//底部渐变蒙层
ctx.save();
let grd = ctx.createLinearGradient(0, 0, 0, 360);
grd.addColorStop(0, 'rgba(0,0,0,0)');
grd.addColorStop(0.5, 'rgba(0,0,0,0.05)');
grd.addColorStop(0.7, 'rgba(0,0,0,0.5)');
grd.addColorStop(1, 'rgba(0,0,0,0.9)');
ctx.setFillStyle(grd);
ctx.fillRect(0, 0, 450, 360);
ctx.restore();
//价格
that.setPrice(ctx, that.data.price, that.data.priceL);
//优惠立减
if (that.data.isDiscount) {
ctx.save();
ctx.drawImage(that.data.discountImg, that.data.priceL * 25 + 50, 226, 108, 32);
ctx.restore();
}
//产品描述(满意度、销量、新品推荐等)
ctx.save();
ctx.setFontSize(20);
ctx.setFillStyle('rgba(255,255,255,0.7)');
ctx.fillText(that.data.info, 20, 335);
ctx.restore();
// 结束绘制
ctx.draw(true, function () {
wx.canvasToTempFilePath({
x: 0,
y: 0,
width: 450,
height: 360,
destWidth: 500,
destHeight: 400,
canvasId: "shareCanvas",
success: function (res) {
that.setData({
shareImageUrl: res.tempFilePath
});
}
});
});
},
onShareAppMessage() {
let that = this;
return {
title: "【盛世公主号】上海-福冈(日本)-上海 4晚5日游", // 分享的卡片标题
imageUrl: that.data.shareImageUrl, // 分享的图片(如果不传默认本页面截图)
path: `/pages/canvas/canvas/main` // 分享给好友的路径
};
},
//绘制价格
setPrice(ctx, price, priceL) {
ctx.save();
ctx.setFontSize(24);
ctx.setFillStyle('#FFCD17');
if (price) {
ctx.fillText("¥", 20, 295);
ctx.setFontSize(46);
ctx.fillText(price, 36, 295);
ctx.setFontSize(20);
ctx.fillText("/人起", priceL * 25 + 50, 293);
} else {
ctx.fillText("实时计价", 20, 295);
}
ctx.restore();
},
/**该方法用来绘制一个有填充色的圆角矩形 yes
*@param ctx canvas的上下文环境
*@param x 左上角x轴坐标
*@param y 左上角y轴坐标
*@param width 矩形的宽度
*@param height 矩形的高度
*@param radius 圆的半径
*@param fillColor 填充颜色
**/
fillRoundRect(ctx, x, y, width, height, radius, fillColor) {
//圆的直径必然要小于矩形的宽高
if (2 * radius > width || 2 * radius > height) { return false; }
ctx.save();
// 对当前坐标系的原点 (0, 0) 进行平移
ctx.translate(x, y);
// 开始创建一个路径 需要调用 fill(填充) 或者 stroke(画框) 才会使用路径进行填充或描边
ctx.beginPath();
//从右下角顺时针绘制,弧度从0到1/2PI(圆的周长是2*PI*R,1/4圆就是PI/2)
ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2, false);
//矩形下边线
ctx.lineTo(radius, height);
//左下角圆弧,弧度从1/2PI到PI
ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI, false);
//矩形左边线
ctx.lineTo(0, radius);
//左上角圆弧,弧度从PI到3/2PI
ctx.arc(radius, radius, radius, Math.PI, Math.PI * 3 / 2, false);
//上边线
ctx.lineTo(width - radius, 0);
//右上角圆弧
ctx.arc(width - radius, radius, radius, Math.PI * 3 / 2, Math.PI * 2, false);
//右边线
ctx.lineTo(width, height - radius);
// 关闭一个路径。会连接起点和终点。如果关闭路径后没有调用 fill 或者 stroke 并开启了新的路径,那之前的路径将不会被渲染。
ctx.closePath();
// 填充颜色
// ctx.stroke()
ctx.setFillStyle(fillColor || "#000");
ctx.fill();
ctx.restore();
}
})
网友评论