飞书小组件
飞书小组件 是飞书独创的轻量级功能块,它被定义为一个集渲染、交互、数据为一体的信息单元载体,实现信息在飞书套件内的顺畅流转与消费。在飞书小组件上可以使用 VChart 进行图表绘制。
使用方式
Tip: 目前 VChart(@visactor/vchart)还未内置进飞书小组件,所以需要你在飞书小组件项目中手动引入。
如何获取 VChart
目前小组件上需要 VChart 的 umd 打包产物,你可以通过如下渠道获取:
- 直接仓库中获取 packages/block-vchart/block/vchart/index.js ,每次发包我们都会进行更新,这是我们专门为飞书小组件环境构建的,为了尽可能降低大小,该包只包含了飞书小组件的渲染环境。
- 你也可以从如下免费的 CDN 中获取,这是包含了所有渲染环境以及所有功能的 vchart 构建产物
<!-- unpkg -->
<script src="https://unpkg.com/@visactor/vchart/build/index.min.js"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@visactor/vchart/build/index.min.js"></script>
暂不支持的功能
飞书小组件现阶段由于序列化问题,还不支持在 setData 传递复杂对象及函数,只支持可序列化的数据。所以你无法在传递给 setData 的图表 spec 中为属性配置回调函数。目前可以通过如下两个方法来解决这个问题:
- 不要通过
setData传递 spec,或者在 VChart 实例创建之后,通过vchart.updateSpec(spec)方法来更新图表,这个时候将带函数的 spec 传入 - 升级 vchart 升级到 1.7.0+,使用我们提供的注册自定义函数功能来实现,具体的使用方式见下文: 注册函数
Demo 示例
我们提供了一个完整的 demo 项目,可供开发时参考:https://github.com/VisActor/VChart/tree/main/packages/block-vchart
前提:请了解下飞书小组件的官方教程文档,大致了解下如何开发飞书小组件。
下面我们从 js、ttml、ttss 三部分介绍下如何在飞书小组件上使用 VChart。
index.ttml
在 index.ttml 去声明如下代码,作为使用图表能力的 canvas 载体,这里的属性有 class,id,canvas-id。
class:样式的类名,会和即将在index.ttss中设置的类自动链接使用id:用于在下方的index.js中通过 query 取到 domcanvas-id:一个特殊标志符,是为了让编译服务进行元素区分
另外因为 VChart 默认使用 html 渲染 tooltip,而小组件环境不支持 html,所以我们需要使用 canvas 版本的 tooltip,同时为了更好的交互性能,我们需要使用另一层 canvas 元素来作为 tooltip 绘制的载体。需要注意 canvas 声明的顺序(在 draw canvas 下层),同时关闭 tooltip canvas 事件。
<view class="vchart"> <!-- 绘制层 canvas --> <canvas class="cs-canvas" id="line_draw_canvas" canvas-id="line_draw_canvas" bindtouchstart="bindChartEvent" bindtouchmove="bindChartEvent" bindtouchend="bindChartEvent" > </canvas> <!-- 注意 canvas 顺序 --> <!-- tooltip canvas --> <canvas id="line_tooltip_canvas" canvas-id="line_tooltip_canvas" class="cs-tooltip-canvas" user-interaction-enabled="{{false}}" > </canvas> </view>
index.ttss
这一部分需要开发者给上方的 index.ttml 元素设置样式。值得注意的是,需要保证图表容器 canvas 是有宽高的。
/* chart */ .vchart { position: relative; width: 100%; margin: 12px 8px; height: 400px; display: block; } .cs-canvas { width: 100%; height: 100%; display: block; } .cs-tooltip-canvas { width: 100%; height: 100%; position: absolute; top: 0; left: 0; pointer-events: none; }
index.js
block 下的根 index.js,在这里引入 VChart 并创建 VChart 实例。开发者需在 block 的生命周期 onReady 内,进行 dom 的查询。取到 dom 后,开始创建 VChart 实例。
import pieSpec from "./data/pie";
import VChart from './vchart/index'; // 假设将 VChart 脚本放在这个目录下
...
onReady(){
tt.createSelectorQuery()
.select('#line_draw_canvas')
.boundingClientRect(domRef => {
if (!domRef) {
console.error(`未找到 #line 画布`);
return;
}
const chartInstance = new VChart(
{
width: domRef.width,
height: domRef.height,
...pieSpec
},
// 以下是小组件端环境的配置
{
mode: 'miniApp',
modeParams: {
domref: domRef,
force: true,
canvasIdLists: ['line_draw_canvas', 'line_tooltip_canvas'],
tooltipCanvasId: 'line_tooltip_canvas',
freeCanvasIdx: 1
},
// 需要保证 dpr 在发生变化时能够实时更新到对象上
dpr: tt.getSystemInfoSync().pixelRatio,
renderCanvas: 'line_draw_canvas'
}
);
chartInstance.renderSync();
})
.exec();
}
事件绑定
可以看到在上面 index.ttml 中 id 为 "line_draw_canvas" 的 canvas 绑定了 touchstart touchmove touchend 这三类事件。绑定了之后,需要在 index.js 上编写 bindChartEvent 事件处理函数:
// index.js
methods: {
init() {
// do something
},
// 处理事件
bindChartEvent(event) {
const id = event.target.id.split('_')[0];
// 以下为示例代码,即根据 id 查找对对应的 VChart 实例,实际场景请根据自己的情况获取 VChart 实例
const targetChart = this.data.chartList.find(x => x.id === id);
const chartInstance = targetChart?.chart;
if (chartInstance) {
event.target = chartInstance.getCanvas(); // Tip: 必须设置
chartInstance.getStage().window.dispatchEvent(event);
}
},
}
mouse 事件监听
飞书小组件桌面端也支持 mouse 事件,所以你也可以绑定 mousemove mouseover 等事件,但是要注意事件冲突的处理(这个需要在自己的业务层处理)。
<!-- index.ttml -->
<canvas
class="cs-canvas"
id="line_draw_canvas"
canvas-id="line_draw_canvas"
bindmousemove="bindChartEvent"
bindmouseover="bindChartEvent"
></canvas>
注册函数
自从1.7.0版本, 飞书小组件支持注册自定义函数,你可以使用全局注册或实例注册registerFunction两种方法进行函数注册。
全局注册函数
在使用全局注册函数时,调用图表方法VChart.registerFunction注册自定义函数,在运行时便会进行回调处理,如下方的示例。
- 在
index.js中引入 VChart,并注册labelFormat自定义函数
<!-- index.js -->
import VChart from './vchart/index';
Block({
// ...
onLoad(options) {
// 全局注册该自定义函数
VChart.registerFunction('labelFormat', (text) => {
return `$${text}`;
});
},
// ...
});
- spec 声明如下
{ type: 'line', // ... label: { visible: true, position: 'top', formatMethod: 'labelFormat', // 声明使用的函数名称 } }
实例注册函数
在使用实例注册函数时,调用实例方法registerFunction注册自定义函数,在运行时便会进行回调处理,如下方的示例。
<!-- index.js -->
methods: {
init() {
this.data.chartList.forEach(item => {
tt.createSelectorQuery()
.select(`#${item.id}_draw_canvas`)
.boundingClientRect(domRef => {
if (!domRef) {
console.error(`未找到 #${item.id} 画布`);
return;
}
item.chart && item.chart.release();
// 自定义函数
function labelFormat(key){
return key + 'test';
}
const chartInstance = new VChart(
{
width: domRef.width,
height: domRef.height,
/**
* spec中可使用该函数名'labelFormat'
* 例如,使用该函数做label的格式化
* label: {
* visible: true,
* formatMethod: 'labelFormat'
* }
*/
...item.spec
},
{
// do something
}
);
// 实例注册该自定义函数
chartInstance.registerFunction('labelFormat', labelFormat);
chartInstance.renderSync();
})
.exec();
});
}
}
具体详见:https://github.com/VisActor/VChart/tree/main/packages/block-vchart