mirror of
https://gitee.com/antv/g6.git
synced 2024-12-02 03:38:20 +08:00
feat: slider timebar component
This commit is contained in:
parent
973d9cf2ef
commit
1bb68f16cd
@ -69,6 +69,8 @@
|
||||
"**/*.{js,ts,tsx}": "npm run lint-staged:js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/color-util": "^2.0.5",
|
||||
"@antv/component": "^0.6.1",
|
||||
"@antv/dom-util": "^2.0.1",
|
||||
"@antv/event-emitter": "~0.1.0",
|
||||
"@antv/g-base": "^0.4.1",
|
||||
|
@ -20,6 +20,7 @@ const Bundling = Plugins.Bundling;
|
||||
const Menu = Plugins.Menu;
|
||||
const ToolBar = Plugins.ToolBar
|
||||
const Tooltip = Plugins.Tooltip
|
||||
const TimeBar = Plugins.TimeBar
|
||||
|
||||
export {
|
||||
registerNode,
|
||||
@ -38,7 +39,8 @@ export {
|
||||
registerBehavior,
|
||||
Algorithm,
|
||||
ToolBar,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
TimeBar
|
||||
};
|
||||
|
||||
export default {
|
||||
@ -59,6 +61,7 @@ export default {
|
||||
Menu: Plugins.Menu,
|
||||
ToolBar: Plugins.ToolBar,
|
||||
Tooltip: Plugins.Tooltip,
|
||||
TimeBar,
|
||||
Algorithm,
|
||||
Arrow,
|
||||
Marker
|
||||
|
@ -4,6 +4,7 @@ import Minimap from './minimap';
|
||||
import Bundling from './bundling';
|
||||
import ToolBar from './toolBar'
|
||||
import Tooltip from './tooltip'
|
||||
import TimeBar from './timeBar'
|
||||
|
||||
export default {
|
||||
Menu,
|
||||
@ -11,5 +12,6 @@ export default {
|
||||
Minimap,
|
||||
Bundling,
|
||||
ToolBar,
|
||||
Tooltip
|
||||
Tooltip,
|
||||
TimeBar
|
||||
};
|
||||
|
240
src/plugins/timeBar/index.ts
Normal file
240
src/plugins/timeBar/index.ts
Normal file
@ -0,0 +1,240 @@
|
||||
import modifyCSS from '@antv/dom-util/lib/modify-css';
|
||||
import createDOM from '@antv/dom-util/lib/create-dom';
|
||||
import isString from '@antv/util/lib/is-string'
|
||||
import { IGroup } from '@antv/g-base'
|
||||
import { Canvas } from '@antv/g-canvas'
|
||||
import { Slider } from '@antv/component'
|
||||
import { ShapeStyle, GraphData } from '../../types';
|
||||
import Base, { IPluginBaseConfig } from '../base';
|
||||
import { IGraph } from '../../interface/graph';
|
||||
|
||||
interface Data {
|
||||
date: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
originValue: number[];
|
||||
value: number[];
|
||||
target: IGroup;
|
||||
}
|
||||
|
||||
interface TrendConfig {
|
||||
readonly data: Data[];
|
||||
// 样式
|
||||
readonly smooth?: boolean;
|
||||
readonly isArea?: boolean;
|
||||
readonly backgroundStyle?: ShapeStyle;
|
||||
readonly lineStyle?: ShapeStyle;
|
||||
readonly areaStyle?: ShapeStyle;
|
||||
};
|
||||
|
||||
type TimeBarOption = Partial<{
|
||||
// position size
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
|
||||
readonly backgroundStyle: ShapeStyle;
|
||||
readonly foregroundStyle: ShapeStyle;
|
||||
readonly handlerStyle: ShapeStyle;
|
||||
readonly textStyle: ShapeStyle;
|
||||
// 允许滑动位置
|
||||
readonly minLimit: number;
|
||||
readonly maxLimit: number;
|
||||
// 初始位置
|
||||
readonly start: number;
|
||||
readonly end: number;
|
||||
// 滑块文本
|
||||
readonly minText: string;
|
||||
readonly maxText: string;
|
||||
|
||||
readonly trend: TrendConfig;
|
||||
}>;
|
||||
|
||||
interface TimeBarConfig extends IPluginBaseConfig {
|
||||
width?: number;
|
||||
height?: number;
|
||||
timebar: TimeBarOption;
|
||||
rangeChange?: (graph: IGraph, min: number, max: number) => void;
|
||||
}
|
||||
|
||||
export default class TimeBar extends Base {
|
||||
private cacheGraphData: GraphData
|
||||
|
||||
constructor(cfg?: TimeBarConfig) {
|
||||
super(cfg);
|
||||
}
|
||||
|
||||
public getDefaultCfgs(): TimeBarConfig {
|
||||
return {
|
||||
width: 400,
|
||||
height: 50,
|
||||
rangeChange: null,
|
||||
timebar: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
width: 400,
|
||||
height: 26,
|
||||
minLimit: 0.05,
|
||||
maxLimit: 0.95,
|
||||
start: 0.1,
|
||||
end: 0.9,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public init() {
|
||||
const timeBarConfig: TimeBarOption = this.get('timebar')
|
||||
const { trend = {} as TrendConfig } = timeBarConfig
|
||||
const { data = [] } = trend
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
console.warn('TimeBar 中没有传入数据')
|
||||
return
|
||||
}
|
||||
|
||||
const container = this.get('container')
|
||||
|
||||
let timebar
|
||||
if (!container) {
|
||||
timebar = createDOM(`<div id='g6-component-timebar'></div>`)
|
||||
modifyCSS(timebar, { position: 'absolute' });
|
||||
document.body.appendChild(timebar)
|
||||
} else if (isString(container)) {
|
||||
timebar = createDOM(`<div id=${container}></div>`)
|
||||
modifyCSS(timebar, { position: 'absolute' });
|
||||
document.body.appendChild(timebar)
|
||||
} else {
|
||||
timebar = container
|
||||
}
|
||||
|
||||
this.set('timeBarContainer', timebar)
|
||||
|
||||
this.initTimeBar(timebar)
|
||||
}
|
||||
|
||||
private initTimeBar(container: HTMLDivElement) {
|
||||
const width = this.get('width')
|
||||
const height = this.get('height')
|
||||
const canvas = new Canvas({
|
||||
container,
|
||||
width,
|
||||
height,
|
||||
});
|
||||
|
||||
const group = canvas.addGroup({
|
||||
id: 'timebar-plugin',
|
||||
})
|
||||
|
||||
const timeBarConfig: TimeBarOption = this.get('timebar')
|
||||
const { trend = {} as TrendConfig , ...option } = timeBarConfig
|
||||
|
||||
const config = {
|
||||
container: group,
|
||||
minText: option.start,
|
||||
maxText: option.end,
|
||||
...option
|
||||
}
|
||||
|
||||
// 是否显示 TimeBar 根据是否传入了数据来确定
|
||||
const { data = [], ...trendOption } = trend
|
||||
|
||||
const trendData = data.map(d => d.value)
|
||||
|
||||
config['trendCfg'] = {
|
||||
...trendOption,
|
||||
data: trendData
|
||||
}
|
||||
|
||||
config.minText = data[0].date
|
||||
config.maxText = data[data.length - 1].date
|
||||
|
||||
this.set('trendData', data)
|
||||
|
||||
console.log('配置项', config)
|
||||
|
||||
const slider = new Slider(config)
|
||||
|
||||
slider.init();
|
||||
slider.render()
|
||||
|
||||
this.set('slider', slider)
|
||||
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* 当滑动时,最小值和最大值会变化,变化以后触发相应事件
|
||||
*/
|
||||
private bindEvent() {
|
||||
const graph: IGraph = this.get('graph')
|
||||
const slider = this.get('slider')
|
||||
const rangeChange = this.get('rangeChange')
|
||||
const trendData: Data[] = this.get('trendData')
|
||||
slider.on('valuechanged', (evt: Callback) => {
|
||||
const { value } = evt
|
||||
|
||||
const min = Math.round(trendData.length * value[0])
|
||||
let max = Math.round(trendData.length * value[1])
|
||||
max = max > trendData.length ? trendData.length : max
|
||||
const minText = trendData[min].date
|
||||
const maxText = trendData[max].date
|
||||
|
||||
slider.set('minText', minText)
|
||||
slider.set('maxText', maxText)
|
||||
|
||||
if (rangeChange) {
|
||||
rangeChange(graph, minText, maxText)
|
||||
} else {
|
||||
// 自动过滤数据,并渲染 graph
|
||||
const graphData = graph.save() as GraphData
|
||||
|
||||
if (!this.cacheGraphData) {
|
||||
this.cacheGraphData = graphData
|
||||
}
|
||||
|
||||
// 过滤不在 min 和 max 范围内的节点
|
||||
const filterData = this.cacheGraphData.nodes.filter((d: any) => d.date >= minText && d.date <= maxText)
|
||||
|
||||
const nodeIds = filterData.map(node => node.id)
|
||||
|
||||
// 过滤 source 或 target 不在 min 和 max 范围内的边
|
||||
const fileterEdges = this.cacheGraphData.edges.filter(edge => nodeIds.includes(edge.source) && nodeIds.includes(edge.target))
|
||||
|
||||
graph.changeData({
|
||||
nodes: filterData,
|
||||
edges: fileterEdges
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public show() {
|
||||
const slider = this.get('slider')
|
||||
slider.show()
|
||||
}
|
||||
|
||||
public hide() {
|
||||
const slider = this.get('slider')
|
||||
slider.hide()
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.cacheGraphData = null
|
||||
|
||||
const slider = this.get('slider')
|
||||
|
||||
if (slider) {
|
||||
slider.off('valuechanged')
|
||||
slider.destroy()
|
||||
}
|
||||
|
||||
const timeBarContainer = this.get('timeBarContainer')
|
||||
if (timeBarContainer) {
|
||||
document.body.removeChild(timeBarContainer);
|
||||
}
|
||||
}
|
||||
}
|
91
tests/unit/plugins/timebar-spec.ts
Normal file
91
tests/unit/plugins/timebar-spec.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import G6 from '../../../src';
|
||||
const div = document.createElement('div');
|
||||
div.id = 'timebar-plugin';
|
||||
document.body.appendChild(div);
|
||||
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
label: 'node1',
|
||||
x: 100,
|
||||
y: 100
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
label: 'node2',
|
||||
x: 150,
|
||||
y: 300
|
||||
}
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
source: 'node1',
|
||||
target: 'node2'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
for(let i = 0; i < 100; i++) {
|
||||
const id = `node-${i}`
|
||||
data.nodes.push({
|
||||
id,
|
||||
label: `node${i}`,
|
||||
date: `2020${i}`,
|
||||
value: Math.round(Math.random() * 300)
|
||||
})
|
||||
|
||||
const edgeId = i + 3
|
||||
data.edges.push({
|
||||
source: `node-${Math.round(Math.random() * 90)}`,
|
||||
target: `node-${Math.round(Math.random() * 90)}`
|
||||
})
|
||||
}
|
||||
|
||||
describe('tooltip', () => {
|
||||
it('tooltip with default', () => {
|
||||
const timeBarData = []
|
||||
|
||||
for(let i = 0; i < 100; i++) {
|
||||
timeBarData.push({
|
||||
date: `2020${i}`,
|
||||
value: Math.round(Math.random() * 300)
|
||||
})
|
||||
}
|
||||
const timebar = new G6.TimeBar({
|
||||
timebar: {
|
||||
trend: {
|
||||
data: timeBarData,
|
||||
isArea: false,
|
||||
smooth: true,
|
||||
}
|
||||
}
|
||||
});
|
||||
const tooltip = new G6.Tooltip()
|
||||
|
||||
const graph = new G6.Graph({
|
||||
container: div,
|
||||
width: 500,
|
||||
height: 500,
|
||||
plugins: [timebar, tooltip],
|
||||
modes: {
|
||||
default: ['drag-node', 'zoom-canvas', 'drag-canvas']
|
||||
},
|
||||
defaultEdge: {
|
||||
style: {
|
||||
lineAppendWidth: 20
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
graph.data(data)
|
||||
graph.render()
|
||||
|
||||
const timebarPlugin = graph.get('plugins')[0]
|
||||
console.log(timebarPlugin)
|
||||
// expect(timebarPlugin.get('offset')).toBe(6)
|
||||
// expect(timebarPlugin.get('tooltip').outerHTML).toBe(`<div class="g6-component-tooltip" style="position: absolute; visibility: hidden;"></div>`)
|
||||
|
||||
// graph.destroy()
|
||||
})
|
||||
});
|
Loading…
Reference in New Issue
Block a user