不好意思,没有很明白是什么意思,能详细说下么,我这里给renderTexture是设置的this.node的高宽呀。
我之前的表述不是问题所在,实际问题出现在this.node.width 和 this.node.height这两位数是浮点数值
texture.readPixels()获取到的像素数组元素是非常多的。
let start = srowwidth4;
for (let i = 0; i < rowBytes; i++) {
imageData.data[i] = data[start+i];
//这里由于浮点数的下标所以这个元素是undefined的,双重for循环下imageData.data的值一会变成undefined,一会变成0,已造成运算压力。
}
建议向下取整:let start = Math.floor(srowwidth4);
改过来了,但是并未改善,这个奇怪的地方是部分高端的安卓机会卡,其他的手机其实都很快。
感觉是这里绘图导致的,我尝试直接截取canvas不绘制就一点都不卡。
我一会再安卓上测试看下
多谢!截的图片越大卡顿越明显,我这边已知会卡的手机有 一加6,魅蓝Note6,小米5,还有一个华为的忘记什么型号了。
我在小米 MIX 2S上测试没有出现问题,在安卓高版本和低版本的机型截图耗时都差不多
不是安卓版本的问题,是手机类型,很明显的卡顿好几秒的那种仅仅只是手机不同,大佬可以试试我说的那几款手机。
我也遇到过同样问题,以下是直接的部分分析见解(主要针对FB Instant Game)
耗时分析
截图耗时主要有几个地方(微信好像不用第二、三步的样子)
- 摄像机渲染耗时,视乎节点内容多少
- 将截图内容写入到CanvasRenderingContext2D的过程,视乎写入数据到Canvas2d时的方法不同,耗时也不同
-
canvas.toDataUrl 方法
- 部分机型可以在这里耗上1200ms,因为js为单线程,所谓异步操作也只是插入到队列中比较前的位置,可以立即执行,但是如果上一个待执行的任务要占用很久,比如1200ms,那么这期间所有内容都会卡死,比如UI的Loading菊花不会转了,音乐暂停了之类
写入图像到 CanvasRenderingContext2D 的不同方法
其中,第2步,参考两种对比写法
// 接下来就可以对这些数据进行操作了
// 创建一个新的画布
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
// 将刚刚的渲染数据渲染到新的画布上
// 图片用32bit位表示一个像素,所以一行有width个像素,一共是width*32/8bit = width*4字节
let rowBytes = width * 4;
let s3 = new Date().getTime();
// 依次读取图片里的每行数据,放入到ctx中。
for (let row = 0; row < height; row++) {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DEMO 写法(高性能,兼容性好)
// 设置开始行为最下面那行
// 因为 RenderTexture 得到的纹理是上下翻转的,所以需要旋转回来
// 在这里几位从最下面的行开始读取图片数据
let srow = height - 1 - row;
// 从图片的所有数据中,获取一行的图片字节数据
let oneLineImageData = new Uint8ClampedArray(textureData.buffer, Math.floor(srow * width * 4), rowBytes);
// 设置图片数据对象
// 设置一个一行的图片,所以就是 width, 1
let imageData = new ImageData(oneLineImageData, width, 1);
ctx.putImageData(imageData, 0, row);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// // 官网文档 写法(在iPhone5机器上 ctx.createImageData 得到的对象是不能直接通过读取 .data 属性赋值的, 因为该属性为只读,所以是不能写入到数据导致没有截图数据)
// // 设置开始行为最下面那行
// // 因为 RenderTexture 得到的纹理是上下翻转的,所以需要旋转回来
// // 在这里几位从最下面的行开始读取图片数据
// let srow = height - 1 - row;
// // 设置图片数据对象
// // 设置一个一行的图片,所以就是 width, 1
// let imageData = ctx.createImageData(width, 1);
// // 从最下面那行的第一个字节开始读
// // 下标是从0开始, srow* width * 4 则刚刚好为最下面那行的第一个字节
// let start = srow * width * 4;
// for (let i = 0; i < rowBytes; i++) {
// imageData.data[i] = data[start + i];
// }
// ctx.putImageData(imageData, 0, row);
// if (row == 0) {
// console.log(`imageData:`);
// console.log(imageData);
// }
}
if (CC_DEBUG) {
cc.log(`生成截图耗时: ${new Date().getTime() - s3} ms`);
}
同一个手机
- 用 Unit8ClampedArray 最快可以100ms
- 用 createImageData 方法需要1200ms
没仔细测试,只是简单对比了手头上的几台手机,其他手机情况未知
Canvas.toDataUrl 方法
这个是MDN统一方法,没解的样子,除非我们自己重写一遍,不然就是完全阻塞,想到一个绕一点的方法,截图数据分几次读取,比如截好图之后,分多次去读取数据,每次读取数据耗时控制在8ms左右,一帧还剩下8ms交给Loading菊花去转,或者音乐播放之类
感谢大佬,刚刚用大佬修改了下,速度的确快了不少,还没上真机测试等会看看能不能解决部分手机卡的问题。
刚刚上真机试了试,可惜还是卡顿,但是已经快了大约40%了。同时对比了调整了Canvas.toDataUrl的质量到0.3和默认对真机的影响,对卡的那几种手机影响几乎感觉不到。感觉不是这个方向的问题,怕是手机渲染硬件导致的问题
补充一点,一开始设置截图大小的时候就要向下取整截图尺寸,这是因为截图尺寸的宽高有可能是浮点数,那么读出来的内容就可能出现越界问题,即便后面 start 那里向下取整调整回来,也会有问题,我也是找了好久才知道,Anyway
using the following code, you may see the screenshot on iPhone 5 like this:
// Getting view rect size.
let visibleSize = cc.view.getVisibleSize();
let width =visibleSize.width;
let height = visibleSize.height;
// 新建一个 RenderTexture,并且设置 camera 的 targetTexture 为新建的 RenderTexture,这样 camera 的内容将会渲染到新建的 RenderTexture 中。
let renderTexture = new cc.RenderTexture();
// 初始化纹理大小,如果截图内容中不包含 Mask 组件,可以不用传递第三个参数
renderTexture.initWithSize(width, height, cc.game._renderContext.STENCIL_INDEX8);
this.camera.targetTexture = renderTexture;
after using Math.floor , everything is fine:
// Getting view rect size.
let visibleSize = cc.view.getVisibleSize();
let width = Math.floor(visibleSize.width);
let height = Math.floor(visibleSize.height);
// 新建一个 RenderTexture,并且设置 camera 的 targetTexture 为新建的 RenderTexture,这样 camera 的内容将会渲染到新建的 RenderTexture 中。
let renderTexture = new cc.RenderTexture();
// 初始化纹理大小,如果截图内容中不包含 Mask 组件,可以不用传递第三个参数
renderTexture.initWithSize(width, height, cc.game._renderContext.STENCIL_INDEX8);
this.camera.targetTexture = renderTexture;
剩下的应该是 Canvas.toDataUrl 那块的卡顿了,你可以测试一下那里的耗时,如果真的是这里耗时了,你可以参考下我上面说到的应对思路,就是比较绕。。。
好的多谢!
希望官方能关注下这个问题,毕竟这种常见需求非常多见,截全屏尚且这么卡了,截长图什么的都不敢想会多卡
没有报错吗?这边运行到微信上 报
Failed to execute ‘createImageData’ on ‘CanvasRenderingContext2D’: The source width is 0.
Error: Failed to execute ‘createImageData’ on ‘CanvasRenderingContext2D’: The source width is 0.
微信端的话调用微信那边的接口吧https://developers.weixin.qq.com/minigame/dev/api/Canvas.toTempFilePath.html
< 1分钟
大佬,可不可以研究下这个问题https://forum.cocos.com/t/cocoscreator2-0-9/75313/4
大佬 3.6版本 Unit8ClampedArray 生成的时候无法生成一行的数据 得到的是buffer完整的数据 这个有可能是什么原因?