Skip to content

小程序 - 刻度尺组件的实现原理 #44

Description

@cobish

使用

需求里有一个需要拖动选择年龄的刻度尺,但小程序里没有自带该组件,所以找到了一个 「wx-scale」 的修改一下。

使用效果如下:

原理

刚开始自己也没有思路来实现,于是找到源码简单了解一下实现,发现其实也不难。

实现如下:

简单讲一下,通过 canvas 画出卡尺,再导出成一张图片,此时 canvas 处于隐藏的状态。然后监听 scroll-view 的滚动事件,并计算当前刻度。

其中有几个需要注意的地方:

画 canvas

官方文档里的这行代码是画不出东西的:

const ctx = wx.createCanvasContext('myCanvas');

需要将第二个参数带上才可:

const ctx = wx.createCanvasContext('myCanvas', this);

小程序里 canvas 的单位是 px,所以不同屏幕看到的刻度尺大小是一致的。

如果要考虑适配不同屏幕,需要使用 rpx,可以根据屏幕宽度来将 rpx 转换成 px。

1rpx = 屏幕宽度 / 750

具体可以参考「小程序坑-canvas」。

导出图片

由于小程序基础库 2.4.0 以下 scroll-view 不支持嵌套 canvas,所以需要将 canvas 导出成图片放入 scroll-view,并将 canvas 隐藏。

所以基础库在 2.4.0 以上不用这么麻烦,直接 scroll-view 里嵌套 canvas 即可。

需要注意的是,官方文档里的 draw 完成后会有回调函数,但并不那么有效,所以保险点最好加上一个定时器。

ctx.draw(true, setTimeout(() => {
    // 导出图片
}, 300);

由于 canvas 属于原生组件,层级是最高的,源码里使用的 index: -1 在手机上还是能看得到 canvas,所以我将 canvas 的隐藏样式修改为:

.canvas {
    position: fixed;
    left: 100%;
    box-sizing: border-box;
}

滚动事件

滚动事件会有一个回传给父组件的值(当前刻度),用于父组件的数据更新,即:

this.triggerEvent('value', {
    value: resultNum
});

因为滚动事件是频繁调用的,如果父组件用于 setData 渲染的话,那会一直 setData,造成性能上的卡顿,所以最好加上函数节流。

// 函数节流
const delay = 250;
const curr = +new Date();
if (!rul.last || curr - rul.last > delay) {
    this.triggerEvent('value', {
        value: resultNum
    });
    rul.last = curr;
}

同时,在滚动事件结束后,会计算卡尺的值,从而进行整数的对齐,所以用到函数防抖,只在滚动结束的最后一次触发,而不是在滚动时频繁触发。

// 函数防抖
clearTimeout(rul.Timer);
rul.Timer = setTimeout(() => {
    this.setData({
        // ...
        centerNum: redNum,
        active: resultNum
    });

    this.triggerEvent('value', {
        value: resultNum
    });
}, 100);

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions