一、关于canvas绘图,小程序官方文档目前有新旧两种接口,分别如下:

1、Canvas 2D 示例代码

<!-- canvas.wxml --><canvas type="2d" id="myCanvas"></canvas>onReady() {const query = wx.createSelectorQuery()query.select('#myCanvas').fields({ node: true, size: true }).exec((res) => {const canvas = res[0].nodeconst ctx = canvas.getContext('2d')const dpr = wx.getSystemInfoSync().pixelRatiocanvas.width = res[0].width * dprcanvas.height = res[0].height * dprctx.scale(dpr, dpr)ctx.fillRect(0, 0, 100, 100)})}

2、示例代码(旧的接口)

<!-- canvas.wxml -->
<canvas style="width: 300px; height: 200px;" canvas-id="firstCanvas"></canvas>onReady() {// 使用 wx.createContext 获取绘图上下文 contextvar context = wx.createCanvasContext('firstCanvas')context.setStrokeStyle("#00ff00")context.setLineWidth(5)context.rect(0, 0, 200, 200)context.stroke()context.setStrokeStyle("#ff0000")context.setLineWidth(2)context.moveTo(160, 100)context.arc(100, 100, 60, 0, 2 * Math.PI, true)context.moveTo(140, 100)context.arc(100, 100, 40, 0, Math.PI, false)context.moveTo(85, 80)context.arc(80, 80, 5, 0, 2 * Math.PI, true)context.moveTo(125, 80)context.arc(120, 80, 5, 0, 2 * Math.PI, true)context.stroke()context.draw()}

目前网上教程大部分是针对旧的api(第二种示例)的解析,相比于第一种示例(canvas2d)旧版本效率低下且官方明示不在维护。

二、鉴于canvas2d官方文档的某种特性(混乱不堪,投诉无门,秉承鹅厂爱答不理的一贯作风),故整理以下内容:

小程序分享海报案例

标题示例图

1、官网文档地址 https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html

2、

, 特别注意第6、7条:

1. 在ios中不设置宽高生成图片大小会错乱;

2. 安卓系统中dpr超过3时会报如下错误:  canvasToTempFilePath:fail:convert native buffer parameter fail. native buffer exceed size       limit,解决办法把dpr设置为1即可;

3.绘图初始化 ,由于绘图分先后顺序 所有方法用Promise,绘制文本时特别注意 字体一定要是系统中有的字体 否则文字绘制不上

 /*** 绘图初始化* @constructor*/public DrawInit() {const that = this;const query = wx.createSelectorQuery().in(that); //如果是在组件中,则改成             this.createSelectorQuery()(query as any).select("#sharePic").fields({ node: true, size: true }).exec((res) => {const canvas = res[0].node;const ctx = canvas.getContext("2d");//dpr大于3 安卓绘制失败 此处设为1const dpr = wx.getSystemInfoSync().pixelRatio;canvas.width = 750 * 1;canvas.height = 1220 * 1;ctx.scale(1, 1);const CanvasD = new CanvasDraw(canvas, ctx);that.CanvasD = CanvasD;CanvasD.DrawImg2d(that.shareInfo.backImg).then(() => {CanvasD.drawLogo().then(() => {CanvasD.drawSiteInfo().then(() => {CanvasD.roundRect(40, 268, 670, 912, 10).then(() => {CanvasD.drawTXT(that.shareInfo.txtArr).then(() => {CanvasD.drawQrCode(that.shareInfo.qrCode).then(() => {CanvasD.DrawImg2d(that.shareInfo.liveIcons, 60, 1018, 92, 36).then(() => {CanvasD.roundRect(40, 268, 670, 670, 0).then(() => {that.covertImageAsround(that.shareInfo.actImg).then((actImg) => {CanvasD.DrawImg2d(actImg, 40, 268, 670, 670).then(() => {CanvasD._canvasToPath(that).then((res: string) => {console.error(res, "-----------");//七牛图片倒角方法// that.canvasImage = `${res}?roundPic/radius/10`;that.canvasImage = res;wx.hideLoading();});});});});});});});});});});});});// });}

分装的canvasDwaw方法

import { AppUrls } from "@/utils/consts";
import { CacheManager } from "@/services/cache/cacheManger";
import { IPSMainService } from "@/services/ipsService/ipsMainService";
import { WXAppSDK } from "@/services/weixin/wxAppSDK";
// const debug = require("debug")("log:Canvas2d");
const getSiteInCache = CacheManager.getSiteInCache;
export default class CanvasDraw {protected WIDTH: number;protected HEIGHT: number;protected rate: number;protected ctx: any = null;protected dpr = 0;protected canvasNode: any = null;protected canvasId = "";protected siteInfo: SiteInfo = {};protected canvasImage = "";constructor(canvasNode, ctx, WIDTH?: number, HEIGHT?: number, rate?: number) {// super(canvasId);this.ctx = ctx;this.canvasNode = canvasNode;// debug(canvasId);this.WIDTH = WIDTH || 750;this.HEIGHT = HEIGHT || 1220;this.rate = rate || 1;this.siteInfo = getSiteInCache() || {};this.init();}public init() {this.dpr = wx.getSystemInfoSync().pixelRatio;}public DrawImg2d(url, x?, y?, w?, h?) {console.error(url);const that = this;return new Promise((resolve, reject) => {this.ctx.beginPath();this.ctx.save();this.ctx.restore();if (!url) {resolve(1);return;}const img = this.canvasNode.createImage(); //canvas 2d 通过此函数创建一个图片对象img.onload = (e) => {console.log("img loaded");console.log("drawImage", (x || 0) * that.rate, (y || 0) * that.rate, (w || that.WIDTH) * that.rate, (h || that.HEIGHT) * that.rate);that.ctx.drawImage(img, (x || 0) * that.rate, (y || 0) * that.rate, (w || that.WIDTH) * that.rate, (h || that.HEIGHT) * that.rate);that.ctx.restore();// that.ctx.drawImage(res.path, (x || 0) * that.rate, (y || 0) * that.rate, res.width * that.rate, res.height * that.rate);resolve(that.ctx);console.log("getImageInfo", e);};img.onerror = (e) => {console.error("err:", e);};img.src = url;console.log("绘制图片公共方法", url);});}/*** 获取siteLogo* (转换为七牛地址)*/public getSiteLogo() {return new Promise((resolve) => {IPSMainService.getSiteLog({postParams: { siteId: this.siteInfo.siteId }}).then((siteLogo) => {resolve(siteLogo);}).catch((errorMessage) => {throw errorMessage;});});}/*** 绘制店铺LOGO* @param shareCanvas* @param res* @param ctx* @param rate*/public drawLogo() {const that = this;return new Promise((resolve, reject) => {that.getSiteLogo().then((siteLogo) => {that.ctx.save();// let siteLogo = shareCanvas.createImage();//绘制外层圆const avatarurl_width = 112 * that.rate, //绘制的头像宽度avatarurl_heigth = 112 * that.rate, //绘制的头像高度avatarurl_x = (that.WIDTH * that.rate - avatarurl_width) / 2, //绘制的头像在画布上的位置avatarurl_y = 32 * that.rate, //绘制的头像在画布上的位置//绘制内层圆s_avatarurl_width = 100 * that.rate, //绘制的头像宽度s_avatarurl_heigth = 100 * that.rate, //绘制的头像高度s_avatarurl_x = (that.WIDTH * that.rate - s_avatarurl_width) / 2, //绘制的头像在画布上的位置s_avatarurl_y = 40 * that.rate; //绘制的头像在画布上的位置that.ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, avatarurl_width / 2, 0, Math.PI * 2);that.ctx.fillStyle = "#e4e4e4";that.ctx.fill();that.ctx.restore();that.ctx.save();that.ctx.beginPath();that.ctx.arc(avatarurl_width / 2 + avatarurl_x, avatarurl_heigth / 2 + avatarurl_y, s_avatarurl_width / 2, 0, Math.PI * 2);that.ctx.fillStyle = "#fff";that.ctx.fill();that.ctx.clip(); //画了圆 再剪切  原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内console.log("绘制店铺LOGO", (siteLogo as any).siteLog);that.DrawImg2d((siteLogo as any).siteLog, s_avatarurl_x, s_avatarurl_y, s_avatarurl_width, s_avatarurl_heigth).then(() => {resolve(that.ctx);});});});}/*** 绘制店铺信息* @param res* @param ctx* @param rate*/public drawSiteInfo() {return new Promise((resolve, reject) => {//siteNamethis.ctx.font = `${32 * this.rate}px Arial`;this.ctx.fillStyle = "#f7f7f7";this.ctx.textAlign = "center";this.ctx.fillText(this.siteInfo.siteName, (750 * this.rate) / 2, 186 * this.rate);// ctx.fillText('139南东 JORDAN', (this.ComputedFontOffsetLeft('139南东 JORDAN', 32 * rate, res[0].width)), 186 * rate);//siteAddrthis.ctx.font = `${24 * this.rate}px Arial`;this.ctx.fillStyle = "#DFDFDF";// ctx.fillText(this.shareInfo.siteInfo.siteAddress, (this.ComputedFontOffsetLeft(this.shareInfo.siteInfo.siteAddress, 24 * rate, res[0].width)), 230 * rate);this.ctx.fillText(this.siteInfo.siteAddress, (750 * this.rate) / 2, 230 * this.rate);resolve(this.ctx);console.log("绘制店铺信息");});}/*** 圆角矩形* @param x X轴* @param y y轴* @param w 宽* @param h 高* @param r 半径* @param ctx*/public roundRect(x, y, w, h, r) {x = x * this.rate;y = y * this.rate;w = w * this.rate;h = h * this.rate;r = r * this.rate;return new Promise((resolve) => {if (w < 2 * r) r = w / 2;if (h < 2 * r) r = h / 2;this.ctx.beginPath();this.ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);this.ctx.moveTo(x + r, y);this.ctx.lineTo(x + w - r, y);this.ctx.lineTo(x + w, y + r);this.ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);this.ctx.lineTo(x + w, y + h - r);this.ctx.lineTo(x + w - r, y + h);this.ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);this.ctx.lineTo(x + r, y + h);this.ctx.lineTo(x, y + h - r);this.ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);this.ctx.lineTo(x, y + r);this.ctx.lineTo(x + r, y);this.ctx.closePath();this.ctx.fillStyle = "#fff";this.ctx.fill();this.ctx.clip();resolve(this.ctx);this.ctx.save();this.ctx.restore();console.log("圆角矩形");});}/*** 绘制海报文本* @param shareCanvas* @param res* @param ctx* @param rate*/public drawTXT(arr) {const TXTArr = arr.filter((item) => {return item.title;});console.log("绘制海报文本", TXTArr);const that = this;return new Promise((resolve, reject) => {for (let index = 0; index < TXTArr.length; index++) {const item = TXTArr[index];if (item.title) {const title = that.workSpace(item.title);//自定义行数const len = title.length > item.line ? item.line : title.length || 1;for (let i = 0; i < len; i++) {console.log(title[i], that.ctx.measureText(title[i]).width);let str = title[i];if (i === len - 1 && str && that.ctx.measureText(str).width > 280 * that.rate) {str += "...";}that.ctx.font = `normal ${item.fontWeight} ${item.fontSize * that.rate}px Arial`; // 设置字体大小that.ctx.fillStyle = item.color; // 设置文字颜色that.ctx.textAlign = item.textAlign;that.ctx.fillText(str, item.x * that.rate, (item.y + (item.fontSize + 10) * i) * that.rate);// that.ctx.fillText(title[i], item.x * that.rate, item.y * that.rate);// 是否显示删除线if (item.isDelLine) {that.ctx.beginPath();that.ctx.moveTo(item.x * that.rate, (item.y - item.fontSize / 2 + 2) * that.rate);that.ctx.lineTo(item.x * that.rate + (item.title.length - 1) * item.fontSize * that.rate, (item.y - item.fontSize / 2 + 2) * that.rate);that.ctx.lineWidth = 2;that.ctx.strokeStyle = item.color;that.ctx.stroke();}that.ctx.save();that.ctx.restore();}}}resolve(this.ctx);console.log("绘制海报文本");});}/*** 文字自动换行*/public workSpace(txt) {const text = this.ctx.measureText(txt);console.log("txt", txt, text.width);const chr = txt.split("");// 保存计算换行后字符let newTxt = "";// 保存结果字符数组const resArr = [];for (let i = 0; i <= chr.length + 1; i++) {if (this.ctx.measureText(newTxt).width < 300 * this.rate) {if (i == chr.length && newTxt) {resArr.push(newTxt);} else {newTxt += chr[i];}} else {i--;newTxt && resArr.push(newTxt);newTxt = "";}}console.log("文字自动换行--------------", resArr);return resArr;}/*** 绘制二维码* @param shareCanvas* @param res* @param ctx* @param rate*/public drawQrCode(url) {const that = this;return new Promise((resolve) => {that.ctx.save();that.ctx.restore();that.DrawImg2d(url, 522 * that.rate, (938 + 20) * that.rate, 154 * that.rate, 154 * that.rate).then(() => {resolve(that.ctx);console.log("绘制二维码", url);});});}/*** 转图片格式*/_canvasToPath(evt) {const that = this;return new Promise((resolve) => {(wx as any).canvasToTempFilePath({x: 0,y: 0,width: that.WIDTH * that.rate,height: that.HEIGHT * that.rate,destWidth: that.WIDTH * that.rate,destHeight: that.HEIGHT * that.rate,fileType: "jpg",quality: 1,canvas: that.canvasNode,success(res) {that.canvasImage = res.tempFilePath;resolve(res.tempFilePath);console.log(res, "canvasToTempFilePath");},fail(err) {console.log(err, "canvasToTempFilePath-fail");}},that);});}/*** @param qrCode 保存图片*/public saveImage(qrCode) {wx.saveImageToPhotosAlbum({filePath: this.canvasImage,success: (img) => {console.error(img);WXAppSDK.errorMessage("图片保存成功");},fail: () => {WXAppSDK.errorMessage("图片保存失败");},complete: () => {}});}
}

小程序 canvas2d 趟坑记录相关推荐

  1. 微信小程序开发采坑记录1

    问题1: 在微信小程序开发过程中,在获取userinfo或其他异步处理函数的过程中采用this碰到一些问题,问题如下: 我的login的success回调函数如下: success: function ...

  2. 如何在一个月空闲时间完成BIM百宝箱小程序之避坑记录

    本人通过一个月的空闲时间(周末及晚上)从完全不懂小程序到完成了BIM百宝箱小程序-一款用来计算工程建设其他费用的计算器并上线运行,现记录全过程: 一.高效学习,在z.cn搜索下载相关书籍.通过cali ...

  3. 微信小程序 NFC 踩坑记录

    需求 读取URL 写入URL 操作流程 一.读取 1. 获取NFC适配器实例 this.NFCAdapter = wx.getNFCAdapter() 2. 开始监听贴卡 this.NFCAdapte ...

  4. mpvue 小程序开发爬坑汇总

    <!-- 小程序的爬坑记录 --> 1 微信小程序之动态获取元素宽高 var obj=wx.createSelectorQuery(); 2 微信小程序图片自适应 <image cl ...

  5. 两百条微信小程序开发跳坑指南(不定时更新)

    2019独角兽企业重金招聘Python工程师标准>>> 微信小程序联盟出品 跳坑textarea<二百二十三>不显示文本及textarea相关问题集合 跳坑<二百一 ...

  6. 【浙政钉】微信-专有钉钉小程序-开发踩坑实记

    文章目录 ⭐[浙政钉]微信-专有钉钉小程序-开发踩坑实记 ⭐ 创建项目 ⭐ 转化方案 ⭐ 政务钉钉调试 ⭐ 上传发布 ⭐[浙政钉]微信-专有钉钉小程序-开发踩坑实记 最近有个需求,要将微信小程序转为浙 ...

  7. 微信小程序初步入坑指南

    微信小程序初步入坑小指南 安装工具 developers.weixin.qq.com/miniprogram- 打开链接下载小程序云开发 app.json 为json格式的文件,为一个配置文件,属于全 ...

  8. 微信小程序继续入坑指南

    微信小程序继续入坑指南 wxml 类似于html 感觉和ejs灰常的相似 数据绑定 js Page({data: {message: "hello world"} }) wxml ...

  9. 微信小程序canvas2d使用封装与案例使用

    微信小程序canvas2d使用封装与案例使用,看一下这边封装效果 canvas2d文档:https://www.canvasapi.cn/ 下载地址:https://download.csdn.net ...

最新文章

  1. 使用PHP+Sphinx建立高效的站内搜索引擎
  2. pandas批量为列名添加字符并重命名实战
  3. linux虚拟机上不了王,虚拟机上安装Linux时出现的问题及解决方法
  4. php+swoole
  5. linux httppost 请求接口参数被截断_记一次小程序图片安全接口和CountDownLatch的使用...
  6. Axure元件库:ElementUI元件、蚂蚁金服元件
  7. c语言中的switch语句中的break和continue的作用
  8. Springboot+Spring-Security+JWT 实现用户登录和权限认证
  9. 今晚8点直播丨 经典知识库:性能优化那些事
  10. opencv 作图函数
  11. php : 收集整理的非常有用的函数
  12. listctrl 的使用技巧
  13. 达芬奇DaVinci Resolve Studio Mac v17.4.6
  14. 【Python】python帮助文档
  15. 项目立项,项目经理需要做什么
  16. psftp 上传和下载
  17. 绝不误人子弟!零基础应该选择学习Java、PHP,还是前端?
  18. typecho插件:用访问量统计插件
  19. WX系列无线漫游的配置
  20. 33 | 如何做好验收测试?

热门文章

  1. 七年级上册英语书人教版单词表第三单元
  2. Golang特辑---简单谈谈我所认为的垃圾回收机制
  3. Java学习笔记之三:Nexus的安装和启动
  4. 炫酷大屏demo_20套大屏模板,教你3分钟制作出酷炫的可视化大屏
  5. 东莞1号线大朗站—湿地公园站右线盾构始发
  6. Python:结合列表和字典,编写一个简单的学生成绩管理系统
  7. 【单元测试】pytest:配置|断言|参数化|夹具|装饰器|插件
  8. Error running : No valid Maven installation found. Either set the home directory in the
  9. qt做触摸屏演示程序
  10. Unity(转载) IPhone机型判断