根据开发过程中遇到的一些问题,整理总结出来的小程序的canvas的相关使用
适合新手,配合小程序开发文档食用更佳~
首先我们来看一下设计图(图比较大)
技术点:(我个人遇到的)
1、小程序相册授权功能(拒绝后重新调起授权)
2、绘制网络图片
3、文字换行(单行超出省略号)
4、canvas生成图片
5、canvas的大小随着手机屏幕自适应
下面我们开始小程序 canvas的使用教程(完全新手那种教程,我用的是低版本的canvas)
先看一下微信小程序的开发文档
https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html
https://developers.weixin.qq.com/miniprogram/dev/api/canvas/wx.createOffscreenCanvas.html
然后直接上代码!!!
wxml
tip:小程序请求授权时,如果点击拒绝,短时间内不能够再调起,找了很久,最后还是只能替换一下按钮,通过 button 里的openSetting 可以打开小程序的相册权限设置
文档里面原话是:调起客户端小程序设置界面,返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
<view class='box'>
<canvas canvas-id="myCanvas" style='position:relative;width: {{canvasWidth}}px;height:{{canvasHigth}}px;left:{{canvasLeft}}px;top:{{canvasTop}}px'></canvas>
</view>
<view class='bottom'>
<view class='btn_qrcode' wx:if='{{saveImgRight}}' bindtap="saveQrcode">仅保存二维码</view>
<!-- 未授权 -->
<button class='btn_qrcode' wx:if='{{!saveImgRight}}' open-type="openSetting">仅保存二维码</button>
<view class='btn_card' wx:if='{{saveImgRight}}' bindtap="saveImg">保存名片到相册</view>
<!-- 未授权 -->
<button class='btn_card' wx:if='{{!saveImgRight}}' open-type="openSetting">保存名片到相册</button>
</view>
定义一个canvas,取好id名(canvas-id:canvas 组件的唯一标识符),定义宽高(后期处理为自适应,注意这里使用的单位是 px)
js的展示部分 按照我的代码顺序介绍
data: {
logo: 'https://xxxxxxxxx/logo_c.png',//logo的网络地址
qcode: '',//二维码信息
doctor: {},//医生信息
shareImg: '',//名片图片路径
shareQrcode: '',//二维码图片路径
canvasWidth: '',//名片canvas宽度
canvasHigth: '',
canvasLeft: '',//名片canvas位置
canvasTop:'',
saveImgRight:true,//图片保存授权
},
在onload的时候,处理好canvas画布的宽高
因为在绘制内容时所应用的单位是 px,但是小程序canvas的API并没有像其他的一样支持小程序独有的 rpx 自适应尺寸单位,而设计稿的尺寸目测常规的都是按照 iPhone6 屏幕尺寸(375*667)来制作的。
如果想要适应其他尺寸的屏幕时其实需按照iPhone6的绘制大小按比例进行换算即可:
onLoad: function (options) {
let that = this;
let rpx = 1;
wx.getSystemInfo({
success(res) {
rpx = res.windowWidth / 375;
},
})
that.setData({
canvasWidth: 305 * rpx,
canvasHigth: 500 * rpx,
canvasLeft: 35 * rpx,
canvasTop: 20 * rpx,
})
that.downLoadImg();
that.getBasicInfo();
},
在onshow的时候 我调用了 getAuthor() 这个方法,主要用于判断用户授权
onShow: function () {
const that = this;
that.getAuthor();
},
获取相册授权
//获取相册授权
getAuthor() {
let that = this;
wx.getSetting({
success(res) {
if (res.authSetting['scope.writePhotosAlbum'] == true) {
console.log("用户已授权")
that.setData({
saveImgRight: true
})
} else if (res.authSetting['scope.writePhotosAlbum'] == false) {
console.log("用户点击拒绝授权")
that.setData({
saveImgRight: false
})
} else {
console.log("用户未授权--没有点击同意,也没点击拒绝")
that.setData({
saveImgRight: true,
})
}
}
})
},
下载网络图片
小程序 canvas 在绘制网络图片时,需要先下载到本地,再用本地生成的路径进行绘制
一定要在小程序中配置好网络图片的路径
// 下载logo图片
downLoadImg: function () {
let that = this;
wx.getImageInfo({
src: that.data.logo,//网络图片地址
success: function (res) {
wx.setStorageSync('logo_canvas', res.path);
}
});
},
绘制圆角矩形
我遇到的第一个问题是:绘制带圆角的矩形,然后我找到了写的最简洁的一个方法(因为搜索时候 打开的页面太多了 已经忘记了原作者是谁。。 再找到会补充链接过来)
// 蓝色圆角矩形
roundRectPath: function (rect, x, y, w, h, r) {
//rect 是接下来绘图时传入的canvas id, x,y:距离左上角的距离
//w,h:绘制图形的宽高, r: 圆角的半径
rect.beginPath();
rect.moveTo(x + r, y);
rect.arcTo(x + w, y, x + w, y + h, r); // 右上
rect.arcTo(x + w, y + h, x, y + h, r);
rect.arcTo(x, y + h, x, y, r);
rect.arcTo(x, y, x + w, y, r);// 左上
rect.closePath();
rect.fillStyle = "#4892FF"; //填充一个矩形
rect.fill();
},
然后白色的部分,我还是用的这个方法,绘制的下半部分有圆角,上半部分没有
调整一下 右上和左上部分,删除 r 即可
文字换行加省略号
文字换行,同样是抄的别的小朋友的方法(开发中日常感谢大家的无私奉献,找到链接再来贴)
// 文字换行
dealWords: function (options) {
options.ctx.setFontSize(options.fontSize);//设置字体大小
var allRow = Math.ceil(options.ctx.measureText(options.word).width / options.maxWidth);//实际总共能分多少行
var count = allRow >= options.maxLine ? options.maxLine : allRow;//实际能分多少行与设置的最大显示行数比,谁小就用谁做循环次数
var endPos = 0;//当前字符串的截断点
for (var j = 0; j < count; j++) {
var nowStr = options.word.slice(endPos);//当前剩余的字符串
var rowWid = 0;//每一行当前宽度
if (options.ctx.measureText(nowStr).width > options.maxWidth) {//如果当前的字符串宽度大于最大宽度,然后开始截取
for (var m = 0; m < nowStr.length; m++) {
rowWid += options.ctx.measureText(nowStr[m]).width;//当前字符串总宽度
if (rowWid > options.maxWidth) {
if (j === options.maxLine - 1) { //如果是最后一行
options.ctx.fillText(nowStr.slice(0, m - 1) + '...', options.x, options.y + (j + 1) * 18); //(j+1)*18这是每一行的高度
} else {
options.ctx.fillText(nowStr.slice(0, m), options.x, options.y + (j + 1) * 18);
}
endPos += m;//下次截断点
break;
}
}
} else {//如果当前的字符串宽度小于最大宽度就直接输出
options.ctx.fillText(nowStr.slice(0), options.x, options.y + (j + 1) * 18);
}
}
},
开始绘图
// canvas 画图--名片
drawImg() {
let that = this;
wx.showLoading({
title: '名片生成中',
})
let rpx = 1;
wx.getSystemInfo({
success(res) {
rpx = res.windowWidth / 375;
},
})
const ctx = wx.createCanvasContext('myCanvas');
//此处我先填充了一个背景色为#f4f4f4的矩形,作为生成图片保存时候的背景色
//不然白色的圆角看不出来,默认canvas的背景就是白色的
ctx.fillStyle = '#f4f4f4';
ctx.fillRect(0, 0, 305 * rpx, 500 * rpx);
this.roundRectPath(ctx, 0, 0, 305 * rpx, 500 * rpx, 10 * rpx);
this.whiteRect(ctx, 0, 195 * rpx, 305 * rpx, 305 * rpx, 10 * rpx);
// 文字部分
ctx.font = "normal 14px PingFangSC-Regular" //设置字体
ctx.fillStyle = '#BCD7FF'; //设置字体颜色
ctx.fillText('xxxxxxxxxxx', 16 * rpx, 32 * rpx); //设置文字
// logo图片
let headUrl = wx.getStorageSync('logo_canvas');
if (headUrl){
ctx.drawImage(headUrl, 252 * rpx, 18 * rpx, 35 * rpx, 35 * rpx) //绘制网图
}
// 医生姓名 —— 这里有加省略号 下面有重复的 我就没有粘代码了
ctx.fillStyle = '#ffffff';
ctx.font = "normal bold 25px PingFangSC-Regular"
that.dealWords({
ctx: ctx, //画布上下文 canvasID
fontSize: '25', //字体大小
word: that.data.doctor.merchants, //需要处理的文字
maxWidth: 240 * rpx, //一行文字最大宽度
x: 16 * rpx, //文字在x轴要显示的位置
y: 60 * rpx, //文字在y轴要显示的位置
maxLine: 1
})
// 二维码
let qcode = wx.getStorageSync('qcode_canvas');
if (qcode) {
ctx.drawImage(qcode, 55 * rpx, 250 * rpx, 190 * rpx, 190 * rpx)
}
// 画图
ctx.draw();//必须调用这个方法,才能绘图
// 生成图片 —— 我这里是直接先生成好图片,防止保存时出现不必要的延时 可以自行调整
that.completeImg();
wx.hideLoading();
},
生成图片( wx.canvasToTempFilePath )
// 生成名片图片
completeImg() {
let that = this;
wx.canvasToTempFilePath({
x: 0,
y: 0,
canvasId: 'myCanvas',
fileType: 'jpg',
success: function (res) {
let shareImg = res.tempFilePath;
that.setData({
shareImg: shareImg
});
},
fail: function (res) {
}
})
},
保存图片(wx.saveImageToPhotosAlbum)
// 保存名片图片到相册
saveImg() {
let that = this;
wx.authorize({
scope: 'scope.writePhotosAlbum',
success() {
if (that.data.shareImg) {
wx.saveImageToPhotosAlbum({
filePath: that.data.shareImg,
success(res) {
let isOK = res.errMsg.search('ok');
if (isOK != -1) {
wx.showLoading({
title: '保存成功',
})
setTimeout(wx.hideLoading, 1000)
}
}
})
} else {
that.completeImg();
}
},
fail(res) {
//如果授权失败,判断一下返回语句中有没有 deny 更换一下显示按钮
let isDeny = res.errMsg.search('deny');
if (isDeny>0) {
that.setData({
saveImgRight: false
})
}
}
})
},
因为我这里还需要单独保存二维码,所以我的页面上增加了一个canvas
想要隐藏canvas,不可以直接在标签内直接 display:none,会影响canvas的绘制,我们需要给它加个外壳,对外部的view隐藏处理就可以了,但是如果这个canvas 展示在可视区内,在手机上看的时候,还是会展示出来,我这里就把这个需要隐藏的canvas,位置left:-9999px
最后写法是下面这样的
<view style='width:0px;height:0px;overflow:hidden;'>
<canvas canvas-id="qrCanvas" style='width: 200px;height:200px;position:relative; top:-160px;left:-9999px;'></canvas>
</view>
至此,我的首次canvas开发就结束啦~~ 希望对你有用!