!!!###!!!title=character——VisActor/VStory 教程文档!!!###!!!!!!###!!!description=请先阅读[dsl 的定义](./DSL),然后再阅读本节内容。Character(角色) 是 VStory 中最基础的元素,它可以是图表、组件、表格等,它可以只是一个简单的文字,也可以是一个非常复杂的图表。Character 可以通过 DSL 配置,也可以通过 API 动态添加。Character 是需要预定义的,如果你的作品中需要使用某个 Character,你需要在 DSL 的`characters`数组中定义好这个 Character。!!!###!!!

character

请先阅读dsl 的定义,然后再阅读本节内容。 Character(角色) 是 VStory 中最基础的元素,它可以是图表、组件、表格等,它可以只是一个简单的文字,也可以是一个非常复杂的图表。Character 可以通过 DSL 配置,也可以通过 API 动态添加。Character 是需要预定义的,如果你的作品中需要使用某个 Character,你需要在 DSL 的characters数组中定义好这个 Character。

Character 的定义

所有的character都可以由一些通用的配置和一些特殊的配置定义。通用配置包括:

  • id:character的 id,用于唯一标识这个character,后续在定义具体的行为(action)时会用到
  • type:character的类型,目前 VStory 支持的character类型包括但不限于:VChartTextImage
  • position:character的位置和大小,以及旋转锚点等信息
  • zIndex:character的层级,默认为 0,层级高的character会覆盖层级低的character

所有的特殊配置都在 options 里,举例:

const textConfig = {
  type: 'Text', // 标记是文字类型
  id: 'title1',
  zIndex: 1,
  position: {
    top: 100,
    left: 200
  },
  // 这里定义文字的配置
  options: {
    graphic: {
      text: 'A BRIEF HISTORY',
      fontSize: 12,
      fill: 'red'
    }
  }
};

const imageConfig = {
  type: 'Image', // 标记是图片类型
  id: 'image1',
  zIndex: 1,
  position: {
    top: 100,
    left: 200
  },
  // 这里定义图片的配置
  options: {
    graphic: {
      image: 'url'
    }
  }
};

关于准确的接口定义,如下所示:

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; // 类型
  position: IWidgetData; // 定位描述
  zIndex: number;
  extra?: any; // 带着的额外信息
  options: any; // 特殊的配置,所有不同的Character可以将自己特殊的配置传入在这里
}

内置的 Character 类型

VChart

VChart 是 VStory 中最常用的 Character 类型,它可以是各种类型的图表,比如折线图、柱状图、饼图等。VChart 可以通过直接传入 VChart 的 Spec 来定义,具体的 spec 如何定义,请参考vchart 站点。VChart 的接口定义如下:

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 in ModelSelector]: Partial<ElementType<ISpec['title']>>;
    };
    // 图例
    legends?: {
      [key in ModelSelector]: Partial<ElementType<ISpec['legends']>>;
    };
    // axes
    axes?: {
      [key in ModelSelector]: Partial<ElementType<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>;
  };
}

我们看一个 VChart 的例子:

VTable

VTable 是 VStory 中表格的 Character 类型,它可以是各种类型的表格。VTable 可以通过直接传入 VTable 的 Spec 来定义,具体的 spec 如何定义,请参考vtable 站点 VTable 的接口定义如下:

interface ITableCharacterConfigOptionsType {
  // 表格spec
  spec?: any;
  records: any;
  columns: any;
  widthMode?: 'standard' | 'adaptive' | 'autoWidth';
  defaultRowHeight: number;
  showHeader?: boolean;
  theme: any;
  // 数据源
  data?: any;

  panel?: any;
  padding?: number | [number, number] | [number, number, number, number];

  // 单个单元格样式,框选也会应用到这里
  // 使用 map 不用数组的原因。减少写入是的反复遍历
  cellStyle?: {
    // col_row
    [key: string]: {
      col: number;
      row: number;
      style?: any;
    };
  };

  // 列宽
  colWidth?: {
    [key: number]: number;
  };
  // 行高
  rowHeight?: {
    [key: number]: number;
  };
  // 列样式(包括列头)
  colStyle?: {
    [key: number]: any;
  };
  // 行样式(包括行头)
  rowStyle?: {
    [key: number]: any;
  };
  // 列隐藏
  colVisible?: {
    [key: number]: boolean;
  };
  // 行隐藏
  rowVisible?: {
    [key: number]: boolean;
  };
  // 内容列样式
  contentColStyle?: {
    [key: number]: any;
  };
  // 内容行样式
  contentRowStyle?: {
    [key: number]: any;
  };
}

export interface ITableCharacterConfig extends ICharacterConfigBase {
  options: ITableCharacterConfigOptionsType;
}

我们看一个 VTable 的例子:

基本组件(Image、Line、Rect、Shape、Text、Arc、Polygon)

组件类型的接口定义如下,其中基本组件(Image、Line、Rect、Shape、Text、Arc、Polygon)的配置都直接基于 VRender 的对应图元,配置在 graphic 属性上:

其中 panel 是组件的额外面板,其实是一个VRender 的 rect图元,可以参考VRender 的 rect图元配置。 text 配置是每个组件都带有的一个额外的配置,是一个VRender 的 text图元,可以参考VRender 的 text图元配置。 padding 表示面板和内容的边距,分别表示上、右、下、左的边距。

interface IComponentCharacterConfig extends ICharacterConfigBase {
  options: {
    // 主图元的配置
    graphic: any;
    // 面板配置
    panel?: any;
    // 文字配置
    text?: any;
    // 边距
    padding?: { left: number; top: number; right: number; bottom: number };
  };
}

使用案例如下:

Timeline 组件

Timeline组件是一个时间轴组件,可以展示一整个时间序列,以及时间的流向,它的接口定义如下:

interface TimelineAttrs extends IGroupGraphicAttribute {
  width: number; // 宽度
  // height?: number;
  times: { label: string; desc?: string }[]; // 具体的时间序列
  labelSpace?: number; // 标签的间距
  symbolStyle?: Partial<ISymbolGraphicAttribute>; // 时间点的样式
  activeSymbolStyle?: Partial<ISymbolGraphicAttribute>; // 激活的时间点的样式
  lineStyle?: Partial<ILineGraphicAttribute>; // 时间线的样式
  activeLineStyle?: Partial<ILineGraphicAttribute>; // 激活的时间线的样式
  labelStyle?: Partial<ITextGraphicAttribute>; // 标签的样式
  activeLabelStyle?: Partial<ITextGraphicAttribute>; // 激活的标签的样式
  pointLayoutMode?: 'space-around' | 'space-between'; // 布局模式
  // 当前进度
  clipRange?: number;
  animation?: boolean; // 是否开启动画
}

interface ITimelineComponentAttributes extends IGroupGraphicAttribute {
  // 结合富文本textConfig的文本配置
  textStyle?: Partial<ITextGraphicAttribute & { textConfig: IRichTextAttribute['textConfig'] }>;
  graphic?: TimelineAttrs;
  /**
   * 内部边距
   */
  padding?: {
    top?: number;
    bottom?: number;
    left?: number;
    right?: number;
  };
}

使用案例如下:

Unit 组件

单元可视化组件,单元可视化是一种将数据转化为视觉元素的叙事方式,能够直观地展示复杂信息。通过将每个数据点个体化,观众能更深入地理解每一个数据背后所代表的真实故事。这种方式利用动画和时间推移,生动地描绘数据变化的过程,同时通过颜色和形状等视觉元素传达多维度的信息,增强了情感共鸣。它不仅提升了数据的可读性,还易于在社交媒体上分享,有助于提高公众对重要社会问题的关注。

Unit组件的接口定义如下

interface IUnitGraphicAttributes extends IGroupAttribute {
  /**
   * The width of the container.
   * Defaults to the width defined by the position of the character.
   */
  width: number;

  /**
   * The height of the container.
   * Defaults to the height defined by the position of the character.
   */
  height: number;

  /**
   * The padding inside the container, specifying space between the container border and its content.
   * @default { top: 50, bottom: 50, right: 50, left: 50 }
   */
  padding?: {
    top?: number;
    bottom?: number;
    right?: number;
    left?: number;
  };

  /**
   * The total number of units to be rendered within the container.
   * @default 250
   */
  count?: number;

  /**
   * 每个symbol代表多少数量
   * @default 1
   */
  countPerSymbol?: number;

  /**
   * The gap between units, represented as a percentage of the unit's width and height.
   * The first value specifies the horizontal gap, and the second value specifies the vertical gap.
   * @default [0.5, 0.5]
   */
  gap?: [number, number];

  // 定义了从什么区间到什么区间是什么样式
  units: {
    style: ISymbolGraphicAttribute;
    range: [number, number];
  }[];

  /**
   * The aspect ratio of the units, defined as width divided by height.
   * @default 1
   */
  aspect?: number;

  /**
   * The direction in which units are laid out within the container.
   * @default 'horizontal'
   */
  direction?: 'horizontal' | 'vertical';

  duration?: number;
}

使用案例如下: