diff --git a/docs/assets/guide/en/tutorial_docs/Basic/A_Basic_DSL.md b/docs/assets/guide/en/tutorial_docs/Basic/A_Basic_DSL.md new file mode 100644 index 00000000..0b7dd1cc --- /dev/null +++ b/docs/assets/guide/en/tutorial_docs/Basic/A_Basic_DSL.md @@ -0,0 +1,1004 @@ +# A Basic DSL + +In the previous chapters, we have roughly understood how to quickly create a VStory work. This tutorial will use a simple dashboard as an example to introduce the basic components of a VStory DSL in detail. A basic DSL should include the following parts: + +1. `character`: the characters used in the work +2. `acts`: the different behaviors of characters at different times + +By the end of this tutorial, we will achieve the effect shown in the image below: +![](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vstory/dashboard.gif) + +## 1. Material Preparation + +A dashboard will contain various charts, as well as modules such as titles and tables. Some of these modules can be implemented using specific characters provided by VStory, while others can be configured using VChart. In this tutorial, we will simplify the material preparation process and provide all the chart specs needed. + +1. A simple bar chart based on `VChart` +```javascript livedemo template=vchart +const mockData = []; +const types = ['A', 'B', 'C']; + +types.forEach(type => { + for (let i = 1; i <= 12; i++) { + mockData.push({ month: i + 'th', value: Math.random() * 100 + 10, type }); + } +}); + +const spec = { + type: 'bar', + data: [ + { + id: 'id0', + values: mockData + } + ], + xField: ['month', 'type'], + yField: 'value', + seriesField: 'type', + legends: { visible: true } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderSync(); + +// Just for the convenience of console debugging, DO NOT COPY! +window['vchart'] = vchart; +``` + +2. A simple area chart based on `VChart` +```javascript livedemo template=vchart +const mockData = []; +const types = ['A', 'B', 'C']; + +types.forEach(type => { + for (let i = 1; i <= 12; i++) { + mockData.push({ month: i + 'th', value: Math.random() * 100 + 10, type }); + } +}); + +const spec = { + type: 'area', + data: [ + { + id: 'id0', + values: mockData.filter((item) => item.type !== 'C') + } + ], + xField: 'month', + yField: 'value', + seriesField: 'type', + line: { + style: { + curveType: 'monotone' + } + }, + legends: { visible: true } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderSync(); + +// Just for the convenience of console debugging, DO NOT COPY! +window['vchart'] = vchart; +``` + + +3. A simple radar chart based on `VChart` +```javascript livedemo template=vchart +const mockData = []; +const types = ['A', 'B', 'C']; + +types.forEach(type => { + for (let i = 1; i <= 12; i++) { + mockData.push({ month: i + 'th', value: Math.random() * 100 + 10, type }); + } +}); + +const spec = { + type: 'radar', + data: [ + { + values: mockData + } + ], + categoryField: 'month', + valueField: 'value', + seriesField: 'type', + point: { + visible: false + }, + area: { + visible: true, + style: { + fillOpacity: 0.15, + curveType: 'catmullRomClosed', + curveTension: 0.6 + } + }, + line: { + visible: true, + style: { + curveType: 'catmullRomClosed', + curveTension: 0.6 + } + }, + legends: { + visible: true, + orient: 'top' + } +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderSync(); + +// Just for the convenience of console debugging, DO NOT COPY! +window['vchart'] = vchart; +``` + +4. A simple rose chart based on `VChart` +```javascript livedemo template=vchart +const mockData = []; +const types = ['A', 'B', 'C']; + +types.forEach(type => { + for (let i = 1; i <= 12; i++) { + mockData.push({ month: i + 'th', value: Math.random() * 100 + 10, type }); + } +}); + +const spec = { + type: 'rose', + data: [ + { + values: mockData + } + ], + categoryField: 'month', + valueField: 'value', + seriesField: 'type', + outerRadius: 1, + stack: true, + legends: [{ visible: true }], + axes: [ + { + orient: 'angle', + bandPadding: 0.02 + }, + ] +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderSync(); + +// Just for the convenience of console debugging, DO NOT COPY! +window['vchart'] = vchart; +``` + +5. A simple gauge chart based on `VChart` +```javascript livedemo template=vchart +const mockData = []; +const types = ['A', 'B', 'C']; + +types.forEach(type => { + for (let i = 1; i <= 12; i++) { + mockData.push({ month: i + 'th', value: Math.random() * 100 + 10, type }); + } +}); + +const spec = { + type: 'gauge', + data: [ + { + id: 'id0', + values: [ + { + type: '目标A', + value: 0.6 + } + ] + } + ], + categoryField: 'type', + valueField: 'value', + outerRadius: 0.8, + innerRadius: 0.5, + startAngle: -225, + endAngle: 45, +}; + +const vchart = new VChart(spec, { dom: CONTAINER_ID }); +vchart.renderSync(); + +// Just for the convenience of console debugging, DO NOT COPY! +window['vchart'] = vchart; +``` + +6. Use a `Text` type from `VStory` as the title +```javascript livedemo template=vstory +// 注册所有需要的内容 +VStory.registerAll(); + +const story = new VStory.Story(null, { dom: CONTAINER_ID, background: '#ebecf0' }); +const player = new VStory.Player(story); +story.init(player); + +story.addCharacterWithAppear({ + type: 'Text', + id: 'title', + zIndex: 1, + position: { + top: 50, + left: 50, + width: 800, + height: 100 + }, + options: { + graphic: { + text: 'VStory简易仪表盘', + fontSize: 70, + wordBreak: 'break-word', + textAlign: 'left', + textBaseline: 'top', + fill: 'black', + fontWeight: 200, + } + } +}); + +player.play(-1); +window.vstory = story; +``` + +7. Use a `WaveScatter` chart type from `VStory` +```javascript livedemo template=vstory +// 注册所有需要的内容 +VStory.registerAll(); +const mockData = []; +const types = ['A', 'B', 'C']; + +types.forEach(type => { + for (let i = 1; i <= 12; i++) { + mockData.push({ month: i + 'th', value: Math.random() * 100 + 10, type }); + } +}); + +const story = new VStory.Story(null, { dom: CONTAINER_ID, background: '#ebecf0' }); +const player = new VStory.Player(story); +story.init(player); + +story.addCharacterWithAppear({ + type: 'WaveScatter', + id: 'wave-scatter', + zIndex: 1, + position: { + top: 50, + left: 50, + width: 300, + height: 300 + }, + options: { + data: { + values: mockData.filter((item) => item.type === 'A') + }, + categoryField: 'month', + valueField: 'value', + /* 水波动画的配置 */ + waveDuration: 2000, + waveRatio: 0.00525, + waveColor: '#0099ff', + background: 'linear-gradient(180deg, #0099ff11 100%, #0099ff33 0%)', + amplitude: 10, + frequency: 2, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8, + clip: true + } + } +}); + +player.play(-1); +window.vstory = story; +``` + +## 2. Assembly + +Next, we will assemble these materials into a large canvas of `VStory`, forming a complete work. We will use a canvas size of 1920 * 1080, with a margin of 30px between the charts and a margin of 30px from the left and right borders. The specific layout is shown in the image below: + +![](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vstory/dashboard_layout_detail.png) + +After designing the layout, we will start writing the DSL to achieve the effect shown in the image above. The DSL core includes an array of `character` and an array of `acts`. The `character` array contains all the elements in the work, and the `acts` array contains various actions of the characters. The interface definitions are as follows: + +```ts +interface IStoryDSL { + acts: IActSpec[]; // 作品的章节 + characters: ICharacterConfig[]; // 作品中的元素 +} +/* character定义 */ +type ICharacterConfig = { + id: string; + type: string; // 类型 + position: IWidgetData; // 定位描述 + zIndex: number; + extra?: any; // 带着的额外信息 + options?: any; // 具体的配置信息 +} + +/* act定义 */ +interface IActSpec { + id: string; // 这一幕的id + scenes: ISceneSpec[]; // 这一幕包含的场景 +} +interface ISceneSpec { + id: string; // 这个场景的id + delay?: number; // 场景的入场延迟,可以是正数或者负数 + actions: IActions[]; // 这个场景包含的动作 +}; + +interface IActions { // 行为定义,角色和行为都可以配数组,可以定义多个角色执行多个行为 + characterId: string | string[]; // 执行行为的角色id + characterActions: IActionSpec[]; // 执行的具体行为 +} + +// 具体的行为定义 +interface IAction { + action: string; // 行为名称 + startTime?: number; // 开始时间 + payload?: { // 行为的参数 + animation?: IAnimationParams; + selector?: string; + [key?: string]: any; + }; +} + +``` + +### 2.1 Configuration of the `characters` array +Based on the provided configuration for each `character` and the interface definition, we can assemble our `characters` array. + +```ts +const characters = [ + { + type: 'Text', + id: 'Title', + zIndex: 3, + position: { + top: 100, + left: 1920 / 2, + width: 1920, + height: 90 + }, + options: { + graphic: { + fontSize: 70, + wordBreak: 'break-word', + textAlign: 'center', + textBaseline: 'bottom', + fill: 'black', + fontWeight: 200, + text: 'VStory简易仪表盘' + } + } + }, + { + type: 'WaveScatter', + id: 'wave-scatter', + zIndex: 1, + position: { + top: 130, + left: 30, + width: 600, + height: 630 + }, + options: { + data: { + values: mockData.filter((item) => item.type === 'A') + }, + categoryField: 'month', + valueField: 'value', + /* 水波动画的配置 */ + waveDuration: 2000, + waveRatio: 0.00525, + waveColor: '#0099ff', + background: 'linear-gradient(180deg, #0099ff11 100%, #0099ff33 0%)', + amplitude: 10, + frequency: 2, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8, + clip: true + } + } + }, + { + type: 'VChart', + id: 'radar1', + zIndex: 3, + position: { + top: 130, + left: 660, + width: 600, + height: 630 + }, + options: { + spec: radar1, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + { + type: 'VChart', + id: 'rose1', + zIndex: 3, + position: { + top: 130, + left: 1290, + width: 600, + height: 630 + }, + options: { + spec: rose1, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + { + type: 'VChart', + id: 'gauge1', + zIndex: 3, + position: { + top: 790, + left: 30, + width: 600, + height: 260 + }, + options: { + spec: gauge1, + padding: { + left: 0, + right: 0, + top: 0, + bottom: 0 + }, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + { + type: 'VChart', + id: 'bar1', + zIndex: 3, + position: { + top: 790, + left: 660, + width: 600, + height: 260 + }, + options: { + spec: bar1, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + { + type: 'VChart', + id: 'area1', + zIndex: 3, + position: { + top: 790, + left: 1290, + width: 600, + height: 260 + }, + options: { + spec: area1, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, +] +``` + +### 2.2 Configuration of the `acts` array +The `characters` array only defines the elements available in the work, but the specific actions are not defined yet. If actions are not defined, the elements will not be displayed. Therefore, we will define the `acts` array next. We expect the elements in the work to have the following actions: + +1. The bar chart and rose chart will have an `appear` animation effect with `oneByOne` (one by one) for the elements, while other charts will have the default `appear` animation effect. +2. The panels containing the charts themselves should also have a `bounce` animation effect with `appear`. + +Since the actions are simple, only one scene is needed to complete each act. + +```ts +const acts = [ + { + id: 'page1', // 这一幕的id + scenes: [ + { + id: 'singleScene', // 这一幕包含的场景 + actions: [ + // 除了柱状图和玫瑰图以外,其他character都做默认的appear的动画效果 + { + characterId: ['Title', 'area1', 'radar1', 'gauge1', 'wave-scatter'], + characterActions: [ + { + action: 'appear', + startTime: 0, + payload: { + animation: { + duration: 2000 + } + } + } + ] + }, + // 柱状图和玫瑰图做oneByOne的appear动画效果 + { + characterId: ['bar1', 'rose1'], + characterActions: [ + { + action: 'appear', + startTime: 0, + payload: { + animation: { + duration: 3000, + oneByOne: true, + dimensionCount: mockData.length + } + } + } + ] + }, + // 包含图表本身的面板做bounce的appear动画效果 + { + characterId: ['area1', 'radar1', 'bar1', 'rose1', 'gauge1', 'wave-scatter'], + characterActions: [ + { + action: 'bounce', + payload: { + animation: { + duration: 2000, + easing: 'quadOut' + }, + type: 'bounce4', + flipY: true, + } + } + ] + } + ] + } + ] + } +] +``` + +## 3. Playback + +Now that we have completed the steps to create a simple dashboard, we will combine the `character` and `acts` arrays to form a DSL, and then use VStory to play it. + +```ts +// 注册所有需要的内容 +VStory.registerAll(); + +// 创建一个VStory实例,将DSL传入 +const story = new VStory.Story(dsl, { dom: CONTAINER_ID, background: '#ebecf0' }); +// 创建一个player实例,用于播放这个Story +const player = new VStory.Player(story); +story.init(player); + +// 开始播放,传入1表示循环播放 +// 传入0表示单次播放,播放到结束时间就停止 +// 传入-1表示单次播放,但是播放结束后,时间会继续往后走,不会停止 +// 我们这里因为有一个永久在播放的波浪动画(wave),所以这里传入-1,不循环,但是时间不停止 +player.play(-1); +``` + +```javascript livedemo template=vstory +// 注册所有需要的内容 +VStory.registerAll(); + +function loadDSL() { + const mockData = []; + const types = ['A', 'B', 'C']; + + types.forEach(type => { + for (let i = 1; i <= 12; i++) { + mockData.push({ month: i + 'th', value: Math.random() * 100 + 10, type }); + } + }); + + const bar1 = { + type: 'bar', + data: [ + { + id: 'id0', + values: mockData + } + ], + xField: ['month', 'type'], + yField: 'value', + seriesField: 'type', + legends: { visible: true } + }; + + const area1 = { + type: 'area', + data: [ + { + id: 'id0', + values: mockData.filter((item) => item.type !== 'C') + } + ], + xField: 'month', + yField: 'value', + seriesField: 'type', + line: { + style: { + curveType: 'monotone' + } + }, + legends: { visible: true } + }; + + const radar1 = { + type: 'radar', + data: [ + { + values: mockData + } + ], + categoryField: 'month', + valueField: 'value', + seriesField: 'type', + point: { + visible: false + }, + area: { + visible: true, + style: { + fillOpacity: 0.15, + curveType: 'catmullRomClosed', + curveTension: 0.6 + } + }, + line: { + visible: true, + style: { + curveType: 'catmullRomClosed', + curveTension: 0.6 + } + }, + legends: { + visible: true, + orient: 'top' + } + }; + + const rose1 = { + type: 'rose', + data: [ + { + values: mockData + } + ], + categoryField: 'month', + valueField: 'value', + seriesField: 'type', + outerRadius: 1, + stack: true, + legends: [{ visible: true }], + axes: [ + { + orient: 'angle', + bandPadding: 0.02 + }, + ] + }; + + const gauge1 = { + type: 'gauge', + data: [ + { + id: 'id0', + values: [ + { + type: '目标A', + value: 0.6 + } + ] + } + ], + categoryField: 'type', + valueField: 'value', + outerRadius: 0.8, + innerRadius: 0.5, + startAngle: -225, + endAngle: 45, + }; + + return { + characters: [ + { + type: 'Text', + id: 'Title', + zIndex: 3, + position: { + top: 100, + left: 1920 / 2, + width: 1920, + height: 90 + }, + options: { + graphic: { + fontSize: 70, + wordBreak: 'break-word', + textAlign: 'center', + textBaseline: 'bottom', + fill: 'black', + fontWeight: 200, + text: 'VStory简易仪表盘' + } + } + }, + { + type: 'WaveScatter', + id: 'wave-scatter', + zIndex: 1, + position: { + top: 130, + left: 30, + width: 600, + height: 630 + }, + options: { + data: { + values: mockData.filter((item) => item.type === 'A') + }, + categoryField: 'month', + valueField: 'value', + /* 水波动画的配置 */ + waveDuration: 2000, + waveRatio: 0.00525, + waveColor: '#0099ff', + background: 'linear-gradient(180deg, #0099ff11 100%, #0099ff33 0%)', + amplitude: 10, + frequency: 2, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8, + clip: true + } + } + }, + { + type: 'VChart', + id: 'radar1', + zIndex: 3, + position: { + top: 130, + left: 660, + width: 600, + height: 630 + }, + options: { + spec: radar1, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + { + type: 'VChart', + id: 'rose1', + zIndex: 3, + position: { + top: 130, + left: 1290, + width: 600, + height: 630 + }, + options: { + spec: rose1, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + { + type: 'VChart', + id: 'gauge1', + zIndex: 3, + position: { + top: 790, + left: 30, + width: 600, + height: 260 + }, + options: { + spec: gauge1, + padding: { + left: 0, + right: 0, + top: 0, + bottom: 0 + }, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + { + type: 'VChart', + id: 'bar1', + zIndex: 3, + position: { + top: 790, + left: 660, + width: 600, + height: 260 + }, + options: { + spec: bar1, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + { + type: 'VChart', + id: 'area1', + zIndex: 3, + position: { + top: 790, + left: 1290, + width: 600, + height: 260 + }, + options: { + spec: area1, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 20 + } + } + }, + ], + acts: [ + { + id: 'page1', + scenes: [ + { + id: 'singleScene', + actions: [ + { + characterId: ['Title', 'area1', 'radar1', 'gauge1', 'wave-scatter'], + characterActions: [ + { + action: 'appear', + startTime: 0, + payload: { + animation: { + duration: 2000 + } + } + } + ] + }, + { + characterId: ['bar1', 'rose1'], + characterActions: [ + { + action: 'appear', + startTime: 0, + payload: { + animation: { + duration: 3000, + oneByOne: true, + dimensionCount: mockData.length + } + } + } + ] + }, + { + characterId: ['area1', 'radar1', 'bar1', 'rose1', 'gauge1', 'wave-scatter'], + characterActions: [ + { + action: 'bounce', + payload: { + animation: { + duration: 2000, + easing: 'quadOut' + }, + type: 'bounce4', + flipY: true, + // dy: 30, + } + } + ] + } + ] + } + ] + } + ] + }; +} + +const dsl = loadDSL(); + +const story = new VStory.Story(dsl, { dom: CONTAINER_ID, width: 1920 / 3, height: 1080 / 3, background: '#ebecf0', scaleX: 1/3, scaleY: 1/3 }); +const player = new VStory.Player(story); +story.init(player); + +player.play(-1); + +window['story'] = story; +window['vstory'] = story; +``` + +By following this tutorial, you have learned the components of a basic DSL configuration. You can now try modifying the `character` and `acts` arrays to explore the powerful features and flexibility of VStory, creating colorful and vibrant works. Happy coding! diff --git a/docs/assets/guide/en/tutorial_docs/Basic/How_to_Get_VStory.md b/docs/assets/guide/en/tutorial_docs/Basic/How_to_Get_VStory.md new file mode 100644 index 00000000..2651bd2f --- /dev/null +++ b/docs/assets/guide/en/tutorial_docs/Basic/How_to_Get_VStory.md @@ -0,0 +1,40 @@ +# How to Get VStory + +There are several ways to get VStory: + +1. Get from npm +2. Get from cdn +3. Get from GitHub repository + +## Get from npm + +```bash +# npm +$ npm install @visactor/vstory + +# yarn +$ yarn add @visactor/vstory +``` + +For how to use when getting, see [How to Import VStory in Your Project](./How_to_Import_VStory). + +## Get from cdn + +> Note: When importing VStory via cdn, pay attention to the way of referencing VStory: `const story = new VStory.Story(dsl, { dom: 'chart' });` + +You can get VStory from the following free CDNs: + +```html + + + + + +``` + +## Get from GitHub + +You can directly get the source code of VStory from GitHub: + +1. You can clone the source code directly from GitHub. +2. You can also choose the corresponding version from the [release](https://github.com/VisActor/VStory/releases) page of VStory on GitHub, click on the Source code under Assets at the bottom of the page, download it to your local machine, unzip it, and then use it. diff --git a/docs/assets/guide/en/tutorial_docs/Basic/How_to_Import_VStory.md b/docs/assets/guide/en/tutorial_docs/Basic/How_to_Import_VStory.md new file mode 100644 index 00000000..b726aa29 --- /dev/null +++ b/docs/assets/guide/en/tutorial_docs/Basic/How_to_Import_VStory.md @@ -0,0 +1,237 @@ +# How to Reference VStory in Your Project + +In the [How to Get VStory](./How_to_Get_VStory) section, we introduced how to get VStory. This section will explain how to reference VStory under these acquisition methods. + +## CDN Usage + +After we get the VStory file from [cdn](./How_to_Get_VStory#cdn-获取), we can add it to the ` + + +``` + +## NPM Usage + +After we install `@visactor/vstory` into the project through the [npm](./How_to_Get_VStory#npm-获取) method, we can use it in the following way: + +```ts +import { registerAll, Story, Player } from '@visactor/vstory'; +// Register all content +registerAll(); +const spec = { + type: 'pie', + data: [ + { + id: 'id0', + values: [ + { type: 'oxygen', value: '46.60' }, + { type: 'silicon', value: '27.72' }, + { type: 'aluminum', value: '8.13' }, + { type: 'iron', value: '5' }, + { type: 'calcium', value: '3.63' }, + { type: 'sodium', value: '2.83' }, + { type: 'potassium', value: '2.59' }, + { type: 'others', value: '3.5' } + ] + } + ], + outerRadius: 0.8, + valueField: 'value', + categoryField: 'type', + title: { + visible: true, + text: 'Surface element content statistics' + }, + legends: { + visible: true, + orient: 'left' + }, + label: { + visible: true + }, + tooltip: { + mark: { + content: [ + { + key: datum => datum['type'], + value: datum => datum['value'] + '%' + } + ] + } + } +}; + +// Create a DSL +const dsl = { + characters: [ + { + type: 'VChart', + id: '0', + position: { + top: 50, + left: 50, + width: 300, + height: 300 + }, + options: { + spec, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8 + }, + } + } + ], + acts: [ + { + id: 'default-chapter', + scenes: [ + { + id:'scene0', + actions: [ + { + characterId: '0', + characterActions: [ + { + action: 'appear', + payload: { + animation: { + duration: 3000 + } + } + } + ] + } + ] + } + ] + } + ] +} + +// Create a vstory instance +const story = new Story(dsl, { dom: 'chart', background: 'pink' }); +const player = new Player(story); +story.init(player); + +player.play(0); +``` diff --git a/docs/assets/guide/en/tutorial_docs/Getting_Started.md b/docs/assets/guide/en/tutorial_docs/Getting_Started.md new file mode 100644 index 00000000..571afc77 --- /dev/null +++ b/docs/assets/guide/en/tutorial_docs/Getting_Started.md @@ -0,0 +1,629 @@ +# Getting Started + +In this tutorial, we will introduce how to use VStory to draw a simple bar chart. VStory is a simple, easy-to-use, cross-platform, high-performance visualization storytelling library. It can combine all components in VisActor to create a powerful visual work. + +## Get VStory + +You can get VStory in the following ways: + +### Using NPM Package + +First, you need to install VStory in the project root directory using the following command: + +```sh +# Install using npm +npm install @visactor/vstory + +# Install using yarn +yarn add @visactor/vstory +``` + +### Using CDN + +You can also get the built VStory file through CDN. Add the following code to the ` +``` + +## Import VStory + +### Import via NPM Package + +Import VStory at the top of your JavaScript file using `import`: + +```js +import VStory from '@visactor/vstory'; +``` + +### Import using script tag + +By adding a ` + + +``` + +## Place a Chart + +Before drawing the chart, we need to prepare a DOM container with a specific width and height for VStory. + +```html + + +
+ +``` + +Next, we create a `VStory` instance, prepare a VChart chart with the DOM container's ID, generate DSL, and pass it in: + +```ts +// Register all necessary content +VStory.registerAll(); +// Prepare a VChart chart +const spec = { + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + type: 'bar', + xField: 'month', + yField: 'sales' +}; + +// Generate a DSL that only contains a VChart element +const dsl = { + characters: [ + { + type: 'VChart', + id: '0', + zIndex: 1, + // Position of the chart on the canvas + position: { + top: 50, + left: 50, + width: 300, + height: 300 + }, + options: { + // Configuration of the chart's background panel + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8 + } + }, + spec + } + ], + // Specific animation arrangement for the chart + acts: [ + // Array of chapters, a story can contain multiple chapters, and chapters are connected in a specific order + { + id: 'default-chapter', + scenes: [ + // Array of scenes, can contain multiple scenes, and scenes are connected in a specific order + { + id:'scene0', + // Array of actions in the scene, actions describe specific behaviors of one or more characters, and actions are executed in parallel within a scene + actions: [ + { + characterId: '0', + characterActions: [ + { + action: 'appear', + payload: { + animation: { + duration: 1000 + } + } + } + ] + } + ] + } + ] + } + ] +} + +// Create a VStory instance +const story = new VStory.Story(dsl, { dom: CONTAINER_ID, background: 'pink' }); +const player = new VStory.Player(story); +story.init(player); + +player.play(0); +``` + +You have successfully drawn a simple bar chart using VStory! + +Hope this tutorial helps you learn how to use VStory. Now, you can try adding different types of elements and create more diverse storytelling effects by exploring the various configuration options of VStory. Start your VStory journey with courage!# Getting Started + +In this tutorial, we will introduce how to use VStory to draw a simple bar chart. VStory is a simple, easy-to-use, cross-platform, high-performance visualization storytelling library. It can combine all components in VisActor to create a powerful visual work. + +## Get VStory + +You can get VStory in the following ways: + +### Using NPM Package + +First, you need to install VStory in the project root directory using the following command: + +```sh +# Install using npm +npm install @visactor/vstory + +# Install using yarn +yarn add @visactor/vstory +``` + +### Using CDN + +You can also get the built VStory file through CDN. Add the following code to the ` +``` + +## Import VStory + +### Import via NPM Package + +Import VStory at the top of your JavaScript file using `import`: + +```js +import VStory from '@visactor/vstory'; +``` + +### Import using script tag + +By adding a ` + + +``` + +## Place a Chart + +Before drawing the chart, we need to prepare a DOM container with a specific width and height for VStory. + +```html + + +
+ +``` + +Next, we create a `VStory` instance, prepare a VChart chart with the DOM container's ID, generate DSL, and pass it in: + +```ts +// Register all necessary content +VStory.registerAll(); +// Prepare a VChart chart +const spec = { + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + type: 'bar', + xField: 'month', + yField: 'sales' +}; + +// Generate a DSL that only contains a VChart element +const dsl = { + characters: [ + { + type: 'VChart', + id: '0', + zIndex: 1, + // Position of the chart on the canvas + position: { + top: 50, + left: 50, + width: 300, + height: 300 + }, + options: { + // Configuration of the chart's background panel + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8 + } + }, + spec + } + ], + // Specific animation arrangement for the chart + acts: [ + // Array of chapters, a story can contain multiple chapters, and chapters are connected in a specific order + { + id: 'default-chapter', + scenes: [ + // Array of scenes, can contain multiple scenes, and scenes are connected in a specific order + { + id:'scene0', + // Array of actions in the scene, actions describe specific behaviors of one or more characters, and actions are executed in parallel within a scene + actions: [ + { + characterId: '0', + characterActions: [ + { + action: 'appear', + payload: { + animation: { + duration: 1000 + } + } + } + ] + } + ] + } + ] + } + ] +} + +// Create a VStory instance +const story = new VStory.Story(dsl, { dom: CONTAINER_ID, background: 'pink' }); +const player = new VStory.Player(story); +story.init(player); + +player.play(0); +``` + +You have successfully drawn a simple bar chart using VStory! + +Hope this tutorial helps you learn how to use VStory. Now, you can try adding different types of elements and create more diverse storytelling effects by exploring the various configuration options of VStory. Start your VStory journey with courage!# Getting Started + +In this tutorial, we will introduce how to use VStory to draw a simple bar chart. VStory is a simple, easy-to-use, cross-platform, high-performance visualization storytelling library. It can combine all components in VisActor to create a powerful visual work. + +## Get VStory + +You can get VStory in the following ways: + +### Using NPM Package + +First, you need to install VStory in the project root directory using the following command: + +```sh +# Install using npm +npm install @visactor/vstory + +# Install using yarn +yarn add @visactor/vstory +``` + +### Using CDN + +You can also get the built VStory file through CDN. Add the following code to the ` +``` + +## Import VStory + +### Import via NPM Package + +Import VStory at the top of your JavaScript file using `import`: + +```js +import VStory from '@visactor/vstory'; +``` + +### Import using script tag + +By adding a ` + + +``` + +## Place a Chart + +Before drawing the chart, we need to prepare a DOM container with a specific width and height for VStory. + +```html + + +
+ +``` + +Next, we create a `VStory` instance, prepare a VChart chart with the DOM container's ID, generate DSL, and pass it in: + +```ts +// Register all necessary content +VStory.registerAll(); +// Prepare a VChart chart +const spec = { + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + type: 'bar', + xField: 'month', + yField: 'sales' +}; + +// Generate a DSL that only contains a VChart element +const dsl = { + characters: [ + { + type: 'VChart', + id: '0', + zIndex: 1, + // Position of the chart on the canvas + position: { + top: 50, + left: 50, + width: 300, + height: 300 + }, + options: { + // Configuration of the chart's background panel + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8 + } + }, + spec + } + ], + // Specific animation arrangement for the chart + acts: [ + // Array of chapters, a story can contain multiple chapters, and chapters are connected in a specific order + { + id: 'default-chapter', + scenes: [ + // Array of scenes, can contain multiple scenes, and scenes are connected in a specific order + { + id:'scene0', + // Array of actions in the scene, actions describe specific behaviors of one or more characters, and actions are executed in parallel within a scene + actions: [ + { + characterId: '0', + characterActions: [ + { + action: 'appear', + payload: { + animation: { + duration: 1000 + } + } + } + ] + } + ] + } + ] + } + ] +} + +// Create a VStory instance +const story = new VStory.Story(dsl, { dom: CONTAINER_ID, background: 'pink' }); +const player = new VStory.Player(story); +story.init(player); + +player.play(0); +``` + +You have successfully drawn a simple bar chart using VStory! + +Hope this tutorial helps you learn how to use VStory. Now, you can try adding different types of elements and create more diverse storytelling effects by exploring the various configuration options of VStory. Start your VStory journey with courage!# Getting Started + +In this tutorial, we will introduce how to use VStory to draw a simple bar chart. VStory is a simple, easy-to-use, cross-platform, high-performance visualization storytelling library. It can combine all components in VisActor to create a powerful visual work. + +## Get VStory + +You can get VStory in the following ways: + +### Using NPM Package + +First, you need to install VStory in the project root directory using the following command: + +```sh +# Install using npm +npm install @visactor/vstory + +# Install using yarn +yarn add @visactor/vstory +``` + +### Using CDN + +You can also get the built VStory file through CDN. Add the following code to the ` +``` + +## Import VStory + +### Import via NPM Package + +Import VStory at the top of your JavaScript file using `import`: + +```js +import VStory from '@visactor/vstory'; +``` + +### Import using script tag + +By adding a ` + + +``` + +## Place a Chart + +Before drawing the chart, we need to prepare a DOM container with a specific width and height for VStory. + +```html + + +
+ +``` + +Next, we create a `VStory` instance, prepare a VChart chart with the DOM container's ID, generate DSL, and pass it in: + +```ts +// Register all necessary content +VStory.registerAll(); +// Prepare a VChart chart +const spec = { + data: [ + { + id: 'barData', + values: [ + { month: 'Monday', sales: 22 }, + { month: 'Tuesday', sales: 13 }, + { month: 'Wednesday', sales: 25 }, + { month: 'Thursday', sales: 29 }, + { month: 'Friday', sales: 38 } + ] + } + ], + type: 'bar', + xField: 'month', + yField: 'sales' +}; + +// Generate a DSL that only contains a VChart element +const dsl = { + characters: [ + { + type: 'VChart', + id: '0', + zIndex: 1, + // Position of the chart on the canvas + position: { + top: 50, + left: 50, + width: 300, + height: 300 + }, + options: { + // Configuration of the chart's background panel + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4, + cornerRadius: 8 + } + }, + spec + } + ], + // Specific animation arrangement for the chart + acts: [ + // Array of chapters, a story can contain multiple chapters, and chapters are connected in a specific order + { + id: 'default-chapter', + scenes: [ + // Array of scenes, can contain multiple scenes, and scenes are connected in a specific order + { + id:'scene0', + // Array of actions in the scene, actions describe specific behaviors of one or more characters, and actions are executed in parallel within a scene + actions: [ + { + characterId: '0', + characterActions: [ + { + action: 'appear', + payload: { + animation: { + duration: 1000 + } + } + } + ] + } + ] + } + ] + } + ] +} + +// Create a VStory instance +const story = new VStory.Story(dsl, { dom: CONTAINER_ID, background: 'pink' }); +const player = new VStory.Player(story); +story.init(player); + +player.play(0); +``` + +You have successfully drawn a simple bar chart using VStory! + +Hope this tutorial helps you learn how to use VStory. Now, you can try adding different types of elements and create more diverse storytelling effects by exploring the various configuration options of VStory. Start your VStory journey with courage! diff --git a/docs/assets/guide/en/tutorial_docs/VStory_Concepts/dsl.md b/docs/assets/guide/en/tutorial_docs/VStory_Concepts/dsl.md new file mode 100644 index 00000000..d9bcdf4d --- /dev/null +++ b/docs/assets/guide/en/tutorial_docs/VStory_Concepts/dsl.md @@ -0,0 +1,178 @@ +# DSL Definition + +DSL is a JSON format that describes a VStory work. It defines which elements are used in this work and their related configurations. It describes how the work is arranged, what elements are doing what actions at what moment. For a quick hands-on introduction to DSL, please refer to [A Basic DSL](../Basic/A_Basic_DSL). This tutorial will provide a detailed explanation of the specific definition of DSL. + +## Structure +`DSL` is a JSON format object that contains the following fields: +1. `character` array + The `character` array is used to describe which elements are used in this work and their related configurations. +2. `acts` array + The `acts` array is used to describe how the work is arranged, what elements are doing what actions at what moment. + +```ts +interface IStoryDSL { + acts: IActSpec[]; // Chapters of the work, describing how the work is arranged and what elements are doing what actions at what moment. + characters: ICharacterConfig[]; // Elements in the work, describing which elements are used in this work and their related configurations. +} +``` + +### character Array +The `character` array is used to describe which types of elements are used in this work and their related configurations. It includes position and size (`position`), and layer (`layout`) information. + +```ts +type ICharacterConfig = IChartCharacterConfig | IComponentCharacterConfig; + +// Definition of position, describing the position, size, rotation anchor, etc. of the element +type IWidgetData = { + left?: number; + top?: number; + x?: number; + y?: number; + angle?: number; + anchor?: [number, number]; +} & ( + | { + bottom?: number; + right?: number; + } + | { + width?: number; + height?: number; + } +); + +interface ICharacterConfigBase { + id: string; + type: string; // Type of character + position: IWidgetData; // Position description + zIndex: number; // Layer description + extra?: any; // Extra information, optional +} +``` + +Currently, there are three major types of `character`: chart, component, and table. This is mainly because the configurations of these three types differ significantly, and each type has numerous subtypes. For example, in the component type, you can customize any component and register it in VStory for use in DSL. + +#### Chart Type + +The chart type supports VChart charts, where you can directly configure the VChart spec and additional properties as listed below: +```ts +interface IChartCharacterConfig extends ICharacterConfigBase { + options: { + // Chart spec + spec?: any; + // Initialization parameters + initOption?: IInitOption; + // Padding + padding?: { left: number; top: number; right: number; bottom: number }; + // Chart container + panel?: any; + // Data source + data?: any; + // Title + title?: { + [key: string]: IComponentConfig; + }; + // Legends + legends?: { + [key: string]: IComponentConfig; + }; + // Axes + axes?: { + [key: string]: IComponentConfig; + }; + // Color palette + color?: any; + // Mark element style + markStyle?: { + [key: string]: IMarkStyle; + }; + // Label element style, different from mark in runtime logic + labelStyle?: { + [key: string]: IMarkStyle; + }; + // Data group style configuration + dataGroupStyle?: { + [StroyAllDataGroup]: IDataGroupStyle; // Style for all groups + [key: string]: IDataGroupStyle; // Style for a specific group + }; + // Directly merged configuration, using VChart spec + rootConfig?: Record; + }; +} +``` +#### Component Type + +Text, images, etc., belong to the component type. If you need to use custom components in VStory, you need to register them in VStory first and then use them in DSL. This will be detailed in [Custom Component](./Custom_Component). +Note that a component can carry additional text, which is configured through the `text` property, while the `graphic` property is the configuration of the component itself. +```ts +interface IComponentCharacterConfig extends ICharacterConfigBase { + options: { + // Main graphic element configuration + graphic: any; + // Panel configuration + panel?: any; + // Text configuration + text?: any; + // Padding + padding?: { left: number; top: number; right: number; bottom: number }; + }; +} +``` +#### Table Type +Under development + +### Acts Array +Through the `characters` array, we can place multiple elements on the canvas. Next, we need to use the `acts` array to describe how the work is arranged, what elements are doing what actions at what moment. `acts` consist of acts, scenes, and actions. +The `acts` array can contain multiple acts, where acts are connected in a sequential structure. Each act can contain multiple scenes, where scenes are connected in a default sequential structure. However, scenes' timelines can overlap, and the `delay` field can be configured to control the offset of the timeline between this scene and the previous scene. Each scene can contain multiple actions, where actions describe specific behaviors of one or more `character`. Multiple characters and actions can be included in one scene, and actions are executed in parallel. The `startTime` is configured to control the start time of the action. + +#### Acts +Acts are the largest chapters in the work, and a work can contain multiple acts, which are connected in a sequential structure. +```ts +interface IActSpec { + id: string; // Act ID + scenes: ISceneSpec[]; // Array of scenes +} +``` +#### Scenes +A scene is a timeline that contains an array of actions. Scenes are connected in a default sequential structure, but the timeline of scenes can be offset from the previous scene by configuring the `delay` field. +```ts +type ISceneSpec = { + id: string; + delay?: number; // Entrance delay, can be positive or negative + actions: IActions[]; +}; +``` +#### Actions +An action contains the specific behavior of one or more `character`. Multiple actions can be included in one scene, and actions are executed in parallel. The `startTime` is configured to control the start time of the action. +```ts +interface IActions { + characterId: string | string[]; // ID or array of IDs of characters to perform the action + characterActions: IAction[]; +} + +interface IAction { + action: string; // Specific action, such as appear + startTime?: number; // Start time of the action + payload?: T | T[]; // Action parameters +} + +export interface IActionPayload { + animation?: IAnimationParams; // Specific animation parameter definition + selector?: string; // Selector, used to specify the element to perform the action, e.g., selecting a bar in a chart +} + +export interface IAnimationParams { + duration: number; // Animation duration + easing?: EasingType; // Animation curve + loop?: number | boolean; // Whether to loop, number of loops + effect?: string | string[]; // Effects, such as fade, bounce for appear + // Other parameters + dimensionCount?: number; + delayPerTime?: number; + enterPerTime?: number; + params?: Record; + [key: string]: any; +} +``` + +With this, the complete definition of a DSL is finished. You can try it out yourself or make changes in the [example](/vstory/example) to experiment. diff --git a/docs/assets/guide/en/tutorial_docs/VStory_Concepts/story-player.md b/docs/assets/guide/en/tutorial_docs/VStory_Concepts/story-player.md new file mode 100644 index 00000000..631d5462 --- /dev/null +++ b/docs/assets/guide/en/tutorial_docs/VStory_Concepts/story-player.md @@ -0,0 +1,133 @@ +# Story and Player + +First, it is recommended to read the chapter on [dsl](./dsl) to understand how to write a VStory DSL description. Once we have a DSL JSON description, we need to instantiate the VStory and then use a player to play the work. + +## Registration + +VStory defaults to a lazy loading mode, so before using VStory, you need to register some modules first. +```ts +registerGraphics(); +registerCharacters(); +registerVChartAction(); +registerVComponentAction(); +registerLottie(); +registerLottieAction(); +initVR(); +``` + +We also provide a mode to directly load all dependencies. +```ts +// Register all necessary content +registerAll(); +``` + +## Create Story Instance + +If you have created a DSL, you can directly pass it to the Story instance. If the DSL is not ready yet, you can pass null and then pass it later. + +```ts +// Method 1: Pass the DSL and a container id (CONTAINER_ID is the container ID) +const story = new Story(dsl, { dom: CONTAINER_ID, background: '#ebecf0' }); + +// Method 2: You can also directly pass a canvas +const story = new Story(dsl, { canvas, background: '#ebecf0' }); + +// Method 3: You can also pass an empty DSL +const story = new Story(null, { dom: CONTAINER_ID, background: '#ebecf0' }); +// Later pass the DSL +story.load(dsl); +``` + +## Add Character Commands (Optional) + +If your DSL does not include `character`, you can use commands to add `character`. If you have already statically defined the DSL, this step is not necessary. + +```ts +// Interface definition as follows +addCharacter: (config: ICharacterConfig, actionParams?: IActionParams) => ICharacter; +addCharacterWithAppear: (config: ICharacterConfig) => ICharacter; +``` + +```ts +story.addCharacter({ + type: item.type, + id: item.type, + zIndex: 1, + position: { + top: 50 + Math.floor(index / 2) * 150, + left: 50 + Math.floor(index % 2) * 150, + width: 100, + height: 100 + }, + options: item.options +}, { + sceneId: 'defaultScene', + actions: [ + { + action: 'appear', + startTime: 1000 * index, + payload: [ + { + animation: { + duration: 1000, + easing: 'linear', + effect: item.effect + } + } + ] + } + ] +}); + +story.addCharacterWithAppear({ + type: 'Text', + id: 'title', + zIndex: 1, + position: { + top: 50, + left: 50, + width: 800, + height: 100 + }, + options: { + graphic: { + text: 'This is a text', + fontSize: 12, + fontWeight: 'bold', + fill: 'red', + textAlign: 'left', + textBaseline: 'top' + }, + panel: { + fill: 'blue', + cornerRadius: 30 + } + } +}); +``` + +## Create Player and Bind Story + +After creating the `Story`, we can proceed with the playback process by creating a `Player` instance and then binding it to the `Story` instance. + +```ts +// Create a Player instance +const player = new Player(story); +// Initialize the Story +story.init(player); +``` +Next, call `player.play()` to start playing. The `play` method takes a parameter of type `number`, where you can pass 0, -1, or 1. +Passing 0 means playing only once, stopping at the end. +Passing 1 means looping playback, starting over from the beginning at the end. +Passing -1 means neither looping nor stopping, allowing the timeline to continue after reaching the end, suitable for scenes with continuous animations, such as a background animation that plays continuously. + +```ts +// Play only once, stop at the end +player.play(0); +// Loop playback, start over from the beginning at the end +player.play(1); +// Neither loop nor stop, continue the timeline after reaching the end +player.play(-1); +``` + +That's all for the definitions of `story` and `player`. You can try it out yourself or make changes in the [example](/vstory/example) to see how it works. diff --git a/docs/assets/guide/en/tutorial_docs/VStory_Website_Guide.md b/docs/assets/guide/en/tutorial_docs/VStory_Website_Guide.md new file mode 100644 index 00000000..3d7c4d86 --- /dev/null +++ b/docs/assets/guide/en/tutorial_docs/VStory_Website_Guide.md @@ -0,0 +1,55 @@ +# Site Guide + +This document is mainly to help you use the VStory site better and help you get the content you want on the site faster. + +## Quick Start + +### Getting Started + +First, you need to complete the [Getting Started](./Getting_Started) section of VStory. It will teach you how to configure and use the environment required by VStory, as well as how to build your first data story. + +### Understanding Your First Chart + +After you complete [Getting Started](./Getting_Started), you can learn more about the composition of a DSL and the VisActor components it can use by studying the [DSL Composition](../tutorial_docs/Chart_Concepts/Understanding_VChart) section. + +## Documentation + +The VStory documentation provides detailed information about features and configurations. Depending on your needs, you can view the following sections: + +- [Tutorial](./VStory_Website_Guide): Introduces the basic concepts of VStory and various usage methods. +- [Concepts](./VStory_Concepts/dsl): Provides the definition of VStory `DSL` and an introduction to core classes. +- [Extension](./extend/Custom_Character): Provides tutorials and guides for custom extensions. + +In addition, to better use VStory, it is recommended that you also understand [VChart](/Vchart), [VTable](/VTable), [VRender](/VRender). + +## Chart Examples + +The Chart Examples page provides many practical application cases of VStory, from simple animations, to Characters, to orchestration, to templates. Each example provides detailed descriptions, key configuration information, and source code. You can also modify the example and view the effect through the online editor. + +
+ Chart Examples +
+ +## How to Use the Search Function + +This site provides a powerful search function that allows you to quickly find relevant usage information. Click on the search box at the top of the site, enter keywords, and you will see matching results in the drop-down list. Select the appropriate result to view the relevant content. + +
+ How to Use the Search Function +
+ +## How to Ask Questions & Suggestions + +We are happy to help you! If you encounter problems while learning VStory, you can ask questions in the following ways: + +1. Submit an issue in the GitHub repository: Visit [VStory GitHub](https://github.com/VisActor/VStory/issues/new/choose), describe the problem you encountered in detail, and our team will respond and resolve it as soon as possible. +2. Submit a discussion in the GitHub repository: Visit [VStory Discussion](https://github.com/VisActor/VStory/discussions), we welcome your ideas and suggestions here, and our team will respond and resolve them as soon as possible. + +## How to Correct Errors + +If you find problems in the document examples, or think that some parts can be improved, please tell us. You can provide us with error correction information in the following ways: + +1. Submit a pull request in the GitHub repository: Correct the content and submit it, and a team member will review and merge it. +2. Submit an issue: Point out the problems in the document, and team members will verify and correct them as soon as possible. + +Thank you for your help! We will continue to improve the documentation and provide a better learning experience for all VStory users. diff --git a/docs/assets/guide/en/tutorial_docs/extend/Custom_Character.md b/docs/assets/guide/en/tutorial_docs/extend/Custom_Character.md new file mode 100644 index 00000000..c1fabe58 --- /dev/null +++ b/docs/assets/guide/en/tutorial_docs/extend/Custom_Character.md @@ -0,0 +1,218 @@ +# Custom Character + +In the previous chapters, we introduced the basic concepts of DSL and then explained how to use VStory to arrange a story. However, in actual development, we may need to customize some Characters, such as customizing a special component and then using it in DSL. In this chapter, we will introduce how to customize a Character with a real example. + +## Introduction + +We provide the `@visactor/vstory-external` package to facilitate users in developing custom Characters. Some extended features can be implemented in this package. Below, we will demonstrate how to extend the package by integrating Lottie. + +![](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/vstory/vstory-external.png) + +## Customizing a Lottie Character + +If you need to display a Lottie animation in your work, but the main VStory package does not provide such a Character, you will need to extend it in the `@visactor/vstory-external` package. + +### Implementing a Component based on VRender + +Since VStory is based on VRender, we need to implement the corresponding functionality with VRender first, and then integrate it into VStory as a `Character`. Fortunately, the `@visactor/vrender-kits` package already provides a Lottie element that we can directly use. +We found the `Lottie` element in the `@visactor/vrender-kits` package, and to use it in VStory, we need to encapsulate it. The purpose of encapsulation is twofold: +1. Provide some default attributes +2. Provide parameter conversion, as the parameters in VStory are different from those in the VRender `Lottie` element. For example, layout parameters need to be converted to corresponding x, y, width, height, etc. parameters. +3. Obtain some special abilities of VStory, such as all components in VStory carry a text configuration. + +```ts +export class LottieComponent extends BaseComponentWithText { + static defaultAttributes: Partial = { + visible: true, + textStyle: {}, + width: 100, + height: 100, + padding: { + top: 0, + bottom: 0, + left: 0, + right: 0 + } + }; + + lottieInstance: Lottie; + + constructor(attributes: ILottieComponentAttributes, options?: ComponentOptions) { + super(options?.skipDefault ? attributes : merge({}, LottieComponent.defaultAttributes, attributes)); + } + + protected render(): void { + super.render(); + // Add image + this.renderLottie(); + } + protected renderLottie() { + const { graphic, padding, width, height } = this.attribute as ILottieComponentAttributes; + const attrs = { ...graphic }; + if (!attrs.x) { + attrs.x = padding.left; + } + if (!attrs.y) { + attrs.y = padding.top; + } + if (!attrs.width) { + attrs.width = width - padding.left - padding.right; + } + if (!attrs.height) { + attrs.height = height - padding.top - padding.bottom; + } + if (!this.lottieInstance) { + const lottie = new Lottie({}); + this.lottieInstance = lottie; + this.addChild(lottie); + } + this.lottieInstance.setAttributes({ ...attrs, scaleX: 1, scaleY: 1, angle: 0, postMatrix: null }); + } +} +``` + +### Encapsulating a Character + +Once we have a `Lottie` component, we can encapsulate a `Character`. Subsequently, the `character` configuration in DSL will be instantiated as the `Character` instance we have encapsulated, and in the Character, we will use the above `Lottie` component. + +```ts +export class LottieCharacter extends CharacterComponent { + static type = LOTTIE; + + protected _group: IGroup; + + static RunTime: IComponentCharacterRuntimeConstructor[] = [LottieRuntime]; + + protected createAndAddGraphic(attribute: ILottieComponentAttributes): void { + this._graphic = new LottieComponent(attribute); + this.canvas.addGraphic(this._graphic); + } + + protected _initRuntime(): void { + LottieCharacter.RunTime.forEach(R => { + this._runtime.push(new R(this)); + }); + } + + protected getDefaultAttribute(): Partial { + return { + ...super.getDefaultAttribute(), + width: 100, + height: 100 + }; + } + + protected _clearGraphic(): void { + super._clearGraphic(); + } + + show() { + this._graphic.setAttribute('visibleAll', true); + } + hide() { + this._graphic.setAttribute('visibleAll', false); + } +} +``` + +After encapsulation, we need to define a `Runtime` for `Lottie`. `Runtime` is the content that a Character executes at runtime, with some lifecycles, such as when options of the Character are updated, the runtime will execute. + +```ts +export class LottieRuntime extends BaseRuntime implements IComponentCharacterRuntime { + type = 'Lottie'; + applyConfigToAttribute(): void { + super.applyConfigToAttribute(); + const rawAttribute = this._character.getAttribute(); + + const { data } = rawAttribute.graphic; + // Place a default Lottie + const builtData = builtinLottieMap[data]; + if (builtData) { + rawAttribute.graphic.data = builtData; + } + // TODO Currently VRender has an issue, must configure fill to draw + rawAttribute.graphic.fill = true; + } +} +``` + +## Defining Processor + +After defining a Lottie Character, we now need to define specific behaviors, such as appear, disappear, style, etc., to execute entrance, exit, and style changes. The definition of behaviors in the code is in the `processor`. The processor is responsible for handling the corresponding behaviors defined in DSL. We just need to define an appear and disappear processor for Lottie. Other behaviors will reuse the behaviors of common components. + +What we need to do is simple. We just need to play the Lottie animation when appearing. Other behaviors will reuse the behaviors of common components. + +```ts +function runLottieAnimate(character: ICharacter, effect: string) { + const graphics = getCharacterByEffect(character, effect) as IGraphic[]; + graphics.forEach((graphic: any) => _runLottieAnimate(graphic)); +} + +function _runLottieAnimate(graphic: IGraphic) { + if (graphic && graphic.type !== 'text' && graphic.type !== 'richtext') { + // Play the Lottie animation when appearing + if (graphic.lottieInstance) { + graphic.lottieInstance.stop(); + graphic.lottieInstance.play(); + } + } +} + +export class LottieVisibilityActionProcessor extends CommonVisibilityActionProcessor { + name: string = 'appearOrDisAppear'; + constructor() { + super(); + } + + run(character: ICharacter, actionSpec: IActionSpec): void { + super.run(character, actionSpec); + // Execute special behavior for Lottie + runLottieAnimate(character, actionSpec.action); + } +} +``` + +## Registration + +Next, we export the registration methods for `Character` and `Processor`. You can see the specific code in `packages/vstory-external/src/character/lottie` and `packages/vstory-external/src/processor/lottie`. + +Finally, we can use `Lottie` in DSL. + +```javascript livedemo template=vstory +// Register all necessary content +VStory.registerAll(); +// Register Lottie +VStory.registerLottie(); +VStory.registerLottieAction(); + +const story = new VStory.Story(null, { dom: CONTAINER_ID, background: '#ebecf0' }); +const player = new VStory.Player(story); +story.init(player); + +story.addCharacterWithAppear({ + type: 'Lottie', + id: 'lottie-test', + zIndex: 2, + position: { + top: 50, + left: 50, + width: 300, + height: 300 + }, + options: { + graphic: { + data: 'loading1' + }, + panel: { + fill: '#ffffff', + shadowColor: 'rgba(0, 0, 0, 0.05)', + shadowBlur: 10, + shadowOffsetX: 4, + shadowOffsetY: 4 + } + } +}); + +player.play(-1); +window.vstory = story; +``` diff --git a/docs/assets/guide/zh/tutorial_docs/Basic/A_Basic_DSL.md b/docs/assets/guide/zh/tutorial_docs/Basic/A_Basic_DSL.md index 9c6a36ce..a7eac2e9 100644 --- a/docs/assets/guide/zh/tutorial_docs/Basic/A_Basic_DSL.md +++ b/docs/assets/guide/zh/tutorial_docs/Basic/A_Basic_DSL.md @@ -230,16 +230,13 @@ story.addCharacterWithAppear({ }, options: { graphic: { - text: '这是一个文本', - fontSize: 12, - fontWeight: 'bold', - fill: 'red', + text: 'VStory简易仪表盘', + fontSize: 70, + wordBreak: 'break-word', textAlign: 'left', - textBaseline: 'top' - }, - panel: { - fill: 'blue', - cornerRadius: 30 + textBaseline: 'top', + fill: 'black', + fontWeight: 200, } } }); diff --git a/docs/assets/guide/zh/tutorial_docs/VStory_Concepts/story-player.md b/docs/assets/guide/zh/tutorial_docs/VStory_Concepts/story-player.md index 2765d368..7eb7fa39 100644 --- a/docs/assets/guide/zh/tutorial_docs/VStory_Concepts/story-player.md +++ b/docs/assets/guide/zh/tutorial_docs/VStory_Concepts/story-player.md @@ -130,4 +130,4 @@ player.play(1); player.play(-1); ``` -到这里,`story`和`player`的定义就介绍完了,大家可以自己动手试一下,或者去example[/vstory/example]里去改一改试一试。 +到这里,`story`和`player`的定义就介绍完了,大家可以自己动手试一下,或者去[example](/vstory/example)里去改一改试一试。