# ChartSpace 组件在小组件中使用
**注意事项**：ChartSpace 已升级至 VChart，如果你是新用户，欢迎使用 [VChart](https://www.visactor.io/vchart/guide/tutorial_docs/Cross-terminal_and_Developer_Ecology/mini-app/block)。如果你是 ChartSpace 用户，欢迎升级至 VChart，如何升级详见[如何升级 VChart](https://open.larksuite.com/document/uAjLw4CM/uYjL24iN/block/component/extend/how-to-upgrade-to-vchart)。
## 使用场景

小组件支持内置 ChartSpace 图表库、用于创建看板/报表类小组件，包含柱状图、条形图、散点图等图表类型，详见 [ChartSpace官网](https://datavis.bytedance.com/)。以下为样式参考：
![](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/000d0da14d99c587d9f3971c50169371_J5C0JzkmTK.png?height=600&lazyload=true&width=1600)

## 使用方式

1. block 内 index.json 增加配置：
```
{
    "useExtendedLib": {
        "chartSpace": true
    }
}
```
```
{
    "useExtendedLib": {
        "chartSpace": "0.1"
    }
}
```
其中 chartSpace 的取值
| 取值类型名称 | 类型      | 说明                 |
| ------ | ------- | ------------------ |
| 布尔值    | boolean | 打包 ChartSpace 最新版本 |
| 指定版本   | enum    | 打包 ChartSpace 指定版本 |
> 如果指定的版本不在版本列表内，会打包最新版本  
> 具体版本请看版本信息一节
2. 在需要使用 ChartSpace 变量的页面 js 中增加如下代码：
```
import ChartSpace from 'lark-block-chartspace'
```
至此，可以成功引入图表的核心 js sdk。接下来需要对它做一些封装，以便在小组件以我们熟悉的组件形式使用

由于小组件未支持图表组件，开发者需要处理 js、ttml、ttss 三部分内容

### ttml

在 ttml 去声明如下代码，作为使用图表能力的 canvas 载体，这里的属性有 `class`，`id`，`canvas-id`。
- `class` ：样式的类名，会和即将在 ttss 中设置的类自动链接使用
- `id`：用于在下方的 js 中通过 query 取到 dom
- `canvas-id` ：一个特殊标志符，是为了让编译服务进行元素区分
```
<view class="chart-space">
    <canvas 
      class="cs-canvas" 
      id="pie_draw_canvas"
      canvas-id="pie_draw_canvas">
    </canvas>
  </view>
```

### ttss

这一部分需要开发者给上方的 ttml 元素设置样式，宽度自适应，高度 100 像素。值得注意的是，需要保证图表容器 canvas 是有宽高的。
```
.chart-space {
  width: 100%;
  margin: 12px 8px;
  height: 100px;
}
.cs-canvas {
  width: 100%;
  height: 100%;
  display: block;
}
```

### js

block下的根 index.js，这里引入了饼图的描述数据 spec以及一个 TTCanvas 的对象（这个对象暂时不需要去关心它的逻辑）。随后，开发者需在 block 的生命周期 onReady 内，进行 dom 的查询。取到 dom 后，补充一个 dom 的 id 属性，再调用 ttCanvas 类进行图表的生成
```
import pieSpec from "./data/pie";
import { TTCanvas } from "./tt-canvas/index";
...
onReady(){
    tt.createSelectorQuery()
      .select(`#pie_draw_canvas`)
      .boundingClientRect(domRef => {
        if (!domRef) {
          console.error(`未找到 #${item.id} 画布`);
          return;
        }
        domRef.id = item.id;
        item.ttCanvas = new TTCanvas({
          domRef,
          spec: item.spec
        });
      })
      .exec();
}
```
饼图 spec 数据
```
export default {
  type: "common",
  data: {
    values: [
      {
        value: 159,
        type: "Tradition Industries"
      },
      {
        value: 50,
        type: "Business Companies"
      },
      {
        value: 13,
        type: "Customer-facing Companies"
      }
    ]
  },
  series: [
    {
      type: "pie",
      radius: 0.8,
      innerRadius: 0.5,
      angleField: "value",
      seriesField: "type"
    }
  ]
};
```
### ttCanvas 逻辑
这部分是处理小组件环境的一个图表胶水代码（开发者不必太关心这里的内容，在后续小组件迭代中将内置这部分代码）。目前，将其复制进代码中即可 
[tt-canvas](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/959126711c09f2571d9ca49dd8820de8_8XpwKkE8ON.zip)
至此，可以完成了一个饼图的配置。更多图表的配置项及文档，可以前往[Chartspace官网](https://datavis.bytedance.com/) 进行查看
### 图表事件使用教程文档
ChartSpace 提供了一套事件机制以及基于此事件机制的默认交互行为，下面就来看下如何在小组件环境使用。
1.  #### Canvas 元素上绑定事件

这里我们需要监听 `touchstart` `touchmove` `touchend` 这三类事件。
```
// index.ttml
<view class="chart-space">
    <canvas 
      class="cs-canvas" 
      id="pie_draw_canvas"
      canvas-id="pie_draw_canvas"
      bindtouchstart = "bindChartEvent"
      bindtouchmove = "bindChartEvent"
      bindtouchend = "bindChartEvent"
    >
    </canvas>
  </view>
```
2. #### 处理监听事件

```
// index.js
...
methods: {
    init() {
      // do something
    },
    // 处理事件
    bindChartEvent(event) {
      const id = event.target.id.split("_")[0];
      const targetChart = this.data.chartList.find(x => x.id === id);
      const canvas = targetChart?.ttCanvas?.chart?.canvas; // 获取对应的 canvas 
      if (!canvas) {
        return;
      }
      // 包装事件对象
      if (event?.changedTouches?.[0]) {
        event.offsetX = event.changedTouches[0].x;
        event.offsetY = event.changedTouches[0].y;
        event.x = event.changedTouches[0].x;
        event.y = event.changedTouches[0].y;

event.changedTouches.forEach(x => {
          x.offsetX = x.x;
          x.offsetY = x.y;
        });
      }

// complete standard event function
      event.preventDefault =
        event.preventDefault ||
        (() => {
          // do nothing
        });
      event.stopPropagation =
        event.stopPropagation ||
        (() => {
          // do nothing
        });

event.target = canvas; // Tip: 必须设置
      canvas.dispatchEvent(event.type, event); // 触发 canvas 的事件
    },
  }

```
3. #### 配置 tooltip

因为 ChartSpace 默认使用 html 渲染 tooltip，而小组件环境不支持 html，所以我们需要使用 canvas 版本的 tooltip，另外为了更好的交互性能，我们需要使用另一层 canvas 元素来作为 tooltip 绘制的载体。如下：

📢：注意 tooltip canvas 的 `id` `canvas-id` 的命名需要遵守如下规则：
1. `${name}`_tooltip_canvas，必须以 _tooltip_canvas 结尾
1. name 需要同绘制层的 canvas （draw canvas）保持一致

**同时需要注意 canvas 声明的顺序（在 draw canvas 下层），同时关闭** **tooltip** **canvas 事件。**

如下例子所示："pie_tooltip_canvas"，"pie_draw_canvas"
```
<view class="content">
  <view class="chart-space">
    <canvas
      class="cs-canvas"
      id="pie_draw_canvas"
      canvas-id="pie_draw_canvas"
      bindtouchstart = "bindChartEvent"
      bindtouchmove = "bindChartEvent"
      bindtouchend = "bindChartEvent"
    >
    </canvas>

< canvas
      id = "pie_tooltip_canvas"
      canvas-id = "pie_tooltip_canvas"
      class = "cs-tooltip-canvas" 
      user-interaction-enabled = {{false}}
    >
    </canvas>
 </view>
```
同时我们需要修改对应的样式：
```
/* chart */
.chart-space {
  position: relative;
  width: 100%;
  margin: 12px 8px;
  height: 150px;
  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; 
 } 
```
至此正常的图表交互就都开启了。

4. #### 监听图表事件
  下面介绍下如何自己监听图表的事件，这里以监听图表的 `touchend` 事件为例，说明如何自定义监听图表的事件。tt-canvas 开放了 `events` 属性用于图表的事件监听，所以我们需要在创建 ttcanvas 的时候将 events 属性传递给 ttcanvas 实例。

我们只需要在 onReady 生命周期中为对应的图表添加上 `events` 属性即可：
```
Block({
  data: {
    chartList: [
      {
        id: "pie",
        spec: pieSpec,
        ttCanvas: undefined
      },
    ],
  },
  onReady() {
    // Block 首次渲染完成
    this.data.chartList.forEach(x => { 
       x. events = [ 
         { 
           element: "view", 
           type: "touchstart"  ,  // 监听的事件类型
           handler: (...args) => { 
             console.log('touchstart', args[0].datum, args) 
           }  // 事件处理函数
         }, 
         { 
           element: "view", 
           type: "touchmove", 
           handler: (...args) => { 
             console.log('touchstart', args[0].datum, args) 
           } 
         }, 
       ]; 
    });
  },

methods: {
    init() {
      this.data.chartList.forEach(item => {
        tt.createSelectorQuery()
          .select(`#${item.id}_draw_canvas`)
          .boundingClientRect(domRef => {
            if (!domRef) {
              console.error(`未找到 #${item.id} 画布`);
              return;
            }
            domRef.id = item.id;
            item.ttCanvas = new TTCanvas({
              domRef,
              spec: item.spec,
              events : item. events
            });
          }).exec();
    });
  },
});
```

## Demo示例

我们提供了一个完整的 demo 项目附件，可供开发时参考：
[block-chart](//sf16-sg.larksuitecdn.com/obj/open-platform-opendoc-sg/aefd6ffe4cb3e0cb53bb019139e3c14d_PGwCRqpR2t.zip)

## IDE
本地调试可使用[Lark开发者工具](https://open.larksuite.com/document/uYjL24iN/ucDOzYjL3gzM24yN4MjN)最新版本

## 版本信息

| 版本号 | 图表功能 | 与上个版本的 changeLog |
| --- | ---- | ---------------- |
| 0.1 |      | /