背景
小游戏需要在ios端做充值切支付,通过二维码形式,游戏端接收一个base64,sdk端则需要将支付地址转成二维码然后再编码成base64。
问题
发现pc端,安卓端,二维码都能正常展示,但是唯独ios端,二维码是空白的;
排查
- base64过长被截断?,通过写死一个较长的二维码发现是能展示的;
- 压缩质量,减少图片大小? 本质上还是减短base64的长度;
解决方法
上述的几点排查还是没能解决问题,查看小程序社区也没有找到比较合理的解决方法。因为是sdk端处理,所以也没办法修改调用方的实际图片显示方式。
后来翻看了一下生成base64的代码;
发现使用 canvas.toDataURL() 将画布转换为 base64 编码的图像数据时,可能会出现图像数据不完整的情况。这种情况在 iOS 设备上尤为常见,原因可能包括以下几个方面:
-
绘图操作未完成:
- 在调用
canvas.toDataURL()时,可能绘图操作尚未完全完成。即使绘图操作已经调用了drawImage或fillRect等方法,但这些操作可能还在后台进行。
- 在调用
-
浏览器渲染延迟:
- 浏览器在处理画布的渲染时,可能存在一定的延迟。特别是在资源加载(如图像)或复杂的绘图操作时,浏览器需要更多时间来完成渲染。
-
异步操作:
- 某些绘图操作是异步的,例如
image.onload事件。在这些情况下,即使代码逻辑上已经调用了toDataURL,但实际的绘图操作可能还没有完成。
- 某些绘图操作是异步的,例如
QRCode.prototype.makeImage = function (callback) {
var _oContext;
var canvas = window["wx"].createCanvas();
if (!canvas) {
console.error("Failed to create canvas");
return;
}
var _htOption = this._htOption;
var oQRCode = this._oQRCode;
//图片大小调整和二维码图片大小一致
canvas.width = _htOption.width;
canvas.height = _htOption.height;
var _oContext = canvas.getContext("2d");
if (!_oContext) {
console.error("Failed to get 2D context");
return;
}
var nCount = oQRCode.getModuleCount();
var nWidth = _htOption.width / nCount;
var nHeight = _htOption.height / nCount;
var nRoundedWidth = Math.round(nWidth);
var nRoundedHeight = Math.round(nHeight);
_oContext.clearRect(0, 0, nWidth, nHeight);
if (_htOption.image && _htOption.image != "") {
var image = new Image();
image.src = _htOption.image;
image.onload = function () {
_oContext.drawImage(image, 0, 0, _htOption.width, _htOption.height);
drawQRCode();
};
}
for (var row = 0; row < nCount; row++) {
for (var col = 0; col < nCount; col++) {
var bIsDark = oQRCode.isDark(row, col);
var nLeft = col * nWidth;
var nTop = row * nHeight;
_oContext.strokeStyle = bIsDark
? _htOption.colorDark
: _htOption.colorLight;
_oContext.fillStyle = bIsDark
? _htOption.colorDark
: _htOption.colorLight;
_oContext.fillRect(nLeft, nTop, nWidth, nHeight);
_oContext.strokeRect(
Math.floor(nLeft) + 0.5,
Math.floor(nTop) + 0.5,
nRoundedWidth,
nRoundedHeight
);
_oContext.strokeRect(
Math.ceil(nLeft) - 0.5,
Math.ceil(nTop) - 0.5,
nRoundedWidth,
nRoundedHeight
);
}
}
var base64 = canvas.toDataURL();
if (callback != null && typeof callback == "function") {
callback(base64);
}
};
考虑问题会不会是在 canvas 画布上,游戏本身就是渲染的canvas,在画二维码canvas的时候出现了问题,于是先将画布周围清理一下:
_oContext.clearRect(0, 0, nWidth, nHeight);
然后再添加定时器,等二维码的canvas完全画好再转换base64:
setTimeout(function () {
var base64 = canvas.toDataURL();
if (callback != null && typeof callback == "function") {
callback(base64);
}
}, 1000);
最后回到ios真机测试,发现已经成功渲染出来了;
结论
虽然定位到了问题通过延迟渲染解决,但还有两点存在怀疑:
- 是否是 ios 高性能模式影响了canvas的渲染;
- ios确实存在截断base64的现象,打印长度最长是490,但实际不影响渲染;