- 由 wyn_writer创建, 最后修改于七月 27, 2022
1. 内容概述
Visual api 用于呈现自定义的可视化效果。它提供了一些有用的界面帮助作者做一些工作,例如显示提示、交叉筛选、管理选择状态和 i18n。
2. Interface
interface IFilterTarget { // dataset: string; column: string; } enum VisualFilterType { Basic = 'basic', Advanced = 'advanced', Tuple = 'tuple', } interface IFilterBase { target: IFilterTarget; filterType: VisualFilterType; } enum BasicFilterOperator { In = 'In', NotIn = 'NotIn', } interface IBasicFilter extends IFilterBase { operator: BasicFilterOperator; values: any[]; } enum AdvancedFilterOperator { LessThan = 'LessThan', LessThanOrEqual = 'LessThanOrEqual', GreaterThan = 'GreaterThan', GreaterThanOrEqual = 'GreaterThanOrEqual', } interface IAdvancedFilterCondition { value: any; operator: AdvancedFilterOperator; } enum AdvancedFilterLogicalOperator { And = 'And', Or = 'Or', } interface IAdvancedFilter extends IFilterBase { conditions: IAdvancedFilterCondition[]; logicalOperator: AdvancedFilterLogicalOperator; } type ITuple = { value: any }[]; interface ITupleFilter { target: IFilterTarget[]; filterType: VisualFilterType; operator: BasicFilterOperator; values: ITuple[]; } type IFilter = IBasicFilter | IAdvancedFilter | ITupleFilter; interface IVisualUpdateOptions { isViewer: boolean; isMobile: boolean; isEditing: boolean; isFocus: boolean; dataViews: any; updateType: string; properties: any; // docTheme: RuntimeNS.IDocTheme; language: string; scale: number; filters: IFilter[]; }
VisualUpdateOption是可视化更新时的数据包(包含聚合更改或属性更改)。
- isViewer:表示在预览仪表板时是否呈现该组件。
- isMobile::表示是否在移动端显示该组件。
- isEditing:表示组件的编辑状态。当触发了编辑操作时,值为true。
- isFocus: 表示组件的聚焦状态。当触发了聚焦操作时,值为true。
- dataViews: 在capabilities.json中定义的计算数据视图(dataViewMapping)。
- properties: 在 capabilities.json 中定义的属性模型(options.visual)。
- docTheme: 选择的文档主题。
- language: 当前语言。
- scale: 比例因子。
- filters: 用来影响其他组件。
3. Visual
要实现的主类。
declare class WynVisual { static Models = { Filter: { BasicFilter, AdvancedFilter, TupleFilter, }, }; static Enums = { FilterType, BasicFilterOperator, AdvancedFilterOperator, AdvancedFilterLogicalOperator, UpdateType }; constructor(dom: HTMLDivElement, host: VisualNS.VisualHost, updateOptions: VisualNS.IVisualUpdateOptions); update(options: VisualNS.IVisualUpdateOptions): void; getInspectorHiddenState(updateOptions: VisualNS.IVisualUpdateOptions): string[]; getActionBarHiddenState(updateOptions: VisualNS.IVisualUpdateOptions): string[]; getColorAssignmentConfigMapping(dataViews: VisualNS.IDataView[]): VisualNS.IColorAssignmentConfigMapping; onResize(): void; onDestroy(): void; }
更新将调用每个聚合更改和属性更改。
getInspectorHiddenState返回值路径列表,表示此属性不应显示在属性设置中。
- getActionBarHiddenState返回值路径列表,表示此属性不应显示在操作栏中。
- getColorAssignmentConfigMapping 返回一个颜色赋值映射对象,表示应该给哪个值赋值颜色。
- 在调整组件对象的大小时将调用onResize。
- 卸载时将调用onDestroy。
- 静态属性Models和Enums为自定义可视化插件提供一些类或枚举。
4. Color Assignment 示例
//capabilities.json { options: { visual: [ { properties: [{ "name": "mainColorAssignment", "type": "ColorAssignment", "displayName": "Enable Color Assignment", "defaultValue": true }] } ] } } // visual.ts export default class Visual extends WynVisual { //...others method private render() { //... const circle = node.append('circle') .attr('r', function (d: any) { return d.r; }) .style('fill', function (d: any) { //consume the color assignment property if (options.mainColorAssignment) { return options.mainColorAssignment[d.data.size] || color(d.package); } else { return color(d.package); } }); //... } public getColorAssignmentConfigMapping(dataViews: VisualNS.IDataView[]): VisualNS.IColorAssignmentConfigMapping { if (!dataViews.length) { return null; } const plain = dataViews[0].plain; const colorProfile = plain.profile.series.values[0]; const dimProfile = plain.profile.dimensions.values[0]; if (!colorProfile || !dimProfile) { return null; } // make the values in series color assignmentable const colorValues = plain.data.map(d => d[colorProfile.display]); return { mainColorAssignment: { values: Array.from(new Set(colorValues)), type: 'dimension', columns: [colorProfile], }, }; } }
5. VisualHost
eventService
EventService 用于调度一些渲染事件,并且用户可以注册操作栏自定义事件处理程序。
方法 | interface | 说明 | 示例 |
---|---|---|---|
renderStart | renderStart(): void | 用于通知仪表板该组件的渲染状态 | // Update method will be invoke while something changed. public update(options: VisualNS.IVisualUpdateOptions) { this.visualHost.eventService.renderStart(); try { this.render(); // render method is used to update UI this.visualHost.eventService.renderFinish(); } catch (e) { this.visualHost.eventService.renderError(); } } |
renderError | renderError(): void | ||
renderFinish | renderFinish(): void | ||
registerOnCustomEventCallback | registerOnCustomEventCallback(fn: (name: string) => void) | 用于注册操作栏事件 callback 是自定义事件处理程序 | // Register the callback in visual constructor. It will print the action name when you click the corresponding button on action bar. this.visualHost.eventService.registerOnCustomEventCallback((name: string) => { console.log(name); }); |
localizationManager
LocalizationManger 用于在此处提供一些本地化的字符串。
方法 | interface | 说明 | 示例 |
---|---|---|---|
getDisplay | getDisplay(displayNameKey: string): string | 返回本地化字符串 | const noDataText = this.visualHost.localizationManager.getDisplay('NoData'); this.container.textContent = noDataText; |
toolTipService
TooltipService 用来控制提示的显示/隐藏。
方法 | interface | 说明 | 示例 |
---|---|---|---|
show | interface ITootipPosition { x: number; y: number; } interface ILabelFields { label: string; value: string; } interface ITooltipConfig { position: ITootipPosition; title?: string; fields?: ILabelFields[]; selectionId?: SelectionId; selected?: SelectionId[]; menu?: boolean; } show(config: VisualImplNS.ITooltipConfig): void | 显示提示(tooltip)
| // this function will bind to the chart mouse enter event. It will show a tooltip with title, fields, selection info and menu buttons. private mouseEnterHandler = (node: any) => { this.visualHost.toolTipService.show({ position: { x: d3.event.x, y: d3.event.y, }, title: node.data.color, fields: [{ label: this.valueField, value: node.data.value, }], selectionId: node.data.selectionId, selected: this.selectionManager.getSelectionIds(), menu: true, }); } |
hide | hide(): void | 隐藏提示(tooltip) | // hide the tooltip when the mouse leave the chart shape private mouseLeaveHandler = () => { this.visualHost.toolTipService.hide(); } |
move | move(pos: VisualImplNS.ITootipPosition): void | 移动提示(tooltip) | // update the tooltip position while mouse move on chart private mouseMoveHandler = (e: any) => { this.visualHost.toolTipService.move({ x: d3.event.x, y: d3.event.y, }); } |
propertyService
PropertyService 用于更新在 capabilities.json 中定义的属性(options.visual)。
方法 | interface | 说明 | 示例 |
---|---|---|---|
setProperty | setProperty(propertyName: string, value: any): void | 更新属性 | // when the button be clicked. The property of showBorder will be change. private onToggle = () => { this.visualHost.propertyService.setProperty('showBorder', !this.properties.showBorder); } |
formatService
FormatService用来设置格式化。
方法 | inferface | 说明 | 示例 |
---|---|---|---|
isAutoDisplayUnit | isAutoDisplayUnit(displayUnit: DisplayUnit): boolean | 判断 displayUnit 是否为DisplayUnit.Auto | enum DisplayUnit { Auto = 'auto', None = 'none', Hundreds = 'hundreds', Thousands = 'thousands', TenThousand = 'tenThousands', HundredThousand = 'hundredThousand', Millions = 'millions', TenMillion = 'tenMillion', HundredMillion = 'hundredMillion', Billions = 'billions', Trillions = 'trillions', } |
getAutoDisplayUnit | getAutoDisplayUnit(values: number[]): DisplayUnit | 当displayUnit为DisplayUnit.Auto时,得到realDisplayUnit。 | |
format | format(format: string, value: number): string | 格式化数值。 第一个参数格式来自 Format Property或 DataView Profile 项目。 默认 displayUnit 为 DisplayUnit.None; | const formatService = this.visualHost.formatService; const { displayUnit, labelFormat } = updateOptions.properties; let realDisplayUnit = displayUnit; if (formatService.isAutoDisplayUnit(displayUnit)) { const values = dataPoints.map(dataPoint => dataPoint.value); realDisplayUnit = formatService.getAutoDisplayUnit(values); } dataPoints.forEach((dataPoint) => { // prepare a formated text for label. dataPoint.labelText = formatService.format(labelFormat, dataPoint.value, realDisplayUnit); }) |
selectionService
方法 | interface | 说明 | 示例 |
---|---|---|---|
createSelectionManager | createSelectionManager(): SelectionManager | 创建一个SelectionManager。 | this.selectionManager = this.visualHost.selectionService.createSelectionManager(); |
createSelectionId | createSelectionId(): SelectionId | 为数据点创建SelectionId。 | const selectionId = this.visualHost.selectionService.createSelectionId(); |
SelectionId
SelectionId就像一个向量。它包含绑定到它的维度和度量。将 selection id 附加到数据点后,在图表中单击时就可以触发。
还可以用SelectionId 做很多事情,例如交叉筛选、呈现选择状态、显示提示信息和链接文档。
方法 | interface | 说明 | 示例 |
---|---|---|---|
withMeasure | withMeasure(profile: RuntimeNS.IFieldProfile): SelectionId | 将度量字段绑定到selection id。 | public update(options: VisualNS.IVisualUpdateOptions) { console.log(options); const dataView = options.dataViews[0]; // convert the data view to datas which can be used in chart if (dataView) { const plainData = dataView.plain; // get the fields via different data role. const valueField = plainData.profile.values.values[0].display; const sizeField = plainData.profile.series.values[0].display; const colorField = plainData.profile.dimensions.values[0].display; const items = plainData.data.reduce((result: any, item: any, i: number) => { if (item[valueField]) { // create a selection id for a data point. const selectionId = this.visualHost.selectionService.createSelectionId(); // set the dimensions of the selection id. This will affect on the crossfilter result and others. selectionId .withDimension(plainData.profile.dimensions.values[0], item) .withDimension(plainData.profile.series.values[0], item); // attach the selection id on data model. so that you can access it on hit test result.push({ size: (item[sizeField] || '-') + '', value: item[valueField], color: (item[colorField] || '') + '', id: i, selectionId, }); } return result; }, []); // refresh the chart with new data items this.render(items); } }; |
withDimension | withDimension(profile: RuntimeNS.IFieldProfile, dataPoint: RuntimeNS.IDataPoint): SelectionId | 将纬度字段绑定到selection id。 |
SelectionManager
Selection manager 用于管理选择状态,并触发交叉筛选。
方法 | interface | 说明 | 示例 |
---|---|---|---|
setSelectionIds | setSelectionIds(ids: Array<SelectionId>) | setter | |
getSelectionIds | getSelectionIds(): Array<SelectionId> | getter | |
getCount | getCount(): number | 获取所选selectionId的计数 | |
select | select(id: SelectionId | Array<SelectionId>, multiSelect: boolean = false): Promise<void> | 选择一个或多个selectionId。multiselect表示应该支持选择多个selectionId。它返回一个许可,并将在选择完成后解决。 | private clickHandler = (node: any) => { d3.event.stopPropagation(); // get selection id which bind on data model const selectionId = node.data.selectionId // if it has been selected, unselect it. if (!this.selectionManager.contains(selectionId)) { this.selectionManager.select(selectionId, true); } else { this.selectionManager.clear(selectionId); } // show tooltip this.visualHost.toolTipService.show({ position: { x: d3.event.x, y: d3.event.y, }, title: node.data.color, fields: [{ label: this.valueField, value: node.data.value, }], selectionId: node.data.selectionId, // the field which display in tooltip selected: this.selectionManager.getSelectionIds(), // all the selected selectionId, will be use in keep/exclude and drill. menu: true, }); } |
clear | clear(id?: SelectionId): Promise<void> | 清除选中的或者全部 SelectionId。 | |
registerOnSelectCallback | registerOnSelectCallback(onSelectCallback: (ids: SelectionId[]) => void) | Register a callback, the callback will be invoke after every selection change. 注册 callback,每次选择更改后都将调用callback。 | this.selectionManager.registerOnSelectCallback(() => { // while the selection state changed, should update ui, to highlight the shape which has been selected this.render(); }); |
contains | contains(id: SelectionId): boolean | 检查目标id 是否被选中。 | |
isEmpty | isEmpty(): boolean | 检查 selection manager 是否包含选中的selectionId。 |
configurationManager
ConfigurationManger should work with the configuration in visual.json. If you defined a configuration item. you can access it via this.
ConfigurationManger 应与 visual.json中的配置项一起使用。如果定义一个配置项目,就可以通过这个进行访问。
method | interface | description | sample |
---|---|---|---|
get | get(key: string): string | 获取配置项 | // to access the amapToken in configuration const amapToken = this.visualHost.configurationManager.get('amapToken'); |
assetsManager
AssetsManager 应与 visual.json 中的 assets 一起使用。当前您可以在assets 中定义图片资源,它们将被打包。可以通过这个访问图片。
method | interface | description | sample |
---|---|---|---|
getImage | getImage(imageName: string): string | 返回base64字符串 | // to access the image in assets this.container.style.backgroundImage = `url(${host.assetsManager.getImage('testImg')})`; |
commandService
CommandService 提供了一些仪表板命令,例如切换选项卡容器。
方法 | interface | 说明 | 示例 |
---|---|---|---|
execute | interface ICommandDescription { name: string; payload: { target: string; }; } execute(commandsDesc: Array<ICommandDescription>): void | 执行命令 | // run a command to active a tab container this.visualHost.commandService.execute([{ name: 'SelectTab', payload: { target: 'myTabContainer', index: 0, } }]) |
SwitchTab
Command Name | 描述 | 说明 | 示例 |
---|---|---|---|
SwitchTab | 用户可以使用这个命令来切换选项卡组件中的标签。 |
|
|
SwitchPage
Command Name | 描述 | 说明 | 示例 |
---|---|---|---|
SwitchPage | 用户可以使用这个命令来切换仪表板页面。 |
|
|
Keep/Exclude
Command Name | 描述 | 说明 | 示例 |
---|---|---|---|
Keep/Exclude | 用户可以使用这个命令来执行保留或排除操作。 | // Keep & Exclude this.host.commandService.execute([{ name: 'Keep', // or 'Exclude' payload: { selectionIds, }, }]); | — |
Drill/Jump
Command Name | 描述 | 说明 | 示例 |
---|---|---|---|
Drill/Jump | 用户可以使用这个命令来执行钻取或跳转操作。 | // Drill & Jump this.host.commandService.execute([{ name: 'Drill', // or 'Jump' payload: { selectionIds, // optional position: { x: event.clientX, y: event.clientY }, }, }]); | — |
filterService
FilterService用于派遣过滤器影响其他场景。过滤器将被列在 updateOptions.filters 中。
目前,您可以创建Basic,Tuple和Advanced Filter。
首先,你应该通过设置options.common.filter为 "true "来启用过滤器。
方法 | interface | description | 示例 |
---|---|---|---|
applyFilter | interface IFilterTarget { // dataset: string; column: string; } enum VisualFilterType { Basic = 'basic', Advanced = 'advanced', Tuple = 'tuple', } interface IFilterBase { target: IFilterTarget; filterType: VisualFilterType; } applyFilter(filter: BasicFilter | TupleFilter | AdvancedFilter): void | 这个方法用来唤醒过滤器。 | dom.addEventListener('click', (e) => { const it = e.target as HTMLLIElement; if (it.hasAttribute('data-value')) { const val = it.dataset.value; if (this.filter.contains(val)) { this.filter.remove(val); } else { this.filter.add(val); } host.filterService.applyFilter(this.filter); } else if (it.getAttribute('data-role') === 'toggle') { this.filter.setOperator(this.filter.getOperator() === Enums.BasicFilterOperator.In ? Enums.BasicFilterOperator.NotIn : Enums.BasicFilterOperator.In ); host.filterService.applyFilter(this.filter); } }) |
clean | clean(): void | 清除过滤器 | host.filterService.clean(); |
BasicFilter
与内置的标签列表类似,相当于SQL:
SELECT * FROM table WHERE Dept IN ( DTD1 , DTD2 , DTD3 )
方法 | interface | description | 示例 |
---|---|---|---|
constructor | enum BasicFilterOperator { In = 'In', NotIn = 'NotIn', } interface IBasicFilter extends IFilterBase { operator: BasicFilterOperator; values: any[]; } constructor(targetProfile: RuntimeNS.IFieldProfile, operator: BasicFilterOperator = BasicFilterOperator.In, values: any[] = []) | targetProfile是数据视图中的过滤区域。 | const profile = dv.plain.profile.dimensions.values[0]; const BasicFilter = WynVisual.Models.Filter.BasicFilter; const filter = new BasicFilter(profile); |
setOperator/getOperator | setOperator(operator: BasicFilterOperator) getOperator(): BasicFilterOperator | this.filter.setOperator(this.filter.getOperator() === WynVisual.Enums.BasicFilterOperator.In ? WynVisual.Enums.BasicFilterOperator.NotIn : WynVisual.Enums.BasicFilterOperator.In ) | |
setValues/getValues | getValues(): any[] setValues(vals: any[]) | filter.setValues(['DTD1', 'DTD2']) | |
toJSON/fromJSON | toJSON(): BasicNS.IBasicFilter fromJSON(obj: BasicNS.IBasicFilter) | 从updateOptions.filters中恢复过滤器。 | const profile = dv.plain.profile.dimensions.values[0]; const filter = new BasicFilter(profile); filter.fromJSON(options.filters[0]); |
contains | contains(value: any): boolean | 是否包含在过滤器中的值 | if (this.filter.contains(val)) { this.filter.remove(val); } else { this.filter.add(val); } |
remove/add | remove(val: any) add(val: any) | 添加或移除值 | if (this.filter.contains(val)) { this.filter.remove(val); } else { this.filter.add(val); } |
isEmpty | isEmpty(): boolean |
TupleFilter
它类似于BasicFilter,但它可以在多维度时生效.相当于SQL:
SELECT * FROM DataTable WHERE ( Team = "DTD1" AND Prod = "Wyn" ) OR ( Team = "DTD2" AND Prod = "SpreadJS" );
方法 | interface | description | 示例 |
---|---|---|---|
constructor | type ITuple = { value: any }[]; interface ITupleFilter { target: IFilterTarget[]; filterType: VisualFilterType; operator: BasicFilterOperator; values: ITuple[]; } constructor(targetProfiles: RuntimeNS.IFieldProfile[], operator: BasicFilterOperator = BasicFilterOperator.In, values: BasicNS.ITuple[] = []) | const dimensionsProfiles = dv.plain.profile.dimensions.values; const filter = new TupleFilter(dimensionsProfiles); | |
setOperator/getOperator | setOperator(operator: BasicFilterOperator) getOperator(): BasicFilterOperator | ||
getValues/setValues | getValues(): BasicNS.ITuple[] setValues(vals: BasicNS.ITuple[]) | ||
toJSON/fromJSON | toJSON(): BasicNS.ITupleFilter fromJSON(obj: BasicNS.ITupleFilter) | ||
contains | contains(tuple: BasicNS.ITuple): boolean | ||
remove/add | remove(tuple: BasicNS.ITuple) add(tuple: BasicNS.ITuple) | ||
createTuple | createTuple(dp: RuntimeNS.IDataPoint, depth?: number): BasicNS.ITuple | 按选定的数据点创建元组 | this.items = dv.plain.data.reduce((res, dp) => { const label = Object.keys(dp).map(key => dp[key]).join('-'); const tuple = filter.createTuple(dp); res.push({ label, className: filter.contains(tuple) ? 'selected' : '', tuple: JSON.stringify(tuple), }); return res; }, []); |
isEmpty | isEmpty(): boolean |
AdvancesFilter
高级过滤器提供了lessThan、greatThan和其他运算符来查询,目前不支持逻辑运算符 "Or"。
方法 | interface | description | 示例 |
---|---|---|---|
constructor | enum AdvancedFilterLogicalOperator { And = 'And', Or = 'Or', } enum AdvancedFilterOperator { LessThan = 'LessThan', LessThanOrEqual = 'LessThanOrEqual', GreaterThan = 'GreaterThan', GreaterThanOrEqual = 'GreaterThanOrEqual', } interface IAdvancedFilterCondition { value: any; operator: AdvancedFilterOperator; } interface IAdvancedFilter extends IFilterBase { conditions: IAdvancedFilterCondition[]; logicalOperator: AdvancedFilterLogicalOperator; } constructor(targetProfile: RuntimeNS.IFieldProfile, logicalOperator: AdvancedFilterLogicalOperator = AdvancedFilterLogicalOperator.And, conditions: BasicNS.IAdvancedFilterCondition[] = []) | ||
setLogicalOperator/getLogicalOperator | setLogicalOperator(operator: AdvancedFilterLogicalOperator) getLogicalOperator(): AdvancedFilterLogicalOperator | ||
setConditions/getConditions | setConditions(conditions: BasicNS.IAdvancedFilterCondition[]) getConditions(): BasicNS.IAdvancedFilterCondition[] | this.filter.setConditions([{ value: min, operator: this.includeMinEle.checked ? Enums.AdvancedFilterOperator.GreaterThanOrEqual : Enums.AdvancedFilterOperator.GreaterThan, }, { value: max, operator: this.includeMaxEle.checked ? Enums.AdvancedFilterOperator.LessThanOrEqual : Enums.AdvancedFilterOperator.LessThan, }]); | |
toJSON/fromJSON | toJSON(): BasicNS.IAdvancedFilter fromJSON(obj: BasicNS.IAdvancedFilter) | ||
remove/add | remove(condtion: BasicNS.IAdvancedFilterCondition) add(condtion: BasicNS.IAdvancedFilterCondition) | ||
isEmpty | isEmpty(): boolean |
- 无标签