微信小程序问题集锦

#0 Canvas 绘制不生效

原因有可能是 canvas context 的创建方式不对。

通过 wx.createCanvasContext(canvasId, this) 创建 canvas 绘图上下文时,第后个参数是指自定义组件的实例。如果 canvas 位于自定义组件中,这个参数是必传的,否则绘制不生效,界面没显示任何东西。

来看官方文档关于 wx.createCanvasContext 的介绍:

自定义组件实例  this ,表示在这个自定义组件下查找拥有 canvas-id 的   ,如果省略,则不在任何自定义组件内查找

components/paint.json

{
  "component": true
}

components/paint.js

Component({
  ready: function () {
    const ctx = wx.createCanvasContext("myCanvas", this /* 它很重要 */);

    ctx.setFontSize(20);
    ctx.fillText("Hello", 20, 20);
    ctx.fillText("MINA", 100, 100);

    ctx.draw();
  },
});

#1 Canvas API 与 DOM 中不一样

部分 API 与 DOM 中 canvas 有差异,体现在方法名与参数上。

比如 :

context.createRadialGradient(x0, y0, r0, x1, y1, r1);,

微信中我能找到对应的,至少从名称上来看,应该是 context.createCircularGradient(x,y,r)

该 API 创建放射状的渐变,渐变由两个圓形边界确定。微信小程序中提供的对应 API 少了一个圆,一如文档中所说,起点从圆心开始。可以理解为缺少的那组参数所确定的圆,其圆心与外围圆心一致,只是半径为 0。

像这种不与我们所熟知的 API 靠拢,进而设计出相近参数不同的 API,不太明白其用意和初衷。只是我们在写码时需要勤看文档,否则会困绕很久。

Component({
  ready: function () {
    const ctx = wx.createCanvasContext("myCanvas");

    // const grd = ctx.createRadialGradient(0, 0, 50, 75, 50, 50);
    const grd = ctx.createCircularGradient(75, 50, 50);

    grd.addColorStop(0, "red");
    grd.addColorStop(1, "white");

    // Fill with gradient
    ctx.setFillStyle(grd);
    ctx.fillRect(10, 10, 150, 80);
    ctx.draw();
  },
});

#2 真机上图片绘制不生效

与 DOM 中 canvas 的图片绘制不同,小程序提供于向画布绘制图片的 API canvasContext.drawImage 其所需的图片资源为图片的地址。

特别地,如果发现绘制后没展示,多半是因为绘制的图片来自远端,需要使用先下载到本地。这其实跟原生 DOM canvas 绘制网络图片时一样,先 new Image() 等加载完后在 img.onload 回调中再绘制。

wx.downloadFile({
  url: "https://example.com/foo.jpg",
  success: function (res) {
    const ctx = wx.createCanvasContext("myCanvas");
    ctx.drawImage(res.tempFilePath, 0, 0, 150, 100);
    ctx.draw();
  },
});

因为小程序内对外部资源的使用的严格限制,这里使用 wx.downloadFile API 下载的图片地址需要添加到 downloadFile 合法域名 中。

#3 cover-view 中文本样式的兼容性问题

<live-player> 这种运行时是 native 元素的组件,其上要盖东西有严格限制,可操作的空间不多,样式操控起来也捉襟见肘。

虽然官方文档说其只能嵌套 <cover-view><cover-image>,实测放入文本也是可以的。真机上发现视频上盖的文本在 iOS 与 安卓端样式上有差异。比如在视频上盖一个 「静音」标签。

mute_badge.wxml

<live-player>
  <cover-view class="mute-badge"> 静音模式 </cover-view>
</live-player>

mute_badge.wxss

.mute-badge {
    color: #fff;
    background: rgba(0, 0, 0, 0.5);
    display: inline-block;
    vertical-align: top;
+    padding: 0px 4px;
    font-size: 24rpx;
    line-height: 48rpx;
    height: 48rpx;
    margin: 20rpx;
}

因为想要文本与背景间有留白,所以左右给了 4px 间距。不给的话默认是紧贴着的。模拟器上没问题,真机测试问题便来了。

iOS 左右依然无留白,安卓正常。审查元素后可发现,iOS 中 padding 部分是没有背景色的!

image

这决定了如果想要文字左右有边距,得靠东西来填充,靠看不见的字符。当然空格键打的空格是不会起作用的,会被忽略掉,好在 \b 是可以起到填充作用的。所以文案前后加 \b

mute_badge.js

data: {
  text: `\b\b 静音模式 \b\b`;
}

mute_badge.wxml

<live-player>
  <cover-view class="mute-badge">  </cover-view>
</live-player>

这样 iOS 上是生效了。但安卓上出新问题了。因为 \b 在安卓上的表现是虽然会占空间,但不会撑开容器,所以容器还是文案那么宽,因为 \b 的加入,原来的文案便显示不下了,被挤掉了一部分。

image

如果想尝试文本外再包一层东西来写样式增加留白的话,会得到以下的结果:

image

既然难两全,那就分开处理喽。

data:{
txt:’\b\b 静音模式 \b\b
},
ready:function() {
        try {
            const systemInfo = wx.getSystemInfoSync();
            if (systemInfo.platform === 'android') {
                this.setData({
                    txt: '静音模式',
                });
            }
        } catch (error) {
            //
        }
    }