微信小程序 canvas type=2d 使用心得
为了方便这里我封装成了一个component然后说说怎么使用最新的方法(使用方法类似于html中的canvas可以进行参考)获取--canvas demo实例和画笔ctx----method中封装了各种绘制的方法*(文字,图片,圆角图片,圆角矩形等,还有利用for循环进行绘制多个圆角文字)*为了方便这里我封装成了一个component
shop-canvas 就是我当前的canvas-----之后的绘图操作都在其中–可以自行更改
然后在index.json中引入该 页面级的 canvas组件
<shop-canvas wx:if="{{shopData}}" shopData="{{shopData}}" renderCanvas="{{renderCanvas}}" class="{{false ? 'hidden_canvas' : ''}}"></shop-canvas><view style="padding: 20rpx;text-align: center;margin: 50rpx;border-radius: 20rpx;color: #f00; border: 1rpx solid #f00" bindtap="onDownloadShopInfo" wx:if="{{shopData}}">点击下载当前门店信息</view>
这里shopData为模拟的数据---进行判断--只有从后端获取到数据后--再进行后续操作---页面加入判断--如果没有数据,那么绘制海报的按钮就不进行展示
.hidden_canvas {position: fixed;left: -9999999rpx;}//这里加入 这个class类的原因是 因为我绘制的canvas不需要在页面上进行 展示---绘制完成后--直接进行下载到手机操作了。---根据自己情况进行处理--是否加入
然后说说怎么使用最新的方法(使用方法类似于html中的canvas可以进行参考)获取–canvas demo实例和画笔ctx----method中封装了各种绘制的方法*(文字,图片,圆角图片,圆角矩形等,还有利用for循环进行绘制多个圆角文字)*
注意: 可以自行选择–method下的方法
// index/shop-canvas/canvas.jsComponent({/*** 组件的属性列表*/properties: {shopData: {// 获取数据type: Object,value: {}},renderCanvas: {// 只有在父组件点击下载按钮的时候--开始进行绘制type: Boolean,value: false}},/*** 组件的初始数据*/data: {startX: 14, // 这个在drawMultipleArcTo的时候用到---使用for循环进行一次绘制多个圆角文字并且自动换行startY: 20},/*** 组件的方法列表*/methods: {// 获取canvas实例和画笔getMyCanvasAndCtx(id) {return new Promise(resolve => {const query = wx.createSelectorQuery().in(this)//因为在组件中----所以要加入 .in(this)如果在页面中可以去掉query.select(`#${id}`).fields({node: true,size: true}).exec((res) => {const canvas = res[0].nodeconst ctx = canvas.getContext('2d')const dpr = wx.getSystemInfoSync().pixelRatio// 这里是为了把画布放大dpr倍进行绘制--利用css中达到在手机中 高清显示canvas.width = res[0].width * dprcanvas.height = res[0].height * dprctx.scale(dpr, dpr)this.setData({canvas: canvas,ctx: ctx}, () => {resolve({canvas,ctx})})})})},//绘制图片的方法drawImageMethod({imgUrl,x,y,imgWidth,imgHeight}) {return new Promise(resolve => {//绘制头像const img = this.data.canvas.createImage();img.src = imgUrl;img.onload = () => {this.data.ctx.save(); //保存绘图上下文this.data.ctx.beginPath(); //开始绘制this.data.ctx.drawImage(img, x, y, imgWidth, imgHeight);this.data.ctx.restore(); //恢复之前保存的绘图上下文 恢复之前保存的绘图问下文即状态 还可以继续绘制resolve()};})},// 绘制有圆角的图片drawRoundImage({imgUrl,x,y,imgWidth,imgHeight,bg_r}) {// bg_r 图片圆角return new Promise(resolve => {//绘制头像const img = this.data.canvas.createImage();img.src = imgUrl;img.onload = () => {this.data.ctx.save(); //保存绘图上下文this.data.ctx.beginPath(); //开始绘制// 绘制圆角this.data.ctx.arc(x + bg_r, y + bg_r, bg_r, Math.PI, Math.PI * 1.5)this.data.ctx.arc(x + imgWidth - bg_r, y + bg_r, bg_r, Math.PI * 1.5, Math.PI * 2)this.data.ctx.arc(x + imgWidth - bg_r, y + imgHeight - bg_r, bg_r, 0, Math.PI * 0.5)this.data.ctx.arc(x + bg_r, y + imgHeight - bg_r, bg_r, Math.PI * 0.5, Math.PI)this.data.ctx.clip();this.data.ctx.drawImage(img, x, y, imgWidth, imgHeight); //绘制图像到画布this.data.ctx.restore();resolve();};})},// 绘制有圆角的矩形框roundRect(x, y, w, h, r, color) {/*** * @param {CanvasContext} ctx canvas上下文* @param {number} x 圆角矩形选区的左上角 x坐标* @param {number} y 圆角矩形选区的左上角 y坐标* @param {number} w 圆角矩形选区的宽度* @param {number} h 圆角矩形选区的高度* @param {number} r 圆角的半径*/// 开始绘制this.data.ctx.save();this.data.ctx.beginPath();// 因为边缘描边存在锯齿,最好指定使用 transparent 填充// 这里是使用 fill 还是 stroke都可以,二选一即可this.data.ctx.fillStyle = color;// ctx.setFillStyle('transparent')// ctx.setStrokeStyle('transparent')// 左上角绘制左上角圆弧this.data.ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)// border-topthis.data.ctx.moveTo(x + r, y)this.data.ctx.lineTo(x + w - r, y)this.data.ctx.lineTo(x + w, y + r);// 右上角this.data.ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2)// border-rightthis.data.ctx.lineTo(x + w, y + h - r)this.data.ctx.lineTo(x + w - r, y + h)// 右下角this.data.ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5)// border-bottomthis.data.ctx.lineTo(x + r, y + h)this.data.ctx.lineTo(x, y + h - r)// 左下角this.data.ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI)// border-leftthis.data.ctx.lineTo(x, y + r)this.data.ctx.lineTo(x + r, y)this.data.ctx.closePath()// 这里是使用 fill 还是 stroke都可以,二选一即可,但是需要与上面对应this.data.ctx.fill()// 剪切this.data.ctx.clip()// 从原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。可以在使用 clip 方法前通过使用 save 方法对当前画布区域进行保存,并在以后的任意时间通过restore方法对其进行恢复。// restore 进行恢复,使其能够访问画布上的其他区域this.data.ctx.restore()},// 绘制多个带有圆角的 文字drawMultipleArcTo(ctx, w, r, color) {let x = parseFloat(this.data.startX + '')let y = parseFloat(this.data.startY + '')let marginRight = 10let marginBottom = 16let endX = x + w + 2 * r + marginRightif (endX > 414) {//判断何时进行换行(这里写死为当前我的屏幕宽度414y += 2 * r + marginBottomx = 14 // 换行进行初始化canvas--x坐标endX = x + w + 2 * r + marginRight}this.data.startX = endXthis.data.startY = yctx.beginPath()ctx.moveTo(x + r, y) // 创建开始点ctx.lineTo(x + w + r, y) // 创建水平线ctx.arcTo(x + w + 2 * r, y, x + w + 2 * r, y + r, r) // 创建弧ctx.arcTo(x + w + 2 * r, y + (2 * r), x + w, y + (2 * r), r)ctx.lineTo(x + r, y + (2 * r))ctx.arcTo(x, y + (2 * r), x, y + r, r)ctx.arcTo(x, y, x + r, y, r)ctx.fillStyle = colorctx.fill()return [x + r, y + r]},//绘制单个带有圆角的文字drawSingleArcText(x,y, ctx,w,r,color) {ctx.beginPath()ctx.moveTo(x + r, y) // 创建开始点ctx.lineTo(x + w + r, y) // 创建水平线ctx.arcTo(x + w + 2 * r, y, x + w + 2 * r, y + r, r) // 创建弧ctx.arcTo(x + w + 2 * r, y + (2 * r), x + w, y + (2 * r), r)ctx.lineTo(x + r, y + (2 * r))ctx.arcTo(x, y + (2 * r), x, y + r, r)ctx.arcTo(x, y, x + r, y, r)ctx.fillStyle = colorctx.fill()return [x + r, y + r]}},observers: {async renderCanvas(val) {console.log(val, "valll")if (val) {//获取画布和画笔await this.getMyCanvasAndCtx("myCanvas");console.log(this.data.canvas, this.data.ctx, '获取canvas--2d数据')// 开始绘制海报wx.showLoading({title: '生成中...',})let txtArr = ['11万公里', '', '排量1.6L', '挂牌价11万元','都来看房就大连市会计分录卡']for (let i = 0; i < txtArr.length; i++) {((i) => {this.data.ctx.font = '12px Microsoft YaHei'let item = txtArr[i]let width = this.data.ctx.measureText(item).width // 获取文字的宽度---let coordinates = this.drawMultipleArcTo(this.data.ctx, width, 16, '#f0f') // this.data.ctx.fillStyle = '#ffffff' //文字的颜色this.data.ctx.textBaseline = 'middle'; // 文字在圆角矩形中 垂直居中this.data.ctx.fillText(item, coordinates[0], coordinates[1])})(i)}wx.canvasToTempFilePath({canvas: this.data.canvas, //现在的写法----注意这里新的api改动 使用canvas对象success(res) {console.log(res.tempFilePath, 'res.tempFilePath')wx.saveImageToPhotosAlbum({filePath: res.tempFilePath,success(res) {console.log(res);wx.showToast({title: '保存到相册成功',success() {wx.hideLoading()}})},fail() {wx.showToast({title: '保存到相册失败',success() {wx.hideLoading()}})}})},fail() {wx.showToast({title: '生成失败',success() {wx.hideLoading()}})}})}}}})