小程序canvas总结
遇到的问题:
- 无法获取canvas实例
- canvas 接收的px要做成rpx
- 当把canvas当组件调用时,图片不显示
- canvas绘画线上图片失败
- canvas绘画文字不能换行
- 当把canvas当做组件调用时,在父组件会高度失效
解决:
1、无法获取canvas实例
小程序不能用document属性,wx.createOffscreenCanvas这个也已经废除了
微信上给的例子是这样的
Page({
onReady() {
const query = wx.createSelectorQuery()
query.select('#myCanvas')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = res[0].width * dpr
canvas.height = res[0].height * dpr
ctx.scale(dpr, dpr)
ctx.fillRect(0, 0, 100, 100)
})
}
})
因为我用的uni的,从uni上找到了相似的方法,uni.createCanvasContext(canvasId, this),坑倒是没有主要是之前找方法的时候想用创建离线的canvas结果失败了。
2、rpx 转px
canvas只接受px传过去之前要换算一下,先用getSystemInfoSync.screenWidth获取设备宽度,然后根据宽度与设计稿的宽度算出比例,忘记时看的哪位的方法了,下次我见到会把连接放进来
/** 获取设备信息 */
// eslint-disable-next-line no-undef
private system = uni.getSystemInfoSync();
private screenWidth = this.system.screenWidth;
private factor = this.screenWidth / 750; // 获取比例
private topx(rpx: number) {
return Math.round(rpx * this.factor);
}
3、当把canvas当组件调用时,图片不显示
canvas 获取实例时,有两个参数,第一个是类名,第二个是代表现在的this要传。
// eslint-disable-next-line @typescript-eslint/no-this-alias
const that = this;
// eslint-disable-next-line no-undef
const ctx = uni.createCanvasContext('myCanvas', that);
4、线上地址绘画失败
从线上拿来的地址直接用,不显示图片,先用getImageInfo下载到本地,然后再用拿到的临时路径绘画
// 获取图片方法
private getImgPath(src: any) {
return new Promise((resolve, reject) => {
// eslint-disable-next-line no-undef
uni.getImageInfo({
src: src,
success: (res: any) => {
resolve(res);
},
fail: (res: any) => {
reject(res);
},
});
});
}
/** 把线上地址下载到本地才能绘画 */
this.getImgPath(sliderImage[0]).then((resolve: any) => {
this._img(resolve.path);
});
5、绘画文字不能换行
canvas绘画文字没有warp这个属性,自己可以定义个方法让她逐字绘画,然后算宽度,超过就让绘画的高度加一点,模拟换行
/**
* ctx canvas 对象
* str 文本
* leftWidth 距离左侧的距离
* initHeight 距离顶部的距离
* titleHeight 文本的高度
* canvasWidth 文本的宽度
*
*/
private drawText(
ctx: any,
str: string,
leftWidth: any,
initHeight: any,
canvasWidth: any
) {
let lineWidth = 0;
for (let i = 0; i < str.length; i++) {
ctx.setFontSize(this.topx(30));
ctx.setFillStyle('#232323');
ctx.fillText(str.substring(i, i + 1), leftWidth + lineWidth, initHeight); // 一个字一个字绘制
lineWidth += ctx.measureText(str[i]).width;// 字体宽度累计相加
// 如果宽度超过了canvas的宽度,就换行渲染
if (leftWidth + lineWidth > canvasWidth) {
initHeight += 20;
lineWidth = 0;
}
}
}
6、当把canvas当做组件调用时,在父组件会高度失效
当用组件调用canvas时,在父组件设置绘画的高度,不成功,最后我也没解决,把组件里面的代码都复制到了本页面,没有用组件。
总的代码
/** 获取设备信息 */
// eslint-disable-next-line no-undef
private system = uni.getSystemInfoSync();
private screenWidth = this.system.screenWidth;
private factor = this.screenWidth / 750; // 获取比例
private topx(rpx: number) {
return Math.round(rpx * this.factor);
}
private _img(img: any) {
/** 设置分享的标题,价格 */
const { otPrice, price, title } = this.shopDetailsArr;
// eslint-disable-next-line @typescript-eslint/no-this-alias
const that = this;
// eslint-disable-next-line no-undef
const ctx = uni.createCanvasContext('myCanvas', that);
/** 设置背景 */
ctx.rect(0, 0, this.topx(600), this.topx(1000));
ctx.setFillStyle('#fff');
ctx.fill();
/** 画图片 */
ctx.drawImage(
img,
this.topx(20),
this.topx(20),
this.topx(560),
this.topx(600)
);
/** 画文字 */
/** ¥ */
ctx.font = 'normal 20px STXingkai'; // 字体
ctx.setFillStyle('#E53B64');
ctx.setFontSize(this.topx(28));
ctx.fillText('¥', this.topx(40), this.topx(690)); //文字内容、x坐标,y坐标
/** 1299 */
ctx.setFontSize(this.topx(56));
ctx.fillText(price, this.topx(66), this.topx(690));
ctx.setFontSize(this.topx(28));
ctx.setFillStyle('#B0B8C9');
ctx.fillText('¥', this.topx(200), this.topx(685)); //文字内容、x坐标,y坐标
ctx.setFontSize(this.topx(28));
ctx.setFillStyle('#B0B8C9');
ctx.fillText(otPrice, this.topx(230), this.topx(685));
/** 线 */
ctx.beginPath();
ctx.setLineWidth(0.5);
ctx.moveTo(this.topx(200), this.topx(678));
ctx.lineTo(
ctx.measureText('¥ 1299').width + this.topx(200),
this.topx(678)
);
ctx.stroke();
/** title */
ctx.setFontSize(this.topx(30));
ctx.setFillStyle('#242C42');
this.drawText(ctx, title, this.topx(40), this.topx(740), this.topx(560));
/** 线 */
// ctx.beginPath();
// ctx.setLineWidth(this.topx(1));
// ctx.moveTo(this.topx(20), this.topx(710));
// ctx.lineTo(this.topx(20), this.topx(980));
// ctx.lineTo(this.topx(980), this.topx(780));
// ctx.lineTo(this.topx(980), this.topx(700));
// ctx.stroke();
// ctx.lineTo(
// ctx.measureText('¥ 1299').width + this.topx(200),
// this.topx(630)
// );
/** 二维码 */
ctx.drawImage(
this.ar,
this.topx(240),
this.topx(820),
this.topx(112),
this.topx(112)
);
ctx.restore();
/** 长按识别 */
ctx.setFillStyle('#242C42');
ctx.setFontSize(10);
ctx.globalAlpha = 0.5;
ctx.fillText('长按识别,查看商品', this.topx(210), this.topx(960));
ctx.draw(false, () => {
// eslint-disable-next-line no-undef
uni.canvasToTempFilePath(
{
x: 0,
y: 0,
width: this.topx(600),
height: this.topx(1000),
destWidth: this.topx(600) * this.system.pixelRatio,
destHeight: this.topx(1000) * this.system.pixelRatio,
fileType: 'png',
canvasId: 'myCanvas',
quality: 1,
success: (res: any) => {
this.getImgPath(res.tempFilePath).then((resolve: any) => {
this.temporaryPath = resolve.path;
});
},
},
that
);
});
}
/** 获取图片临时路径 */
private temporaryPath = '';
private getImgPath(src: any) {
return new Promise((resolve, reject) => {
// eslint-disable-next-line no-undef
uni.getImageInfo({
src: src,
success: (res: any) => {
resolve(res);
},
fail: (res: any) => {
reject(res);
},
});
});
}
/**
* ctx canvas 对象
* str 文本
* leftWidth 距离左侧的距离
* initHeight 距离顶部的距离
* titleHeight 文本的高度
* canvasWidth 文本的宽度
*
*/
private drawText(
ctx: any,
str: string,
leftWidth: any,
initHeight: any,
canvasWidth: any
) {
let lineWidth = 0;
for (let i = 0; i < str.length; i++) {
ctx.setFontSize(this.topx(30));
ctx.setFillStyle('#232323');
ctx.fillText(str.substring(i, i + 1), leftWidth + lineWidth, initHeight); // 一个字一个字绘制
lineWidth += ctx.measureText(str[i]).width;
if (leftWidth + lineWidth > canvasWidth) {
initHeight += 20;
lineWidth = 0;
}
}
}