小程序的填坑小技巧之Canvas

首先在我们的wxml写下如下代码

<canvas class="canvas" canvas-id="canvas"></canvas>

设置 Canvas 大小,可以直接在 wxml 里的 canvas 标签上设置,也可以用 wcss 。个人比较喜欢用wcss 。

重点来了 canvas-id 是小程序里特有的,是canvas 组件的唯一标识符,之后的很多地方都需要用到。

然后我们就开始写 js 了

 
  1. // 这里的 canvas 就是之前在 wxml 里的 canvas-id

  2. let ctx = wx.createCanvasContext('canvas')

第一个坑的地方来了,正常来 page 里用是一点没问题没有的,但是在组件里的话,画不了,就是显示不出来。

文档能发现下列一段话

创建 canvas 绘图上下文(指定 canvasId)。在自定义组件下,第二个参数传入组件实例this,以操作组件内 <canvas/> 组件Tip: 需要指定 canvasId,该绘图上下文只作用于对应的 <canvas/>

很显然,我们就代码改成如下即可

let ctx = wx.createCanvasContext('canvas', this)

在相应的位置画文字及图片

具体怎么画,我就不讲了,直接开始讲坑的点。

在相应的位置

没错,就一个很坑的点。canvas 是居中的,而且在不同尺寸的屏幕上各个点的位置都不一样。我们需要获取屏幕的宽度以及高度进行计算。(主要是宽度)小程序提供了如下方面,让我们来获取设备的一些信息

 
  1. wx.getSystemInfo({

  2. success: (res) => {

  3. this.setData({

  4. windowWidth: res.windowWidth,

  5. windowHeight: res.windowHeight,

  6. })

  7. }

  8. })

文字

这个是最坑的一个点

看UI给的样式,中间会有一段两行的文字。

开始拿到的时候,觉得so easy。算了每一行的字数,截取两段就不好了嘛。 最后出来的效果确实很坑爹。有长有短,还有超过界限的。

这样明显不能满足我们的需求。而之所以会造成这样的问题。就是因为我们的内容是中英文混合的,中文占两个字符长度而英文只占一个,我们根据字符串的length截取时,中英文都是1。除非是纯英文或者纯中文,才能对齐。怎么解决呢? 上代码!

 
  1. getContent(detail, length = 24, row = 2) {

  2. let len = 0

  3. let index = 0

  4. let content = []

  5. for (let i = 0; len < detail.length; i++) {

  6. // 若未定义则致为 ''

  7. if (!content[index]) content[index] = ''

  8. content[index] += detail[i]

  9. // 中文或者数字占两个长度

  10. if (detail.charCodeAt(i) > 127 || (detail.charCodeAt(i) >= 48 && detail.charCodeAt(i) <= 57)) {

  11. len += 2;

  12. } else {

  13. len++;

  14. }

  15. if (len >= length || (row - index == 1 && len >= length - 2)) {

  16. len = 0

  17. index++

  18. }

  19. if (index === row) break

  20. }

  21. return content

  22. }

我们可以使用 charCodeAt 来获取 charCode,中文的话在 127以上(ps.测试的时候发现 数字也是占两个长度的)。为了预防之后需求的变动 我把这块提取出来写了一个方法。之后只用传入内容,每行的字数以及行数就好。

图片

这里有两个坑

  1. 不能直接使用网络图片,在真机上画不出来
  2. 不能圆角,图片如果是方形的 不会再处理

网络图片使用

既然不能直接使用网络图片,那么我们换个思路,把图片下载到本地,在显示出来是不是就行了。

  1. 下载网络图片
 
  1. wx.getImageInfo({

  2. src: url,

  3. success: (res) => {

  4. // 下载成功 即可获取到本地路径

  5. console.log(res.path)

  6. }

  7. })

直接用这个你会发现,会出错。小程序对能下载的图片做白名单处理。即需要在 小程序后台 > 设置 > 服务器域名 > downloadFile合法域名 里设置网络图片的域名。

ps.因为域名要求是https的, 并且一个月只能修改五次,建议把需要下载的网络图片放在自己的https的服务器上,再走个CDN什么的。

  1. 绘制图片

接下来跟绘制本地图片一样

ctx.drawImage(res.path, left, top, width, height);

图片圆角处理 (本例示范的是圆形,原理一样)

整体思路是画一个圆形区域,再在这块区域把图片画上去,最后进行合并截取就生成了圆形图片。具体代码如下所示

 
  1. drawImage(ctx, url, left, top, width, height) {

  2. // 保存当前环境的状态

  3. ctx.save();

  4. // 起始一条路径,或重置当前路径

  5. ctx.beginPath();

  6. // 画一个圆

  7. ctx.arc(width / 2 + left, height / 2 + top, width / 2, 0, Math.PI * 2, false);

  8. // 从原始画布剪切任意形状和尺寸的区域

  9. ctx.clip();

  10. wx.getImageInfo({

  11. src: url,

  12. success: (res) => {

  13. // 向画布上绘制图像

  14. ctx.drawImage(res.path, left, top, width, height);

  15. // 返回之前保存过的路径状态和属性

  16. ctx.restore();

  17. // 画出来

  18. ctx.draw();

  19. }

  20. })

  21. },

会发现下载图片是一步异步操作,如果只是一个图片还好,如果是多个的话,就会非常的乱,我们可以用 Promise 优化下

 
  1. drawImage(ctx, url, left, top, width, height) {

  2. ctx.save();

  3. ctx.beginPath();

  4. ctx.arc(width / 2 + left, height / 2 + top, width / 2, 0, Math.PI * 2, false);

  5. ctx.clip();

  6. return new Promise((resolve, reject) => {

  7. wx.getImageInfo({

  8. src: url,

  9. success: (res) => {

  10. ctx.drawImage(res.path, left, top, width, height);

  11. ctx.restore();

  12. resolve()

  13. },

  14. fail: (e) => {

  15. reject(e)

  16. }

  17. })

  18. })

  19. },

把当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径。

使用微信提供的canvasToTempFilePath

 
  1. savePic () {

  2. let that = this;

  3. let offset_left = (this.data.windowWidth - 303) / 2

  4. console.log('savePic')

  5. wx.canvasToTempFilePath({

  6. x: offset_left,

  7. y: 0,

  8. width: 303,

  9. height: 398,

  10. canvasId: 'canvas',

  11. success: function (res) {

  12. console.log(res.tempFilePath)

  13. },

  14. fail (e) {

  15. console.log(e)

  16. }

  17. }, this)

  18. }

预览/保存图片

上一步我们已经把把canvas的内容保存生成图片了。继续来其实有两种方案。

  1. saveImageToPhotosAlbum, 把该文件保存到相册里去
  2. previewImage,直接预览该图片

其实两种方法都行,可以根据实际的需求做相应的选择。

我在选择的时候采用了 previewImage,因为这个不需要用户授权, 用户可以直接把这个图片发送给朋友 并不需要保存图片。如果想要保存图片的话,在预览的时候 也可以保存。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值