Cocos2.0微信小游戏截图

Cocos Creator更新到2.0.4了,官方给出了截图的教程
点击访问

文档中说:

注意,微信小游戏中由于不支持 createImageData.

经我测试是支持的.

大体思路是:
  1. 拿到摄像机Camera
  2. 新建RenderTexture
  3. 设置Camera.targetTexture为新建的RenderTexture,手动执行Camera.render()
  4. 从RenderTexture中取得texture
  5. 渲染到微信的canvas中
  6. 调用微信API使用图片(canvas.toTempFilePathSync等等)

Camera是2.0版本大改了的一个组件,新的用法很方便。未设置targetTexture时,Camera将默认渲染到屏幕,根据摄像机的depth决定多个相机的渲染顺序。

经我使用,单独给需要截图的节点放一个相机,并指定单独的group,使用时再激活相机,这样截图有效,否则就是一片空白。

重点在这

直接调用微信的主域

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
39
40
41
42
43
44

但是一旦接入微信的开放数据域,主域的截图功能就被屏蔽了。

表现为```canvas.toTempFilePathSync```返回的路径只是一个id,不是tmp的图片地址.

解决方法是:
1. ```wx.createCanvas()```新建离屏画布
2. 然后将需要截图的节点使用RenderTexture生成texture。
3. 渲染texture到新的canvas
4. 调用canvas.toTempFilePathSync即可,离屏画布不会被限制.


渲染texture到新的canvas是个很麻烦的过程,1.9.x版本的cocos可以使用

canvas.getContext('2d').drawImage(texture().getHtmlElementObj());

直接绘图,但是要强制使用canvas渲染方式。
2.0版本canvas的方式已被舍弃,这种方法无法再使用。

官房给出了复制像素数据的方法

```javascript
...
// 这样我们就能从 RenderTexture 中获取到数据了
let data = texture.readPixels();

// 接下来就可以对这些数据进行操作了
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
canvas.width = texture.width;
canvas.height = texture.height;

let rowBytes = width * 4;
for (let row = 0; row < height; row++) {
let srow = height - 1 - row;
let imageData = ctx.createImageData(width, 1);
let start = srow*width*4;
for (let i = 0; i < rowBytes; i++) {
imageData.data[i] = data[start+i];
}

ctx.putImageData(imageData, 0, row);
}
...

这种方式效率有些低,微信真机测试耗时1s+++,但苦于找不到别的方式。

我把代码优化了下,省出了0.2s左右。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
let dataBytes = canvas.width * canvas.height * 4;//全部数据长度
let rowBytes = canvas.width * 4;//一行数据长度
let imageData = ctx.createImageData(canvas.width, canvas.height);
let rowNum, extra = 0;
for (let dataIndex = 0; dataIndex < dataBytes; dataIndex++) {
//原始纹理行数,原始纹理仅上下颠倒,这里做适配
rowNum = height - Math.ceil(dataIndex / rowBytes);
//余数
extra = dataIndex % rowBytes;
imageData.data[dataIndex] = data[rowNum * width * 4 + extra];
}
ctx.putImageData(imageData, 0, 0);
...
只创建一次imageData,for循环也不嵌套,等找到更好的方式,再来补充。