小程序小游戏ios端url生成二维码转base64无法显示?

278 阅读3分钟

背景

小游戏需要在ios端做充值切支付,通过二维码形式,游戏端接收一个base64,sdk端则需要将支付地址转成二维码然后再编码成base64。

问题

发现pc端,安卓端,二维码都能正常展示,但是唯独ios端,二维码是空白的;

排查

  • base64过长被截断?,通过写死一个较长的二维码发现是能展示的;
  • 压缩质量,减少图片大小? 本质上还是减短base64的长度;

解决方法

上述的几点排查还是没能解决问题,查看小程序社区也没有找到比较合理的解决方法。因为是sdk端处理,所以也没办法修改调用方的实际图片显示方式。

后来翻看了一下生成base64的代码;

发现使用 canvas.toDataURL() 将画布转换为 base64 编码的图像数据时,可能会出现图像数据不完整的情况。这种情况在 iOS 设备上尤为常见,原因可能包括以下几个方面:

  1. 绘图操作未完成

    • 在调用 canvas.toDataURL() 时,可能绘图操作尚未完全完成。即使绘图操作已经调用了 drawImage 或 fillRect 等方法,但这些操作可能还在后台进行。
  2. 浏览器渲染延迟

    • 浏览器在处理画布的渲染时,可能存在一定的延迟。特别是在资源加载(如图像)或复杂的绘图操作时,浏览器需要更多时间来完成渲染。
  3. 异步操作

    • 某些绘图操作是异步的,例如 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,但实际不影响渲染;