Skip to content

Commit

Permalink
Merge pull request #107 from VisActor/feat/tutorials
Browse files Browse the repository at this point in the history
feat: support document for vstory-external
  • Loading branch information
neuqzxy authored Nov 25, 2024
2 parents e0834d6 + 96397f4 commit 773c436
Show file tree
Hide file tree
Showing 4 changed files with 524 additions and 0 deletions.
39 changes: 39 additions & 0 deletions docs/assets/guide/menu.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,45 @@
}
}
]
},
{
"path": "VStory_Concepts",
"title": {
"zh": "VStory 概念",
"en": "VStory Concepts"
},
"children": [
{
"path": "dsl",
"title": {
"zh": "dsl",
"en": "dsl"
}
},
{
"path": "story-player",
"title": {
"zh": "Story-Player",
"en": "Story-Player"
}
}
]
},
{
"path": "extend",
"title": {
"zh": "自定义扩展",
"en": "Custom Extension"
},
"children": [
{
"path": "Custom_Character",
"title": {
"zh": "自定义character",
"en": "Custom character"
}
}
]
}
]
}
Expand Down
177 changes: 177 additions & 0 deletions docs/assets/guide/zh/tutorial_docs/VStory_Concepts/dsl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# DSL定义

DSL是描述一个VStory作品的JSON格式。其中定义了这个作品中使用了哪些元素,以及相关配置。描述了这个作品是如何编排的,什么元素在什么时刻做了什么行为。关于DSL的快速实战入门请参考[一份基础的 DSL](../Basic/A_Basic_DSL)。本节教程将详细介绍DSL的具体定义。

## 结构
DSL 是一个 JSON 格式的对象,包含以下几个字段:
1. character数组
character 数组用于描述这个作品中使用了哪些元素,以及相关配置。
2. acts数组
acts 数组用于描述这个作品是如何编排的,什么元素在什么时刻做了什么行为。

```ts
interface IStoryDSL {
acts: IActSpec[]; // 作品的章节,描述这个作品是如何编排的,什么元素在什么时刻做了什么行为。
characters: ICharacterConfig[]; // 作品中的元素,描述这个作品中使用了哪些元素,以及相关配置。
}
```

### character数组
character 数组用于描述这个作品中使用了哪些类型的元素,以及相关配置。其中包含位置大小(position),层级(layout)。

```ts
type ICharacterConfig = IChartCharacterConfig | IComponentCharacterConfig;

// position的定义,描述元素的位置和大小,以及旋转锚点等信息
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; // character的类型
position: IWidgetData; // 定位描述
zIndex: number; // 层级描述
extra?: any; // 带着的额外信息,可不填
}
```

目前character有三大类型,分别是图表、组件、表格。主要是因为这三大类型的配置有较大差异,然后每个类型下面还有无数的子类型,比如组件类型,你可以自定义任意的组件,然后注册到VStory中在DSL中使用。

#### 图表类型

图表类型支持VChart图表,可以直接配置VChart的spec,然后支持一些额外属性列举如下:
```ts
interface IChartCharacterConfig extends ICharacterConfigBase {
options: {
// 图表spec
spec?: any;
// 初始化参数
initOption?: IInitOption;
// 边距
padding?: { left: number; top: number; right: number; bottom: number };
// 图表容器
panel?: any;
// 数据源
data?: any;
// 标题
title?: {
[key: string]: IComponentConfig<ISpec['title']>;
};
// 图例
legends?: {
[key: string]: IComponentConfig<ISpec['legends']>;
};
// axes
axes?: {
[key: string]: IComponentConfig<ISpec['axes']>;
};
// 色板
color?: any;
// mark 单元素样式
markStyle?: {
[key: string]: IMarkStyle;
};
// label 单元素样式 与 mark 区分开,runtime逻辑完全不同
labelStyle?: {
[key: string]: IMarkStyle;
};
// 组样式配置
dataGroupStyle?: {
[StroyAllDataGroup]: IDataGroupStyle; // 全部分组的样式
[key: string]: IDataGroupStyle; // 某一组
};
// 直接合并的配置,使用VChart的spec
rootConfig?: Record<string, any>;
};
}
```
#### 组件类型

文字、图片等都属于组件类型,如果需要在VStory中使用自定义组件,需要先注册到VStory中,然后在DSL中使用。这个在[自定义组件](./Custom_Component)中会详细介绍。
注意的是,组件可以携带一个额外的文本,这个文本通过text属性配置,而graphic属性则是组件本身的配置。
```ts
interface IComponentCharacterConfig extends ICharacterConfigBase {
options: {
// 主图元的配置
graphic: any;
// 面板配置
panel?: any;
// 文字配置
text?: any;
// 边距
padding?: { left: number; top: number; right: number; bottom: number };
};
}
```
#### 表格类型
正在开发中

### Acts数组
通过characters数组,我们可以在画布中放置多个元素,接下来我们需要通过acts数组来描述这个作品是如何编排的,什么元素在什么时刻做了什么行为。Acts由幕、场景、动作组成。acts数组中可以包含多个幕,幕与幕之间是有先后顺序的串联结构。每一个幕中可以包含多个场景,场景与场景默认是有先后顺序的串联结构。但是场景和场景的时间线是可以重叠的,通过配置场景的delay字段,可以控制该场景与上一个场景时间线的偏移。每一个场景中可以包含多个动作,动作中描述了一个或多个character的具体行为,一个场景中可以包含多个character和多个动作,动作之间是并行执行的,通过配置startTime来控制该动作的开始时间。

####
幕是作品中最大的章节,一个作品可以包含多个幕,幕与幕之间是有先后顺序的串联结构。
```ts
interface IActSpec {
id: string; // 幕的id
scenes: ISceneSpec[]; // 场景数组
}
```
#### 场景
场景是一个时间线,一个场景包含一个动作数组,场景与场景默认是有先后顺序的串联结构,但也可以通过配置delay字段来控制该场景与上一个场景时间线的偏移。
```ts
type ISceneSpec = {
id: string;
delay?: number; // 入场延迟,可以是正数或者负数
actions: IActions[];
};
```
#### 动作
一个动作包含一个或多个character的具体行为,一个场景中可以包含多个动作,动作之间是并行执行的,通过配置startTime来控制该动作的开始时间。
```ts
interface IActions {
characterId: string | string[]; // 要执行动作的character的id或者数组
characterActions: IAction<IActionPayload>[];
}

interface IAction<T extends IActionPayload> {
action: string; // 具体的action,比如appear
startTime?: number; // 动作开始时间
payload?: T | T[]; // 动作的参数
}

export interface IActionPayload {
animation?: IAnimationParams; // 具体的动画参数定义
selector?: string; // 选择器,用于指定要执行动作的元素,比如图表中,可以通过 bar 选择到柱子
}

export interface IAnimationParams {
duration: number; // 动画时长
easing?: EasingType; // 动画曲线
loop?: number | boolean; // 是否循环,循环几次
effect?: string | string[]; // 特效,比如appear有fade、bounce等特效
// 其他参数
dimensionCount?: number;
delayPerTime?: number;
enterPerTime?: number;
params?: Record<string, any>;
[key: string]: any;
}
```

到这里,一个DSL的完整定义就完成了,大家可以自己动手试一下,或者去example[/vstory/example]里去改一改试一试。
133 changes: 133 additions & 0 deletions docs/assets/guide/zh/tutorial_docs/VStory_Concepts/story-player.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Story和Player

首先建议大家阅读[dsl]()的章节,了解如何编写一个VStory的DSL描述。当我们有了一个DSL的JSON描述之后,我们就需要进行VStory的实例化,然后使用播放器去播放作品了。

## 注册

VStory默认采用按需加载的模式,所以在使用VStory之前需要先注册一些模块
```ts
registerGraphics();
registerCharacters();
registerVChartAction();
registerVComponentAction();
registerLottie();
registerLottieAction();
initVR();
```

我们也提供了直接全量加载依赖的模式
```ts
// 注册所有需要的内容
registerAll();
```

## 创建Story实例

如果您已经制作了一个DSL,可以直接传入给Story实例,如果DSL还没准备好,可以传null,后续准备好了再传入。

```ts
// 方式1:传入DSL,和一个容器id(CONTAINER_ID为容器ID)
const story = new Story(dsl, { dom: CONTAINER_ID, background: '#ebecf0' });

// 方式2:也可以直接传入canvas
const story = new Story(dsl, { canvas, background: '#ebecf0' });

// 方式3:也可以传入一个空的DSL
const story = new Story(null, { dom: CONTAINER_ID, background: '#ebecf0' });
// 后续再传入DSL
story.load(dsl);
```

## 命令添加character(可选)

如果您的DSL中没有包含character,那么您可以使用命令添加character。如果您已经静态的定义好了DSL,这个步骤就不需要

```ts
// 接口定义如下
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: '这是一个文本',
fontSize: 12,
fontWeight: 'bold',
fill: 'red',
textAlign: 'left',
textBaseline: 'top'
},
panel: {
fill: 'blue',
cornerRadius: 30
}
}
});
```

## 创建Player并绑定Story

创建好Story之后,我们就可以进行播放流程了,我们需要创建一个Player实例,然后绑定Story实例。

```ts
// 创建Player实例
const player = new Player(story);
// 初始化Story
story.init(player);
```
接下来调用`player.play()`即可播放。play方法接收一个number类型的参数,可传入0,-1,1
传入0表示只播放一次,播放到结尾就停止
传入1表示循环播放,播放到结尾就从头开始播放
传入-1表示不循环也不停止,播放到结尾之后时间线继续往后走,适用于有循环动画的场景,比如有一个持续播放的背景动画

```ts
// 只播放一次,播放到结尾就停止
player.play(0);
// 循环播放,播放到结尾就从头开始播放
player.play(1);
// 不循环也不停止,播放到结尾之后时间线继续往后走
player.play(-1);
```

到这里,story和player的定义就介绍完了,大家可以自己动手试一下,或者去example[/vstory/example]里去改一改试一试。
Loading

0 comments on commit 773c436

Please sign in to comment.