blogs icon indicating copy to clipboard operation
blogs copied to clipboard

微信小程序 drawImage 问题

Open forever-z-133 opened this issue 7 years ago • 0 comments

最近公司项目使用小程序做序列帧动画,大概有 116 张图,共 7.4M。

比较闲的日子里实验了一番,主要有以下几种方法,

  1. css background-image + animation
  2. css background-position + animation
  3. js background-image
  4. js background-position
  5. js img src
  6. canvas drawImage

结果当然是 canvas 性能最优咯,不会出现掉帧和卡屏的情况,其中最不推荐第一种

所以这次项目也就准备尝试下微信小程序的 canvas 会不会有别样的风味。 基本上和 html 的 canvas 区别不大,方法名略有不同, canvas.getContext('2d') 等于 wx.createCanvasContext(canvas)。 再就是需要一个 draw() 方法才会绘制(经常容易忘记)。 至于 wx.createCanvasContext 放在 onReady 还是 onShow,以及重复新建等问题上,由于项目紧凑,手里机型太少,没试太多


接着就开始了填坑之路:

一. Image 对象问题,只需直接使用图片路径

官方案例用的是 wx.chooseImage 返回缓存文件,但我们的是116张图呀。 真是的,也不多给几个案例,还得自己来尝试。

在 HTML 中如果想 drawImage 那就需要一个 Image 对象, 需要先 new Image() 或者获取到 DOM 中的 <img>,那么小程序该怎么办呢。

我略一沉凝,准备试它一试,直接使用了图片绝对路径,

ctx = ctx ? ctx : wx.createCanvasContext('imgs');
url = 'https://sum.kdcer.com/test/sw_shake/0/0 (1).jpg';
ctx.drawImage(url, 0, 0, 300, 500);  // 直接使用图片路径
ctx.darw()

调试器上正常(后面证明,预览时也是正常的)。url 为相对路径也是可以的。

但,这个时候预加载就是个问题,图片加载比 draw 慢时就很尴尬了。 只能在 wxml 中去 bindload 或者 wx.downloadFile 来进行是否加载完成的处理咯。

2. 图片路径不能有特殊符号

上面的情况虽然调试器中可看,但手机预览时图片却没有绘制(其他点线图形绘制了), 唔,不妙。然后去论坛博客寻找了番,不禁感叹资料可真少啊。相当不妙。

换个方向,我再试试 wx.downloadFile 这种方法,用缓存文件该是符合官方案例的吧。

wx.downloadFile({
  url: url,
  success: function (res) {
    ctx.drawImage(res.tempFilePath, 0, 0, 300, 500);
    ctx.draw();
  }
})

还好能看到些反馈,算是找到了错误原因。 返回给我的 res.tempFilePath 是个 .htm 结尾的文件,并报出 http 400(请求无效)。

我怀疑问题出在了文件本身,于是我改了下文件名,由 0 (1).jpg 改为 1.jpg,就能正常访问了。 再后来进行了一些实验,暂时还只发现了 空格+括号 这一种命名会失败。

返回去再试一次, drawImage 直接使用命名正确的图片绝对路径其实也是可以的。

比较坑的是,downloadFile 不能下载相对路径的图片, 这让我想优化把一部分图片放进小程序变得无比麻烦。 (其实 2M 资源放进去小程序就会变得非常卡,不推荐)

3. downloadFile 文件数限制

官方表示,downloadFile 最大并发限制为 10 个, 意味着直接 for 个 116 下是会报错的。 因此需要换用为递归的方式去预加载图片。

我写的递归不见得都适用,就不放出来了,应该没什么难点的。 (推荐先用 .html 写通递归,不然小程序编辑器死循环了很扎心)

4. downloadFile 合法域名的配置

初次开发时出现了小程序仅有打开了调试工具才能正常运行(预览时未下载图片)的情况, 后来经过同事点拨,原来还要设置 downloadFile 的合法域名。

合法域名每个月只能修改 5 次的限制应该不是什么大问题。

5. requestAnimationFrame 问题

为了更好的动画优化,当然少不了 requestAnimationFrame 的存在。 然而,安卓机的小程序是有的,苹果机小程序却根本没有这个方法。

好在我们可以写段回退兼容:

if (typeof requestAnimationFrame == 'undefined') {
  var lastTime = 0;
  var requestAnimationFrame = function (callback) {
    var currTime = new Date().getTime();
    var timeToCall = Math.max(0, 16 - (currTime - lastTime));
    var id = setTimeout(function () { callback(currTime + timeToCall); }, timeToCall);
    lastTime = currTime + timeToCall;
    return id;
  };
}
if (typeof cancelAnimationFrame == 'undefined') {
  var cancelAnimationFrame = function (id) {
    clearTimeout(id);
  };
}

6. fps 性能问题

小程序一直吹嘘着接近原生的流畅体验,但这次帧动画的项目中显然打脸了。 html 版的 canvas 每 15ms 绘制一次都是小 case, 但小程序则需要 50ms 以上的间隔。否则会出现间断性白屏。

60fps 和 20fps 虽然在 html 中有很大差距, 但在小程序中 20fps 并没有太影响用户的浏览体验。 毕竟 js 的运算和 webview 的通信本身就不是多快的一件事。 如果单单只考虑 webviewhtml 不带 JS 一起玩耍的话那当然小程序会流畅些。

7. canvas 在小程序层叠上下文层级非常高

canvas / video / map 在小程序中的 z 轴层级非常高,甚至能盖过调试工具。 所以我们想在他们上面再叠一些元素就只能靠 cover-view 了。

但是,cover-view 只支持基本的定位、布局、文本样式, 不支持设置单边的 borderopacitybackground-image 等。 我觉得不能叠图这个问题还是有些麻烦的,至少操作起来是这样。 而且,cover-view 暂不支持 css 动画。 项目中还遇到过 cover-view 高度丢失等问题,玩耍得实在不畅快。

稍提一下,canvas 绑定的事件自带不冒泡,知道就行,影响不大。

8. 小程序的 drawImage 不支持9个参数的传参

不过有人去改了编辑器源码,见 we_flappybird, 测了可行,但并不推荐,编辑器下次更新又要改。


总的来说,填坑的路是比较烦人的, 后一个问题解决了又开始想,是不是前一个问题其实本来是对的,然后又回去重来一遍, 最后的最后,来来回回,才能彻底填平这个坑。

forever-z-133 avatar Aug 20 '17 10:08 forever-z-133