WxCanvas

利用canvas微信小程序原生实现的一个绘制微信小程序海报的类,基础库版本>2.9.0。

下面链接中使用的api已被废弃,所以有了本文。

微信小程序生成海报保存到手机

先看效果图

使用场景

该类封装了一些小程序海报生成中常用的方法,方便快速根据设计稿完成画布,减少搬砖时间。除基础功能外支持以下使用:

  1. 多行文字绘制获得动态高度
  2. 图片定宽或者定高,另一边自适应
  3. 圆形图片绘制

npm源码

文末放全部代码

调用示例

js引入

这取决于你放在哪里,你可以使用npm,也可以直接复制文末源代码,然后直接引入即可

import WxCanvas from '../../utils/canvas.js';

需要准备的内容

一段wxml,里面放置了canvas组件,以及一些别的你可能用到的东西,height和width我们采用动态设置的方式,当需要根据文字行数不定,图片高度不定调整时,这是有必要的。

<view class="page"><canvas type="2d" id="myCanvas" style='height:{{height}}px;width:{{width}}px;'></canvas><view bindtap="onSave" class="save">保存</view>
</view>

一段wxss,建议调试时置于视图内方便调试,使用时移出视图,用户看到的东西尽量是html+css实现的。

#myCanvas{position: absolute;left: 50%;top: 50%;transform: translateX(-50%) translateY(-50%);/* left: 100%;top: 0%;transform: translateX(0%) translateY(0%); */
}

js中只需要简单的声明width和height

Page({/*** 页面的初始数据*/data: {width: 0,height: 0},/*** 生命周期函数--监听页面初次渲染完成*/onReady: async function () {//演示代码将在这里完成//你也可以在用户点击生成时,才执行绘制代码,这取决于你的业务逻辑},
})

参数说明

计算画布width和height

// 如果有动态图片高度,文字换行这两种情况才需要如下代码// 定宽或定高,另一边的长根据图片自适应时,可以提前获得图片信息,此时WxCanvas参数可以是个空options
// 680表示设计稿宽度为680,0表示高度根据图片比例自适应
// 下面这行代码仅作演示
//const autoImageInfo = await new WxCanvas({}).getImageInfo("http://media.tcogid.top/canvas_test.jpg", 680, 0);// 如果是一段换行文字,需要提前知晓将占用多少宽度 42 :42个字节一行,26:字体大小,仅用于计算这里不能设置字体大小,20:文字行距
const textLineInfo = new WxCanvas({}).getLineHeight("演示局部的文字样式并且是个多行文字or abcdefg,一行显示几个字节得自己实际衡量并且传入参数,一个英文一个字节,一个中文两个字节", 42, 26, 20);// 如果有动态图片高度,文字换行这两种情况才需要以上代码const width = new WxCanvas({design: 750}).size(680); //可以使用size方法提前换算设计稿尺寸
const height = new WxCanvas({design: 750}).size(1000); //这里高写死1000并没有利用上面求得数值来计算// 设置画布宽高
this.setData({width,height});

初始化实例

// 得到的wcvs实例 非promise的支持链式调佣
let wcvs = new WxCanvas({el: "#myCanvas",design: 750,//设计稿尺寸width: 680,//设计稿中画布宽height: 1000,//设计稿中画布高useDpr: true //是否计算dpr wx.getSystemInfoSync().pixelRatio,调试时可以关,正式时置为true
});await wcvs.init();// 画布初始化,此时有ctx,尝试log一下wcvs可以看到更多的内容

size()

size 可以单独计算某个设计稿的数值

// 计算字体大小,设计稿为28的字体换算后可直接用于字体大小设置
const fontSize_28 = wcvs.size(28);
const fontSize_26 = wcvs.size(26);

setOptions

options 官方文档中ctx任意可设置的属性,这将影响全局ctx的属性

wcvs.setOptions(//设置全局通用的属性,比如填充的颜色{font: `bold ${fontSize_28}px serif`,fillStyle: '#d2665f'})// 链式调用继续填充一个#e2e2e2的灰色背景,x,y,width,hegiht 与官方相同// 除此之外你仍然可以传入第五个参数,与setOptions的属性相同,只不过它并不污染全局ctx的属性,只对当前调用的方法生效,也就是说fillStyle: '#e2e2e2'只对当次矩形填充生效.fillRect(0, 0, 680, 1000, {fillStyle: '#e2e2e2'});

drawImage

当你确定图片的尺寸并且符合你设计稿的要求的,你确实可以这么写,这与你直接ctx.drawImage没有什么区别,只是包装了一下图片信息的处理

//url x  y  w  h
await wcvs.drawImage("http://media.tcogid.top/canvas_test.jpg", 0, 0, 680, 370);// 当然,业务中也有可能你的海报是左上角坐标确定,但定宽或者定高,另一边的长度需要根据图片宽高比自适应的,该方法会自动换算
// 但由此产生的的不确定宽高,可能会影响你的其他内容位置,因此,这个方法返回 对应设计稿尺寸中的宽、高
//imageInfo, x,y   imageInfo在上述 getImageInfo 方法中返回,x,y为左上角坐标
// 下面这行代码中的autoImageInfo 在计算画布width和height中注释的一行代码中可以找到
// await wcvs.drawAutoImage(autoImageInfo, 0, 0);

fillText

当你确保你的文字不需要换行时,请使用这个 同样的,这里可以接受与setOptions的参数相同的参数可以单独给该行文字设置样式

// text  x  y  options
wcvs.fillText("演示默认文字样式", 20, 450);

fillLineText

需要换行文字时,可以使用这个, 同样的,这里可以接受与setOptions的参数相同的参数

其中textLineInfo 已经在计算画布width和height中提前获取

// text 字符串,x,y :左上角坐标,rs 两行文字之间的间隔 ,options
wcvs.fillLineText(textLineInfo, 20, 550, {font: `bold ${fontSize_26}px serif`,fillStyle: '#000'
});

fillRect

绘制矩形

// 底部矩形框
wcvs.fillRect(0, 860, 680, 140, {fillStyle: 'rgb(0,44,62)'
});

fillArc

绘制圆形

// 圆形白底框,默认使用fill填充,如果只需要一个圆圈,自行修改代码,可用作放小程序码
wcvs.fillArc(600, 930, 60, 0, 2 * Math.PI, {fillStyle: '#fff',strokeStyle: '#fff',
});

fillArcImage

绘制圆形的图片

可以绘制用户圆形头像,或者圆形小程序码

圆心x,y,半径r,图片地址
await wcvs.fillArcImage(80,930,50,"https://thirdwx.qlogo.cn/mmopen/vi_32/Q3auHgzwzM6j6yx3D156xibNHM6DuHicMicf1J9jLwaLHgL91sdEjVzmxHKpIMuBrJUnncSHzo2rv1fZM0ahqJf9w/132");await wcvs.fillArcImage(600,930,50,"https://res.wx.qq.com/wxdoc/dist/assets/img/WXACode.fa3d686a.png");
// 最后加一个微信昵称
wcvs.fillText("微信昵称", 160, 935,{fillStyle:'#fff'});

保存到手机,以及自己拓展更多的操作

wcvs.ctx 是Canvas 的绘图上下文,你可以直接在这里操作

wcvs.canvas 返回画布,你可以配合 canvasToTempFilePath 以及 wx.saveImageToPhotosAlbum(Object object) 官方api将其保存到本地相册

画布效果

完成以上代码时,你可以看到的效果(useDpr为fasle)

WxCanvas 源码

以下是全部代码,你可以直接根据需要进行修改

/*** WxCanvas 除init外,所有方法支持链式调* init需要 await* el        节点id* design    设计稿尺寸,这里以750为例,也是比较多使用的 (默认750)* width     画布宽  设计稿定的宽,假设680* height    画布高  设计稿定的宽,假设1000* useDpr    是否开启dpr,建议开启,(默认true),调试时false方便调试用户手机像素密度决定图片实际大小,保存下来才更清晰*           用户是不会在小程序中看到画图的过程的*           结果是直接保存到手机,如果用户需要预览,请css+html 完成更好*/
export default class WxCanvas {constructor(options) {const {el,design,width,height,useDpr = false} = options;const dpr = wx.getSystemInfoSync().pixelRatio;this.dpr = useDpr ? dpr : 1;this.el = el;this.design = design || 750;this.width = width;this.height = height;}/*** 初始化 画布的准备将在次完成*/init() {return new Promise((resolve, reject) => {const query = wx.createSelectorQuery();query.select(this.el).fields({node: true,size: true}).exec((res) => {this.canvas = res[0].node;this.canvas.width = res[0].width;this.canvas.height = res[0].height;this.ctx = this.canvas.getContext('2d');resolve(this);})})}/*** 获取转换后给画布的 数值* @param {设计稿数值} num */size(num) {return (375 * this.dpr * num) / this.design;}/*** 返回换行之后的文本数组* @param {需要换行的字符串} str * @param {一行字节个数} num */getBreak(str, num = 1) {let arr = [];let item = '';let len = 0;let start = 0;for (let i = 0; i < str.length; i++) {let a = str.charAt(i);if (a.match(/[^\x00-\xff]/ig) != null) { //\x00-\xff→GBK双字节编码范围len += 2;} else {len += 1;}if (len >= num || i === (str.length - 1)) {item = str.substring(start, i + 1);arr.push(item);start = i + 1;len = 0;}}//获取字符串长度(汉字算两个字符,字母数字算一个)return arr;}/*** 获取当前设置之后占用了多少的高,以及绘图数组* @param {文字,左上角 x,y轴,一行可以书写的字节数,英文字母1,中文2,每行间隔} text, x, y, num, rs*/getLineHeight(text, num, size, rs) {const texts = this.getBreak(text, num, rs);const info = {texts,_h: texts.length * (size + rs),num,size,rs};return info;}/*** {font : 'bold 100px serif',fillStyle : '#d2665f'} 这将支持更好的个性化* @param {ctx所有支持的配置项} options */setOptions(options) {if (!options) {return this;}Object.keys(options).forEach(item => {this.ctx[item] = options[item];})return this;}/*** @param {ctx所有支持的配置项} options * @param {ctx画布操作的回调函数} callback */setTempOptions(options, callback) {let tempObj = {}Object.keys(options).forEach(item => {tempObj[item] = this.ctx[item];})this.setOptions(options);this.ctx.beginPath();callback();this.ctx.stroke();this.setOptions(tempObj);return this;}/*** 获取图片信息* @param {图片的url 可以是网络,本地代码包,临时路径} url * @param {设计稿定宽} w * @param {设计稿定高} h */getImageInfo(url, w, h) {return new Promise((resolve, reject) => {wx.getImageInfo({src: url,success: function (res) {const r = res.width / res.height;let _w = 0,_h = 0;if (h) { //定高_h = h;_w = _h * r;} else {_w = w;_h = _w / r;}res._w = _w;res._h = _h;resolve(res);return this;}})})}/*** 获取可绘制的图片对象* @param {图片的url 可以是网络,本地代码包,临时路径} path */async getImage(path) {return new Promise((resolve, reject) => {const drImg = this.canvas.createImage();drImg.src = path;drImg.onload = () => {resolve(drImg);return this;}})}/*** 绘图* @param {见字意} url, x, y, w, h */async drawImage(url, x, y, w, h) {const info = await this.getImageInfo(url, w, h);const drImg = await this.getImage(info.path);this.ctx.drawImage(drImg, this.size(x), this.size(y), this.size(w), this.size(h));return this;}/*** 自适应高或者宽,绘图* @param {getImageInfo返回值} info * @param {左上角坐标} x,y*/async drawAutoImage(info, x, y) {const drImg = await this.getImage(info.path);this.ctx.drawImage(drImg, this.size(x), this.size(y), this.size(info._w), this.size(info._h));return this;}/*** 文字* @param {文字,左上角 x,y轴} text, x, y * @param {ctx 可设置的属性} options*/fillText(text, x, y, options = {}) {return this.setTempOptions(options, () => {this.ctx.fillText(text, this.size(x), this.size(y));});}/*** 可换行文字* @param {文字,左上角 x,y轴,一行可以书写的字节数,英文字母1,中文2,每行间隔} text, x, y, num, rs* @param {ctx 可设置的属性} options*/fillLineText(info, x, y, options = {}) {const {texts,num,size,rs} = info;let _x = x,_y = y;return this.setTempOptions(options, () => {texts.forEach((text, index) => {_y = y + (size + rs) * index;this.ctx.fillText(text, this.size(_x), this.size(_y));});});}/*** 绘制矩形* @param {左上角 x,y轴,宽度,高度} x, y, width, height* @param {ctx 可设置的属性} options*/fillRect(x, y, width, height, options = {}) {return this.setTempOptions(options, () => {this.ctx.fillRect(this.size(x), this.size(y), this.size(width), this.size(height));});}/*** 绘制圆形* @param {圆心坐标 x,y轴,半径,圆形起始角度} x,y,r,sAngle,eAngle* @param {ctx 可设置的属性} options*/fillArc(x, y, r, sAngle, eAngle, options = {}) {return this.setTempOptions(options, () => {this.ctx.arc(this.size(x), this.size(y), this.size(r), sAngle, eAngle);this.ctx.fill();});}/*** 绘制圆形图片* @param {圆心坐标 x,y轴,半径 ,图片地址} x,y,r,url* @param {ctx 可设置的属性} options*/async fillArcImage(x, y, r, url) {let tempStyle = this.ctx.strokeStyle;this.ctx.strokeStyle = 'transparent';this.ctx.beginPath();this.ctx.save();this.ctx.arc(this.size(x), this.size(y), this.size(r), 0, 2 * Math.PI);this.ctx.clip();await this.drawImage(url, x - r, y - r, 2 * r, 2 * r);this.ctx.stroke();this.ctx.restore();this.ctx.strokeStyle = tempStyle;}
}

微信小程序2.9.0基础库canvas2D新API,生成海报保存到手机功能实现,包括文字换行,圆形图片,图片高度自适应等功能封装相关推荐

  1. 微信小程序中使用vant组件库(超详细)

    目录 前言 Vant Weapp的安装与使用 1.安装 node.js 2.通过 npm 安装 3.修改 app.json 4.修改 project.config.json 5.构建 npm 包 6. ...

  2. 微信小程序开发数据缓存基础知识辨析以及运用实例

    微信小程序开发数据缓存基础知识辨析以及运用实例 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的 ...

  3. 小程序源码:网课查题微信小程序源码下载,题库资源丰富自动采集,支持语音拍照识别

    这是一款网课查题微信小程序源码 题库资源丰富自动采集, 支持语音拍照识别 该款采用接口方式,所以题库自动全网采集 而且该款小程序无需服务器和域名即可搭建 大家解压源码然后使用微信开发者工具打开源码 然 ...

  4. 微信小程序名片3:0大战纸质名片,你应该知道如何选择了吧

    一大早,朋友圈和微博就被"阿根廷"和"梅西"这两个词刷屏了,小编我是个伪球迷,不敢妄下评论,但我还是心疼梅西,想起一个梗"我是梅西现在慌得一比&quo ...

  5. 微信小程序导入Vant Weapp组件库

    超详细!微信小程序导入Vant Weapp组件库 Vant Wepp组件库轻量.可靠,同时组件样式丰富美观,容易上手,我们可以引入该组件库来实现小程序的相关界面样式. Vant Weapp组件库官方文 ...

  6. 微信小程序中使用阿里巴巴图标库

    这里写自定义目录标题 微信小程序中使用阿里巴巴图标库 第一步 第二步 第三步 微信小程序中使用阿里巴巴图标库 第一步 阿里巴巴图标正常加入自己的项目,并下载到本地: 注意:这里的选择 ![在这里插入图 ...

  7. 微信小程序把玩(三十五)Video API

    原文:微信小程序把玩(三十五)Video API 电脑端不能测试拍摄功能只能测试选择视频功能,好像只支持mp4格式,值得注意的是成功之后返回的临时文件路径是个列表tempFilePaths而不是tem ...

  8. 微信小程序微商城(五):动态API实现商品详情页(下)

    IT实战联盟博客:http://blog.100boot.cn 上一篇:微信小程序微商城(四):动态API实现商品详情页(上) 看效果 加入购物车.gif 开发计划 1.加入购物车悬浮框.商品数量.价 ...

  9. 在H5、微信小程序中使用canvas绘制二维码、分享海报

    在H5.微信小程序中使用canvas绘制二维码.分享海报 文章目录 在H5.微信小程序中使用canvas绘制二维码.分享海报 前言 一.canvas绘制二维码 1.H5中使用canvas 2.微信小程 ...

最新文章

  1. Laravel源码解析之从入口开始
  2. math.floor java_Java Math.floor() 方法
  3. ValidateUtil
  4. [Unity3D]深度相机 Depth Camera
  5. 让无数人头疼的网络攻击到底是什么?究竟隐藏了哪些不为人知的秘密?
  6. Ubuntu安装ffmpeg教程
  7. BP算法详细推导及分析
  8. 大数据趋势下,服务风暴必将来临
  9. 一个大学教授让人发冷汗的讲演(浙大高分子物理郑强教授)
  10. PHPStorm的资料网址
  11. flea-frame-cache使用之Redis接入【旧】
  12. Office Tool Plus 下载使用 365 2021/2019等版本
  13. 十六进制编辑器HxD Hex Editor x64 v2.3.0.0
  14. 美女画廊(点击上面的图片下面进行显示)
  15. 固态硬盘(SSD)原理及相关介绍
  16. 漫画:什么是 “幼态持续” ?
  17. 计算机网络——不同网段下的主机通信
  18. 用html制作编写静态日志,[译] 编写一个小型静态网站生成器
  19. LevelDB Arena源码分析
  20. 互联网人:我太太太太太太太太太太难了!

热门文章

  1. 【 uC/OS II 】uC/OS II 源代码阅读(os_mbox.c)消息邮箱
  2. 2023-07-013——或许大多数人都不是那么耀眼,但我认为当他或她能够独立又骄傲的撑起自己的天,也如明星般闪耀
  3. c语言结束程序符,c语句的结束符是什么
  4. BIOS 中断服务 设置颜色
  5. 从社团怎么选带头大哥来看DR与BDR选举
  6. KB编号、MS编号、CVE编号的区别
  7. 用ls和du显示的文件大小为何有差别
  8. springboot 前缀_SpringBoot自动加载路由前缀
  9. php实现秒杀、抢购的消息队列
  10. mount: 未知的文件系统类型“viboxsf”