Fork me on GitHub

小程序隐藏canvas的坑

需求

今天收到一个需求,内容是在微信小程序点击按钮后,弹出一个动态生成的图片,并且保存在系统相册里面。
在实现过程中遇到一些坑,记录一下。

Canvas动态生成图片

第一步先在普通页面实现动态生成图片的功能,先不考虑隐藏等功能。
代码如下:

wxml:

1
2
<canvas canvas-id="shareCanvas" style="width:{{canvasWidth}}px;height:{{canvasWidth*1.5}}px">
</canvas>

JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var canvas = wx.createCanvasContext('shareCanvas')

canvas.drawImage('../../image/share-img.png',
0,
0,
this.data.canvasWidth,
this.data.canvasWidth * 1.5)

canvas.setTextAlign('center')
canvas.setFillStyle('#ffffff')
canvas.setFontSize(12)
canvas.fillText("生成的文字", this.data.canvasWidth * 0.5, this.data.canvasWidth * 1.26)

canvas.stroke();
canvas.draw()

wx.canvasToTempFilePath({
canvasId: 'shareCanvas',
success: function(res) {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: function(res) {
console.log(res)
},
fail: function(err) {
console.log(err)
}
})
}
})

以上js代码就是获取canvas控件,填充一张图做背景,然后生成文字。
然后canvas生成图片,在成功回调方法里面保存到相册里面。
以上代码在正常页面内执行没问题。

Canvas在自定义组件内

完成了第一步后,考虑到这个按钮可能在不同的页面。就单独把上面的功能封装成一个组件。
在普通页面引入组件后,报错"canvasToTempFilePath: fail canvas is empty",
查阅文档后,发现createCanvasContext`canvasToTempFilePathsaveImageToPhotosAlbum这三个方法需要传this这个参数。JS`:

1
2
3
4
5
6
7
8
9
10
var canvas = wx.createCanvasContext('shareCanvas',this)
...
wx.canvasToTempFilePath({
canvasId: 'shareCanvas',
success: function(res) {
wx.saveImageToPhotosAlbum({
...
},this)
}
},this)

改成这样就可以了。

Canvas隐藏

现在来解决隐藏canvas的问题。在需要的时候才会显示,类似于弹框,后面有个蒙版的样式。
wxml:

1
2
3
4
5
6
7
8
<view class="root" style="display:{{show}}">

<view class="modal-dialog">
<canvas canvas-id="shareCanvas" style="width:{{canvasWidth}}px;height:{{canvasWidth*1.5}}px"></canvas>
</view>

<view class="close" bindtap='close'></view>
</view>

这样我们控制root就可以控制整个组件的隐藏和显示了。
那么问题来了,保存的图片都是空白的。获取不了canvas的内容。如果在显示情况下没问题,当root隐藏后就不行了。
在网上基本查到这两种解决方法:

方法一:这种方法模拟器可以,但是在真机上不行,所以这个方法放弃了。

1
2
3
<view style='width:0px;height:0px;overflow:hidden;'>
<canvas canvas-id='canone'></canvas>
</view>

方法二: 这种主要是把canvas放到无限远的位置,并且设置页面不可滚动。因为我这个是组件,所以不一定哪个页面要用,觉得比较麻烦,放弃。

1
2
3
4
5
<view style="position:fixed;top:9999999999999rpx;">
<canvas style="width:400px;height:400px;background:#fff;"></canvas>
</view>

"disableScroll": true

那么有没有更好的解决方法呢? 我们分析一下,当root显示的情况下canvas这个没问题,如果root先隐藏在显示,canvas就会出现问题了。那么我们能不能监听root隐藏和显示方法的事件呢?当显示完成后在去使用canvas呢?经过一翻搜索后,好像不能监听隐藏显示。
换个思路:不监听root的显示和隐藏,直接用setTimeout试试呢?

1
2
3
4
5
6
7
8
9
10
11
12
setTimeout(function() {
var canvas = wx.createCanvasContext('shareCanvas',this)
...
wx.canvasToTempFilePath({
canvasId: 'shareCanvas',
success: function(res) {
wx.saveImageToPhotosAlbum({
...
},this)
}
},this)
},100)

延迟0.1秒后在操作canvas,😆果然成功了,该死的小程序。

Canvas第一次空白

测试几次后发现第一次获取的图片还是空白的,而之后就好了。经过研究之后在canvas.draw()回调方法里面也是延迟0.1秒后就没问题了。

1
2
3
4
5
canvas.draw(false, setTimeout(function() {
wx.canvasToTempFilePath({
...
}, this)
}, 100))

完整代码

JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
make: function () {

this.setData({
show: ''
})

var that = this;

setTimeout(function () {
var canvas = wx.createCanvasContext('shareCanvas', that)

canvas.drawImage('../../image/share-img.png',
0,
0,
that.data.canvasWidth,
that.data.canvasWidth * 1.5)

canvas.setTextAlign('center')
canvas.setFillStyle('#ffffff')
canvas.setFontSize(12)
canvas.fillText("动态文字", that.data.canvasWidth * 0.5, that.data.canvasWidth * 1.26)

canvas.stroke();
canvas.draw(false, setTimeout(function () {
wx.canvasToTempFilePath({
canvasId: 'shareCanvas',
success: function (res) {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: function (res) {
console.log(res)
}
}, that)
}
}, that)
}, 100))
}, 100)
}

wxml:

1
2
3
4
5
<view class="root" style="display:{{show}}">
<view class="modal-dialog">
<canvas canvas-id="shareCanvas" style="width:{{canvasWidth}}px;height:{{canvasWidth*1.5}}px"></canvas>
</view>
</view>
------ 本文结束------
0%