mirror of
https://gitee.com/antv/g6.git
synced 2024-12-02 11:48:29 +08:00
feat: add trend teimebar component
This commit is contained in:
parent
00e70ed33b
commit
426d07e92a
@ -74,10 +74,10 @@
|
||||
"@antv/component": "^0.6.1",
|
||||
"@antv/dom-util": "^2.0.1",
|
||||
"@antv/event-emitter": "~0.1.0",
|
||||
"@antv/g-base": "^0.5.0",
|
||||
"@antv/g-canvas": "^0.5.0",
|
||||
"@antv/g-base": "^0.5.1",
|
||||
"@antv/g-canvas": "^0.5.1",
|
||||
"@antv/g-math": "^0.1.1",
|
||||
"@antv/g-svg": "^0.5.0",
|
||||
"@antv/g-svg": "^0.5.1",
|
||||
"@antv/hierarchy": "^0.6.2",
|
||||
"@antv/matrix-util": "^2.0.4",
|
||||
"@antv/path-util": "^2.0.3",
|
||||
@ -130,4 +130,4 @@
|
||||
"webpack-cli": "^3.3.10",
|
||||
"worker-loader": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
368
src/plugins/timeBar/controllerBtn.ts
Normal file
368
src/plugins/timeBar/controllerBtn.ts
Normal file
@ -0,0 +1,368 @@
|
||||
/**
|
||||
* @file 基于 G 的播放轴组件
|
||||
*/
|
||||
import { IGroup, IShape } from '@antv/g-base';
|
||||
import { deepMix } from '@antv/util'
|
||||
import Button from './timeButton';
|
||||
import { ShapeStyle } from '../../types';
|
||||
import { Arrow } from '../../shape'
|
||||
|
||||
export const TIMELINE_START = 'timelinestart';
|
||||
export const TIMELINE_CHANGE = 'timelinechange';
|
||||
export const TIMELINE_END = 'timelineend';
|
||||
|
||||
const DEFAULT_RECT_FILL = '#ccc'
|
||||
const DEFAULT_RECT_STROKE = 'green'
|
||||
const DEFAULT_PLAYBTN_STYLE = {
|
||||
fill: '#607889',
|
||||
stroke: '#607889'
|
||||
}
|
||||
|
||||
const DEFAULT_PREBTN_STYLE = {
|
||||
fill: '#fff'
|
||||
}
|
||||
|
||||
const DEFAULT_NEXTBTN_STYLE = {
|
||||
fill: 'green'
|
||||
}
|
||||
|
||||
const DEFAULT_CONTROLLER_CONFIG = {
|
||||
speed: 2,
|
||||
loop: false,
|
||||
fill: '#fff',
|
||||
stroke: '#fff',
|
||||
preBtnStyle: {
|
||||
fill: '#ccc',
|
||||
stroke: '#ccc'
|
||||
},
|
||||
nextBtnStyle: {
|
||||
fill: '#ccc',
|
||||
stroke: '#ccc'
|
||||
},
|
||||
playBtnStyle: {
|
||||
fill: '#fff',
|
||||
stroke: '#ccc'
|
||||
}
|
||||
}
|
||||
|
||||
export type ControllerCfg = Partial<{
|
||||
readonly group: IGroup;
|
||||
|
||||
readonly x?: number;
|
||||
readonly y?: number;
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
/** 播放速度,1 个 tick 花费时间 */
|
||||
readonly speed?: number;
|
||||
/** 是否循环播放 */
|
||||
readonly loop?: boolean;
|
||||
readonly fill?: string;
|
||||
readonly stroke?: string;
|
||||
readonly preBtnStyle?: ShapeStyle;
|
||||
readonly nextBtnStyle?: ShapeStyle;
|
||||
readonly playBtnStyle?: ShapeStyle;
|
||||
}>
|
||||
|
||||
/**
|
||||
* 参考示例
|
||||
* https://www.gapminder.org/tools/#$state$time$value=1870&delay:100;;&chart-type=bubbles
|
||||
*/
|
||||
export default class ControllerBtn {
|
||||
/** 是否处于播放状态 */
|
||||
private isPlay: boolean;
|
||||
|
||||
public playButton: Button;
|
||||
public preStepButton: IShape;
|
||||
public nextStepButton: IShape;
|
||||
private checkedIcon: IShape;
|
||||
private checkedText: IShape;
|
||||
|
||||
readonly controllerCfg?: ControllerCfg;
|
||||
|
||||
private group: IGroup;
|
||||
|
||||
private speedGroup: IGroup;
|
||||
private toggleGroup: IGroup;
|
||||
|
||||
private speedText: IShape;
|
||||
private speedPoint: IShape;
|
||||
private speedAxisY: number[];
|
||||
|
||||
// 当前播放速度
|
||||
private currentSpeed: number;
|
||||
|
||||
private currentType: 'signle' | 'range';
|
||||
|
||||
constructor(cfg: ControllerCfg) {
|
||||
this.controllerCfg = deepMix(
|
||||
{},
|
||||
cfg,
|
||||
DEFAULT_CONTROLLER_CONFIG
|
||||
);
|
||||
|
||||
this.group = cfg.group
|
||||
this.speedAxisY = []
|
||||
this.currentSpeed = this.controllerCfg.speed
|
||||
this.currentType = 'range'
|
||||
this.init()
|
||||
}
|
||||
|
||||
public init() {
|
||||
this.renderPlayButton();
|
||||
}
|
||||
|
||||
/** 获取播放键 marker path */
|
||||
private getNextMarkerPath(x, y, len) {
|
||||
return [
|
||||
['M', x, y - len],
|
||||
['L', x + len, y],
|
||||
['L', x, y + len],
|
||||
];
|
||||
}
|
||||
|
||||
private getPreMarkerPath(x, y, len) {
|
||||
return [
|
||||
['M', x, y - len],
|
||||
['L', x - len, y],
|
||||
['L', x, y + len],
|
||||
];
|
||||
}
|
||||
|
||||
private renderPlayButton() {
|
||||
const { controllerCfg } = this;
|
||||
const { width, height, x, y,
|
||||
fill = DEFAULT_RECT_FILL, stroke = DEFAULT_RECT_STROKE,
|
||||
playBtnStyle = DEFAULT_PLAYBTN_STYLE,
|
||||
preBtnStyle = DEFAULT_PREBTN_STYLE,
|
||||
nextBtnStyle = DEFAULT_NEXTBTN_STYLE } = controllerCfg
|
||||
|
||||
const r = height / 2 - 5;
|
||||
const realY = y + 10
|
||||
|
||||
// 绘制最外层的矩形包围框
|
||||
this.group.addShape('rect', {
|
||||
attrs: {
|
||||
x,
|
||||
y: realY,
|
||||
width,
|
||||
height,
|
||||
stroke,
|
||||
fill
|
||||
}
|
||||
})
|
||||
|
||||
if (this.playButton) {
|
||||
this.playButton.update({
|
||||
x: width / 2,
|
||||
y: realY,
|
||||
r,
|
||||
});
|
||||
} else {
|
||||
this.playButton = new Button({
|
||||
group: this.group,
|
||||
x: width / 2,
|
||||
y: realY + r + 5,
|
||||
r,
|
||||
isPlay: this.isPlay,
|
||||
style: playBtnStyle
|
||||
});
|
||||
}
|
||||
|
||||
// 前进按钮
|
||||
this.group.addShape('path', {
|
||||
attrs: {
|
||||
path: this.getPreMarkerPath(width / 2 - 5 * r, realY + r + 5, r * 0.5),
|
||||
...preBtnStyle
|
||||
},
|
||||
capture: false,
|
||||
name: 'preStepBtn'
|
||||
})
|
||||
this.group.addShape('path', {
|
||||
attrs: {
|
||||
path: this.getPreMarkerPath(width / 2 - 4.5 * r, realY + r + 5, r * 0.5),
|
||||
...preBtnStyle
|
||||
},
|
||||
capture: false,
|
||||
name: 'preStepBtn'
|
||||
})
|
||||
|
||||
// 后退按钮
|
||||
this.group.addShape('path', {
|
||||
attrs: {
|
||||
path: this.getNextMarkerPath(width / 2 + 5 * r, realY + r + 5, r * 0.5),
|
||||
...nextBtnStyle
|
||||
},
|
||||
capture: false,
|
||||
name: 'nextStepBtn'
|
||||
})
|
||||
this.group.addShape('path', {
|
||||
attrs: {
|
||||
path: this.getNextMarkerPath(width / 2 + 4.5 * r, realY + r + 5, r * 0.5),
|
||||
...nextBtnStyle
|
||||
},
|
||||
capture: false,
|
||||
name: 'nextStepBtn'
|
||||
})
|
||||
|
||||
// 调节speed的按钮
|
||||
this.renderSpeedBtn()
|
||||
this.renderToggleTime()
|
||||
this.bindEvent()
|
||||
}
|
||||
|
||||
private renderSpeedBtn() {
|
||||
const { y, width } = this.controllerCfg
|
||||
const speedGroup = this.group.addGroup({
|
||||
name: 'speed-group'
|
||||
})
|
||||
|
||||
this.speedGroup = speedGroup
|
||||
|
||||
let count = 2
|
||||
const speedNum = []
|
||||
let maxSpeed = 9
|
||||
// 增加speed刻度
|
||||
for (let i = 0; i < 5; i++) {
|
||||
// 灰色刻度
|
||||
speedGroup.addShape('rect', {
|
||||
attrs: {
|
||||
x: width * 4 / 5,
|
||||
y: y + 10 + i * (i + 1) + count,
|
||||
width: 15,
|
||||
height: 2,
|
||||
fill: '#ccc'
|
||||
},
|
||||
speed: maxSpeed,
|
||||
name: 'speed-rect'
|
||||
})
|
||||
this.speedAxisY.push(y + 10 + i * (i + 1) + count)
|
||||
speedNum.push(maxSpeed)
|
||||
maxSpeed = maxSpeed - 2
|
||||
count++
|
||||
}
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
// 灰色刻度
|
||||
speedGroup.addShape('rect', {
|
||||
attrs: {
|
||||
x: width * 4 / 5,
|
||||
y: this.speedAxisY[i] + 2,
|
||||
width: 15,
|
||||
height: 2 * i + 1,
|
||||
fill: '#fff',
|
||||
opacity: 0.3
|
||||
},
|
||||
speed: speedNum[i] - 1,
|
||||
name: 'speed-rect'
|
||||
})
|
||||
}
|
||||
|
||||
// 速度文本
|
||||
this.speedText = speedGroup.addShape('text', {
|
||||
attrs: {
|
||||
x: width * 4 / 5 + 20,
|
||||
y: this.speedAxisY[1] + 15,
|
||||
text: '3.0X',
|
||||
fill: '#ccc'
|
||||
}
|
||||
})
|
||||
|
||||
this.speedPoint = speedGroup.addShape('path', {
|
||||
attrs: {
|
||||
path: this.getPath(width * 4 / 5, this.speedAxisY[2]),
|
||||
fill: '#ccc'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private getPath (x, y) {
|
||||
return [
|
||||
['M', x, y],
|
||||
['L', x - 12, y - 6],
|
||||
['L', x - 12, y + 6],
|
||||
['Z']
|
||||
]
|
||||
}
|
||||
|
||||
private renderToggleTime() {
|
||||
const { width, y } = this.controllerCfg
|
||||
this.toggleGroup = this.group.addGroup({
|
||||
name: 'toggle-group'
|
||||
})
|
||||
|
||||
console.log(this.speedAxisY)
|
||||
this.toggleGroup.addShape('rect', {
|
||||
attrs: {
|
||||
width: 14,
|
||||
height: 14,
|
||||
x: width - 45,
|
||||
y: this.speedAxisY[1],
|
||||
fill: '#fff',
|
||||
stroke: '#ccc',
|
||||
lineWidth: 2,
|
||||
radius: 3
|
||||
},
|
||||
isChecked: false,
|
||||
name: 'toggle-model'
|
||||
})
|
||||
|
||||
this.checkedIcon = this.toggleGroup.addShape('path', {
|
||||
attrs: {
|
||||
path: [
|
||||
['M', width - 45 + 3, this.speedAxisY[1] + 6],
|
||||
['L', width - 45 + 7, this.speedAxisY[1] + 10],
|
||||
['L', width - 45 + 12, this.speedAxisY[1] + 4],
|
||||
],
|
||||
stroke: 'green',
|
||||
lineWidth: 3,
|
||||
},
|
||||
capture: false
|
||||
})
|
||||
|
||||
this.checkedIcon.hide()
|
||||
|
||||
this.checkedText = this.toggleGroup.addShape('text', {
|
||||
attrs: {
|
||||
text: '单一时间',
|
||||
x: width - 30,
|
||||
y: this.speedAxisY[1] + 15,
|
||||
fill: '#ccc'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private bindEvent() {
|
||||
const { width } = this.controllerCfg
|
||||
this.speedGroup.on('speed-rect:click', evt => {
|
||||
this.speedPoint.attr('path', this.getPath(width * 4 / 5, evt.y))
|
||||
this.currentSpeed = evt.target.get('speed')
|
||||
this.speedText.attr('text', `${this.currentSpeed}.0X`)
|
||||
this.group.emit('timebarConfigChanged', {
|
||||
speed: this.currentSpeed,
|
||||
type: this.currentType,
|
||||
})
|
||||
})
|
||||
|
||||
this.toggleGroup.on('toggle-model:click', evt => {
|
||||
const isChecked = evt.target.get('isChecked')
|
||||
if (!isChecked) {
|
||||
this.checkedIcon.show()
|
||||
this.checkedText.attr('text', '时间范围')
|
||||
this.currentType = 'signle'
|
||||
} else {
|
||||
this.checkedIcon.hide()
|
||||
this.checkedText.attr('text', '单一时间')
|
||||
this.currentType = 'range'
|
||||
}
|
||||
evt.target.set('isChecked', !isChecked)
|
||||
this.group.emit('timebarConfigChanged', {
|
||||
type: this.currentType,
|
||||
speed: this.currentSpeed
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.group.off('playPauseBtn:click')
|
||||
}
|
||||
}
|
@ -23,8 +23,8 @@ export interface HandlerCfg {
|
||||
}
|
||||
|
||||
const DEFAULT_STYLE = {
|
||||
fill: '#F7F7F7',
|
||||
stroke: '#BFBFBF',
|
||||
fill: '#1890ff',
|
||||
stroke: '#1890ff',
|
||||
radius: 2,
|
||||
opacity: 1,
|
||||
cursor: 'ew-resize',
|
||||
@ -47,10 +47,11 @@ export default class Handler {
|
||||
|
||||
// 组件
|
||||
private background: IShape;
|
||||
private handleGroup: IGroup;
|
||||
|
||||
constructor(cfg: HandlerCfg) {
|
||||
|
||||
const { group, name, x = 0, y = 0, width = 10, height = 24, style = {} } = cfg;
|
||||
const { group, name, x = 0, y = 0, width = 2, height = 24, style = {} } = cfg;
|
||||
|
||||
this.group = group
|
||||
this.name = name
|
||||
@ -95,8 +96,9 @@ export default class Handler {
|
||||
const { width, height, style, name } = this;
|
||||
const { fill, stroke, radius, opacity, cursor } = style;
|
||||
|
||||
this.handleGroup = this.group.addGroup()
|
||||
// 按钮框框
|
||||
this.background = this.group.addShape('rect', {
|
||||
this.background = this.handleGroup.addShape('rect', {
|
||||
attrs: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
@ -111,38 +113,31 @@ export default class Handler {
|
||||
name: `${name}-rect`
|
||||
});
|
||||
|
||||
// 两根竖线
|
||||
const x1 = 1 / 3 * width;
|
||||
const x2 = 2 / 3 * width;
|
||||
|
||||
const y1 = 1 / 4 * height;
|
||||
const y2 = 3 / 4 * height;
|
||||
|
||||
this.group.addShape('line', {
|
||||
this.handleGroup.addShape('circle', {
|
||||
attrs: {
|
||||
x1,
|
||||
y1,
|
||||
x2: x1,
|
||||
y2,
|
||||
x: width / 2,
|
||||
y: 0,
|
||||
r: 2 * width,
|
||||
fill,
|
||||
stroke,
|
||||
cursor,
|
||||
},
|
||||
capture: false
|
||||
// name
|
||||
});
|
||||
radius,
|
||||
opacity,
|
||||
cursor
|
||||
}
|
||||
})
|
||||
|
||||
this.group.addShape('line', {
|
||||
this.handleGroup.addShape('circle', {
|
||||
attrs: {
|
||||
x1: x2,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
x: width / 2,
|
||||
y: height,
|
||||
r: 2 * width,
|
||||
fill,
|
||||
stroke,
|
||||
cursor,
|
||||
},
|
||||
capture: false
|
||||
// name
|
||||
});
|
||||
radius,
|
||||
opacity,
|
||||
cursor
|
||||
}
|
||||
})
|
||||
|
||||
// 移动到对应的位置
|
||||
this.updateXY();
|
||||
@ -152,18 +147,26 @@ export default class Handler {
|
||||
|
||||
private bindEvents() {
|
||||
const { name } = this;
|
||||
this.group.on(`${name}-rect:mouseenter`, () => {
|
||||
this.handleGroup.on(`${name}-rect:mouseenter`, () => {
|
||||
const { highLightFill } = this.style;
|
||||
this.background.attr('fill', highLightFill);
|
||||
});
|
||||
|
||||
this.group.on(`${name}-rect:mouseleave`, () => {
|
||||
this.handleGroup.on(`${name}-rect:mouseleave`, () => {
|
||||
const { fill } = this.style;
|
||||
this.background.attr('fill', fill);
|
||||
});
|
||||
}
|
||||
|
||||
public show() {
|
||||
this.handleGroup.show()
|
||||
}
|
||||
|
||||
public hide() {
|
||||
this.handleGroup.hide()
|
||||
}
|
||||
|
||||
private updateXY() {
|
||||
this.group.setMatrix([1, 0, 0, 0, 1, 0, this.x, this.y, 1])
|
||||
this.handleGroup.setMatrix([1, 0, 0, 0, 1, 0, this.x, this.y, 1])
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { IGroup, ICanvas } from '@antv/g-base';
|
||||
import createDOM from '@antv/dom-util/lib/create-dom'
|
||||
import { isString } from '@antv/util'
|
||||
import Base, { IPluginBaseConfig } from '../base';
|
||||
import Slider, { SliderOption, VALUE_CHANGE } from './slider'
|
||||
import Slider, { SliderOption, VALUE_CHANGE, ControllerCfg } from './trendTimeBar'
|
||||
import TimeLine, { TimeLineCfg, TIMELINE_CHANGE } from './timeLine'
|
||||
import { IGraph } from '../../interface/graph';
|
||||
import { GraphData } from '../../types';
|
||||
@ -19,8 +19,24 @@ interface Callback {
|
||||
target: IGroup;
|
||||
}
|
||||
|
||||
interface TrendConfig extends TrendCfg{
|
||||
interface TrendConfig {
|
||||
readonly slider?: SliderOption;
|
||||
// 数据
|
||||
readonly data: {
|
||||
date: string;
|
||||
value: string;
|
||||
}[];
|
||||
// 位置大小
|
||||
readonly x?: number;
|
||||
readonly y?: number;
|
||||
readonly width?: number;
|
||||
readonly height?: number;
|
||||
// 样式
|
||||
readonly smooth?: boolean;
|
||||
readonly isArea?: boolean;
|
||||
readonly backgroundStyle?: object;
|
||||
readonly lineStyle?: object;
|
||||
readonly areaStyle?: object;
|
||||
}
|
||||
|
||||
interface TimeBarConfig extends IPluginBaseConfig {
|
||||
@ -31,11 +47,13 @@ interface TimeBarConfig extends IPluginBaseConfig {
|
||||
readonly height?: number;
|
||||
readonly padding?: number;
|
||||
|
||||
// 趋势图配置项,包括滑块、及前后背景的配置
|
||||
// 趋势图配置项
|
||||
readonly trend?: TrendConfig;
|
||||
// 滑块、及前后背景的配置
|
||||
readonly slider?: SliderOption;
|
||||
|
||||
// 时间线的配置项
|
||||
readonly timeline?: TimeLineCfg;
|
||||
// 控制按钮
|
||||
readonly controllerCfg?: ControllerCfg;
|
||||
|
||||
// readonly opti
|
||||
rangeChange?: (graph: IGraph, minValue: string, maxValue: string) => void;
|
||||
@ -50,21 +68,22 @@ export default class TimeBar extends Base {
|
||||
container: null,
|
||||
className: 'g6-component-timebar',
|
||||
padding: 10,
|
||||
speed: 2,
|
||||
loop: false,
|
||||
trend: {
|
||||
data: [],
|
||||
group: null,
|
||||
isArea: false,
|
||||
smooth: true,
|
||||
slider: {
|
||||
minLimit: 0,
|
||||
maxLimit: 1,
|
||||
start: 0.1,
|
||||
end: 0.9,
|
||||
minText: 'min',
|
||||
maxText: 'max',
|
||||
}
|
||||
smooth: true
|
||||
},
|
||||
controllerCfg: {
|
||||
speed: 2,
|
||||
loop: false,
|
||||
},
|
||||
slider: {
|
||||
minLimit: 0,
|
||||
maxLimit: 1,
|
||||
start: 0.1,
|
||||
end: 0.9,
|
||||
minText: 'min',
|
||||
maxText: 'max',
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -125,14 +144,14 @@ export default class TimeBar extends Base {
|
||||
this.set('timeBarGroup', timeBarGroup)
|
||||
|
||||
this.renderTrend()
|
||||
this.renderTimeLine()
|
||||
// this.renderTimeLine()
|
||||
this.initEvent()
|
||||
}
|
||||
|
||||
private renderTrend() {
|
||||
const ratio = 0.6
|
||||
const { width, height, x, y, padding, trend } = this._cfgs
|
||||
const { slider, ...other } = trend
|
||||
const { width, height, x, y, padding, trend, slider, controllerCfg } = this._cfgs
|
||||
const { data, ...other } = trend
|
||||
|
||||
const realHeight = height - 2 * padding
|
||||
const realWidth = width - 2 * padding
|
||||
@ -140,56 +159,27 @@ export default class TimeBar extends Base {
|
||||
graph: this.get('graph'),
|
||||
canvas: this.get('canvas'),
|
||||
group: this.get('timeBarGroup'),
|
||||
x,
|
||||
y,
|
||||
x: x + padding,
|
||||
y: y + padding,
|
||||
width: realWidth,
|
||||
height: realHeight * ratio,
|
||||
padding,
|
||||
trendCfg: {
|
||||
...other,
|
||||
data: other.data.map(d => d.value)
|
||||
data: data.map(d => d.value)
|
||||
},
|
||||
...trend.slider,
|
||||
...slider,
|
||||
ticks: data.map(d => d.date),
|
||||
handlerStyle: {
|
||||
...slider.handlerStyle,
|
||||
height: realHeight * ratio
|
||||
}
|
||||
},
|
||||
controllerCfg
|
||||
})
|
||||
|
||||
this.set('slider', sliderComponent)
|
||||
}
|
||||
|
||||
private renderTimeLine() {
|
||||
const ratio = 0.2
|
||||
const { width, height, x, y, padding, timeline } = this._cfgs
|
||||
const { speed, defaultCurrentTick, loop, axisCfg = {}, controllerCfg = {}, ticks } = timeline
|
||||
const realHeight = height - 2 * padding
|
||||
const realWidth = width - 2 * padding
|
||||
|
||||
const timelineComponent = new TimeLine({
|
||||
graph: this.get('graph'),
|
||||
canvas: this.get('canvas'),
|
||||
group: this.get('timeBarGroup'),
|
||||
x,
|
||||
y: realHeight * 0.6,
|
||||
width: realWidth,
|
||||
height: realHeight * ratio,
|
||||
speed,
|
||||
defaultCurrentTick,
|
||||
loop,
|
||||
ticks,
|
||||
axisCfg,
|
||||
controllerCfg: {
|
||||
x,
|
||||
y: realHeight * (1 - ratio),
|
||||
width: realWidth,
|
||||
height: realHeight * ratio,
|
||||
...controllerCfg
|
||||
}
|
||||
})
|
||||
|
||||
this.set('timeline', timelineComponent)
|
||||
}
|
||||
|
||||
private filterData(evt) {
|
||||
const { value } = evt;
|
||||
const { data: trendData } = this._cfgs.trend
|
||||
@ -275,20 +265,20 @@ export default class TimeBar extends Base {
|
||||
}
|
||||
|
||||
private initEvent() {
|
||||
const { start, end } = this._cfgs.trend.slider;
|
||||
const { start, end } = this._cfgs.slider;
|
||||
const graph: IGraph = this.get('graph');
|
||||
graph.on('afterrender', () => {
|
||||
this.filterData({ value: [start, end] });
|
||||
});
|
||||
|
||||
graph.on(VALUE_CHANGE, (evt: Callback) => {
|
||||
this.filterData(evt);
|
||||
// this.filterData(evt);
|
||||
});
|
||||
|
||||
// 时间轴的值发生改变的事件
|
||||
graph.on(TIMELINE_CHANGE, (value: string) => {
|
||||
this.renderCurrentData(value)
|
||||
})
|
||||
// graph.on(TIMELINE_CHANGE, (value: string) => {
|
||||
// this.renderCurrentData(value)
|
||||
// })
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
|
@ -62,7 +62,7 @@ export default class Button {
|
||||
this.startMarker = group.addShape('path', {
|
||||
attrs: {
|
||||
path: this.getStartMarkerPath(),
|
||||
fill: '#ffffff',
|
||||
fill: '#ccc',
|
||||
},
|
||||
name: 'playPauseBtn'
|
||||
});
|
||||
@ -76,7 +76,7 @@ export default class Button {
|
||||
y: this.config.y - height / 2,
|
||||
width: width * 2.5,
|
||||
height,
|
||||
...style
|
||||
fill: '#fff'
|
||||
},
|
||||
name: 'playPauseBtn'
|
||||
})
|
||||
@ -86,7 +86,7 @@ export default class Button {
|
||||
y: this.config.y - height / 2,
|
||||
width: width,
|
||||
height: height,
|
||||
fill: '#ffffff',
|
||||
fill: '#ccc',
|
||||
},
|
||||
name: 'playPauseBtn'
|
||||
});
|
||||
@ -97,7 +97,7 @@ export default class Button {
|
||||
y: this.config.y - height / 2,
|
||||
width,
|
||||
height,
|
||||
fill: '#ffffff',
|
||||
fill: '#ccc',
|
||||
},
|
||||
name: 'playPauseBtn'
|
||||
});
|
||||
|
@ -1,12 +1,8 @@
|
||||
/**
|
||||
* @file 基于 G 的缩略轴组件(Slider 组件)
|
||||
* @author hustcc
|
||||
*/
|
||||
|
||||
import { Event, IGroup, ICanvas, IShape } from '@antv/g-base';
|
||||
import { get, size, assign, each } from '@antv/util';
|
||||
import Trend, { TrendCfg } from './trend';
|
||||
import Handler from './handler';
|
||||
import ControllerBtn from './controllerBtn'
|
||||
import { IGraph } from '../../interface/graph';
|
||||
import { ShapeStyle } from '../../types';
|
||||
|
||||
@ -25,7 +21,7 @@ export const FOREGROUND_STYLE = {
|
||||
cursor: 'move',
|
||||
};
|
||||
|
||||
export const DEFAULT_HANDLER_WIDTH = 10;
|
||||
export const DEFAULT_HANDLER_WIDTH = 2;
|
||||
|
||||
export const HANDLER_STYLE = {
|
||||
width: DEFAULT_HANDLER_WIDTH,
|
||||
@ -38,8 +34,30 @@ export const TEXT_STYLE = {
|
||||
opacity: 0.45,
|
||||
};
|
||||
|
||||
export const TIMELINE_START = 'timelinestart';
|
||||
export const TIMELINE_CHANGE = 'timelinechange';
|
||||
export const TIMELINE_END = 'timelineend';
|
||||
|
||||
export const VALUE_CHANGE = 'valueChange';
|
||||
|
||||
export type ControllerCfg = Partial<{
|
||||
readonly x: number;
|
||||
readonly y: number;
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
readonly padding: number;
|
||||
/** 播放速度,1 个 tick 花费时间 */
|
||||
readonly speed?: number;
|
||||
/** 是否循环播放 */
|
||||
readonly loop?: boolean;
|
||||
|
||||
readonly fill: string;
|
||||
readonly stroke: string;
|
||||
readonly preBtnStyle: ShapeStyle;
|
||||
readonly nextBtnStyle: ShapeStyle;
|
||||
readonly playBtnStyle: ShapeStyle;
|
||||
}>
|
||||
|
||||
export type SliderOption = Partial<{
|
||||
readonly backgroundStyle?: ShapeStyle;
|
||||
readonly foregroundStyle?: ShapeStyle;
|
||||
@ -61,7 +79,7 @@ export type SliderOption = Partial<{
|
||||
readonly maxText: string;
|
||||
}>;
|
||||
|
||||
interface SliderCfg extends SliderOption {
|
||||
interface TrendTimeBarConfig extends SliderOption {
|
||||
readonly graph: IGraph;
|
||||
readonly canvas: ICanvas;
|
||||
readonly group: IGroup;
|
||||
@ -70,12 +88,16 @@ interface SliderCfg extends SliderOption {
|
||||
readonly y: number;
|
||||
readonly width: number;
|
||||
readonly height: number;
|
||||
|
||||
readonly padding: number;
|
||||
// style
|
||||
readonly trendCfg?: TrendCfg;
|
||||
|
||||
readonly ticks?: string[];
|
||||
|
||||
readonly controllerCfg: ControllerCfg;
|
||||
}
|
||||
|
||||
export default class Slider {
|
||||
export default class TrendTimeBar{
|
||||
private group: IGroup;
|
||||
private graph: IGraph;
|
||||
private canvas: ICanvas;
|
||||
@ -84,8 +106,11 @@ export default class Slider {
|
||||
public y: number;
|
||||
public width: number;
|
||||
public height: number;
|
||||
private padding: number;
|
||||
|
||||
private trendCfg: TrendCfg;
|
||||
|
||||
private controllerCfg: ControllerCfg;
|
||||
// 样式配置
|
||||
private backgroundStyle: any;
|
||||
private foregroundStyle: any;
|
||||
@ -102,6 +127,8 @@ export default class Slider {
|
||||
/* 右侧文本 */
|
||||
private maxTextShape: IShape;
|
||||
|
||||
private textList: IShape[];
|
||||
|
||||
// 交互相关的数据信息
|
||||
private start: number;
|
||||
private end: number;
|
||||
@ -110,17 +137,32 @@ export default class Slider {
|
||||
|
||||
private currentHandler: Handler | IShape;
|
||||
private prevX: number = 0;
|
||||
private prevY: number = 0;
|
||||
private sliderGroup: IGroup;
|
||||
|
||||
constructor(cfg: SliderCfg) {
|
||||
/** 刻度位置预处理 */
|
||||
private tickPosList: number[];
|
||||
|
||||
private ticks: string[];
|
||||
|
||||
/** 是否处于播放状态 */
|
||||
private isPlay: boolean;
|
||||
|
||||
/** 动画 id */
|
||||
private playHandler: number;
|
||||
|
||||
private controllerBtnGroup: ControllerBtn;
|
||||
|
||||
constructor(cfg: TrendTimeBarConfig) {
|
||||
|
||||
const {
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 100,
|
||||
height = 16,
|
||||
padding = 10,
|
||||
trendCfg,
|
||||
controllerCfg = {
|
||||
speed: 1
|
||||
},
|
||||
backgroundStyle = {},
|
||||
foregroundStyle = {},
|
||||
handlerStyle = {},
|
||||
@ -132,7 +174,8 @@ export default class Slider {
|
||||
maxText = '',
|
||||
group,
|
||||
graph,
|
||||
canvas
|
||||
canvas,
|
||||
ticks
|
||||
} = cfg;
|
||||
|
||||
this.graph = graph
|
||||
@ -143,8 +186,10 @@ export default class Slider {
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
this.padding = padding
|
||||
this.ticks = ticks
|
||||
this.trendCfg = trendCfg;
|
||||
this.controllerCfg = controllerCfg;
|
||||
// style
|
||||
this.backgroundStyle = { ...BACKGROUND_STYLE, ...backgroundStyle };
|
||||
this.foregroundStyle = { ...FOREGROUND_STYLE, ...foregroundStyle };
|
||||
@ -165,7 +210,7 @@ export default class Slider {
|
||||
* 更新配置
|
||||
* @param cfg
|
||||
*/
|
||||
public update(cfg: Partial<SliderCfg>) {
|
||||
public update(cfg: Partial<TrendTimeBarConfig>) {
|
||||
const { x, y, width, height, minText, maxText, start, end } = cfg;
|
||||
|
||||
// start、end 只能是 0~1 范围
|
||||
@ -203,8 +248,8 @@ export default class Slider {
|
||||
// 趋势图数据
|
||||
if (size(get(this.trendCfg, 'data'))) {
|
||||
new Trend({
|
||||
x: 0,
|
||||
y: 0,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
width,
|
||||
height,
|
||||
...this.trendCfg,
|
||||
@ -213,10 +258,11 @@ export default class Slider {
|
||||
}
|
||||
|
||||
const sliderGroup = this.group.addGroup({
|
||||
name: 'slider-group'
|
||||
name: 'slider-group',
|
||||
// x: this.x,
|
||||
// y: this.y,
|
||||
})
|
||||
|
||||
this.sliderGroup = sliderGroup
|
||||
// 1. 背景
|
||||
sliderGroup.addShape('rect', {
|
||||
attrs: {
|
||||
@ -228,11 +274,12 @@ export default class Slider {
|
||||
},
|
||||
});
|
||||
|
||||
const textGroup = this.group.addGroup()
|
||||
// 2. 左右文字
|
||||
this.minTextShape = sliderGroup.addShape('text', {
|
||||
this.minTextShape = textGroup.addShape('text', {
|
||||
attrs: {
|
||||
// x: 0,
|
||||
y: height / 2,
|
||||
x: 0,
|
||||
y: height / 2 + this.y,
|
||||
textAlign: 'right',
|
||||
text: this.minText,
|
||||
silent: false,
|
||||
@ -241,10 +288,10 @@ export default class Slider {
|
||||
capture: false
|
||||
});
|
||||
|
||||
this.maxTextShape = sliderGroup.addShape('text', {
|
||||
this.maxTextShape = textGroup.addShape('text', {
|
||||
attrs: {
|
||||
// x: 0,
|
||||
y: height / 2,
|
||||
y: height / 2 + this.y,
|
||||
textAlign: 'left',
|
||||
text: this.maxText,
|
||||
silent: false,
|
||||
@ -255,29 +302,31 @@ export default class Slider {
|
||||
});
|
||||
|
||||
// 3. 前景 选中背景框
|
||||
this.foregroundShape = sliderGroup.addShape('rect', {
|
||||
this.foregroundShape = this.group.addGroup().addShape('rect', {
|
||||
attrs: {
|
||||
// x: 0,
|
||||
y: 0,
|
||||
// width: 0,
|
||||
x: 0,
|
||||
y: this.y,
|
||||
// width,
|
||||
height,
|
||||
...this.foregroundStyle,
|
||||
},
|
||||
});
|
||||
|
||||
// 滑块相关的大小信息
|
||||
const handlerWidth = get(this.handlerStyle, 'width', 10);
|
||||
const handlerWidth = get(this.handlerStyle, 'width', 2);
|
||||
const handlerHeight = get(this.handlerStyle, 'height', 24);
|
||||
|
||||
const minHandleGroup = this.group.addGroup({
|
||||
name: 'minHandlerShape'
|
||||
name: 'minHandlerShape',
|
||||
// x: this.x,
|
||||
// y: this.y,
|
||||
})
|
||||
// 4. 左右滑块
|
||||
this.minHandlerShape = new Handler({
|
||||
name: 'minHandlerShape',
|
||||
group: minHandleGroup,
|
||||
x: 0,
|
||||
y: 0,//(height - handlerHeight) / 2,
|
||||
x: this.x,
|
||||
y: this.y,//(height - handlerHeight) / 2,
|
||||
width: handlerWidth,
|
||||
height: handlerHeight,
|
||||
cursor: 'ew-resize',
|
||||
@ -285,24 +334,73 @@ export default class Slider {
|
||||
});
|
||||
|
||||
const maxHandleGroup = this.group.addGroup({
|
||||
name: 'maxHandlerShape'
|
||||
name: 'maxHandlerShape',
|
||||
// x: this.x,
|
||||
// y: this.y,
|
||||
})
|
||||
this.maxHandlerShape = new Handler({
|
||||
name: 'maxHandlerShape',
|
||||
group: maxHandleGroup,
|
||||
x: 0,
|
||||
y: 0,//(height - handlerHeight) / 2,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
width: handlerWidth,
|
||||
height: handlerHeight,
|
||||
cursor: 'ew-resize',
|
||||
...this.handlerStyle,
|
||||
});
|
||||
|
||||
// 缩略图下面的时间刻度
|
||||
const tickData = this.ticks
|
||||
const interval = width / (tickData.length - 1);
|
||||
this.tickPosList = [];
|
||||
if (this.textList && this.textList.length) {
|
||||
this.textList.forEach((text) => {
|
||||
text.destroy();
|
||||
});
|
||||
}
|
||||
let lastX = -Infinity;
|
||||
this.textList = tickData.map((tick, index) => {
|
||||
this.tickPosList.push(this.x + index * interval);
|
||||
|
||||
const text = this.group.addShape('text', {
|
||||
attrs: {
|
||||
x: this.x + index * interval,
|
||||
y: this.y + height + 5,
|
||||
text: tick,
|
||||
textAlign: 'center',
|
||||
textBaseline: 'top',
|
||||
fill: '#607889',
|
||||
opacity: 0.35,
|
||||
},
|
||||
});
|
||||
|
||||
const bbox = text.getBBox();
|
||||
|
||||
// 抽样,标签与标签间距不小于 10
|
||||
if (bbox.minX > lastX) {
|
||||
text.show();
|
||||
lastX = bbox.minX + bbox.width + 10;
|
||||
} else {
|
||||
text.hide();
|
||||
}
|
||||
|
||||
return text;
|
||||
});
|
||||
|
||||
// 渲染播放、快进和后退的控制按钮
|
||||
this.controllerBtnGroup = new ControllerBtn({
|
||||
group: this.group,
|
||||
x: this.x,
|
||||
y: this.y + height + 25,
|
||||
width,
|
||||
height: 40
|
||||
})
|
||||
|
||||
// 根据 start end 更新 ui 的位置信息
|
||||
this.updateUI();
|
||||
|
||||
// 移动到对应的位置
|
||||
this.sliderGroup.move(this.x, this.y);
|
||||
sliderGroup.move(this.x, this.y);
|
||||
|
||||
// 绑定事件鼠标事件
|
||||
this.bindEvents();
|
||||
@ -334,6 +432,45 @@ export default class Slider {
|
||||
// 3. 前景选中区域
|
||||
this.foregroundShape.on('mousedown', this.onMouseDown(this.foregroundShape));
|
||||
this.foregroundShape.on('touchstart', this.onMouseDown(this.foregroundShape));
|
||||
|
||||
// 播放区按钮控制
|
||||
/** 播放/暂停事件 */
|
||||
this.group.on('playPauseBtn:click', () => {
|
||||
this.isPlay = !this.isPlay;
|
||||
this.currentHandler = this.maxHandlerShape
|
||||
this.changePlayStatus();
|
||||
})
|
||||
|
||||
// 处理前进一步的事件
|
||||
this.group.on('nextStepBtn:click', () => {
|
||||
this.currentHandler = this.maxHandlerShape
|
||||
this.updateStartEnd(0.01);
|
||||
this.updateUI()
|
||||
})
|
||||
|
||||
// 处理后退一步的事件
|
||||
this.group.on('preStepBtn:click', () => {
|
||||
this.currentHandler = this.maxHandlerShape
|
||||
this.updateStartEnd(-0.01);
|
||||
this.updateUI()
|
||||
|
||||
})
|
||||
|
||||
this.group.on('timebarConfigChanged', ({ type, speed }) => {
|
||||
console.log('timebarConfigChanged', type, speed)
|
||||
if(type === 'signle') {
|
||||
this.minHandlerShape.hide()
|
||||
this.foregroundShape.hide()
|
||||
this.minTextShape.hide()
|
||||
} else if (type === 'range') {
|
||||
this.minHandlerShape.show()
|
||||
this.foregroundShape.show()
|
||||
this.minTextShape.show()
|
||||
}
|
||||
})
|
||||
|
||||
/** 播放轴上圆点滑动事件 */
|
||||
// this.timeSelect.on('mousedown', this.onTimeSelectMouseDown);
|
||||
}
|
||||
|
||||
private onMouseDown = (handler: Handler | IShape) => (event: Event) => {
|
||||
@ -347,7 +484,6 @@ export default class Slider {
|
||||
|
||||
// 兼容移动端获取数据
|
||||
this.prevX = get(event, 'touches.0.pageX', event.x);
|
||||
this.prevY = get(event, 'touches.0.pageY', event.y);
|
||||
|
||||
// 3. 开始滑动的时候,绑定 move 和 up 事件
|
||||
const containerDOM = this.canvas.get('container');
|
||||
@ -368,8 +504,6 @@ export default class Slider {
|
||||
e.preventDefault();
|
||||
|
||||
const x = get(e, 'touches.0.pageX', e.pageX);
|
||||
const y = get(e, 'touches.0.pageY', e.pageY);
|
||||
console.log('slider move', e, x, y)
|
||||
|
||||
// 横向的 slider 只处理 x
|
||||
const offsetX = x - this.prevX;
|
||||
@ -382,9 +516,6 @@ export default class Slider {
|
||||
this.updateUI();
|
||||
|
||||
this.prevX = x;
|
||||
this.prevY = y;
|
||||
|
||||
// this.get('canvas').draw();
|
||||
|
||||
// 因为存储的 start、end 可能不一定是按大小存储的,所以排序一下,对外是 end >= start
|
||||
this.graph.emit(VALUE_CHANGE, {value: [this.start, this.end].sort()});
|
||||
@ -410,6 +541,17 @@ export default class Slider {
|
||||
}
|
||||
};
|
||||
|
||||
/** 输入当前圆点位置,输出离哪个 tick 的位置最近 */
|
||||
private adjustTickIndex(timeSelectX: number) {
|
||||
for (let i = 0; i < this.tickPosList.length - 1; i++) {
|
||||
if (this.tickPosList[i] <= timeSelectX && timeSelectX <= this.tickPosList[i + 1]) {
|
||||
return Math.abs(this.tickPosList[i] - timeSelectX) < Math.abs(timeSelectX - this.tickPosList[i + 1])
|
||||
? i
|
||||
: i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整 offsetRange,因为一些范围的限制
|
||||
* @param offsetRange
|
||||
@ -445,13 +587,21 @@ export default class Slider {
|
||||
switch (this.currentHandler) {
|
||||
case this.minHandlerShape:
|
||||
this.start += offsetRange;
|
||||
const minTick = this.adjustTickIndex(this.start * this.width)
|
||||
this.minText = this.ticks[minTick]
|
||||
break;
|
||||
case this.maxHandlerShape:
|
||||
this.end += offsetRange;
|
||||
const maxTick = this.adjustTickIndex(this.end * this.width)
|
||||
this.maxText = this.ticks[maxTick]
|
||||
break;
|
||||
case this.foregroundShape:
|
||||
this.start += offsetRange;
|
||||
this.end += offsetRange;
|
||||
const minRangeTick = this.adjustTickIndex(this.start * this.width)
|
||||
const maxRangeTick = this.adjustTickIndex(this.end * this.width)
|
||||
this.minText = this.ticks[minRangeTick]
|
||||
this.maxText = this.ticks[maxRangeTick]
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -461,9 +611,16 @@ export default class Slider {
|
||||
* @private
|
||||
*/
|
||||
private updateUI() {
|
||||
if (this.start < 0) {
|
||||
this.start = 0
|
||||
}
|
||||
|
||||
if (this.end > 1) {
|
||||
this.end = 1
|
||||
}
|
||||
|
||||
const min = this.start * this.width;
|
||||
const max = this.end * this.width;
|
||||
|
||||
// 1. foreground
|
||||
this.foregroundShape.attr('x', min);
|
||||
this.foregroundShape.attr('width', max - min);
|
||||
@ -472,8 +629,9 @@ export default class Slider {
|
||||
const handlerWidth = get(this.handlerStyle, 'width', DEFAULT_HANDLER_WIDTH);
|
||||
|
||||
// 设置文本
|
||||
this.minTextShape.attr('text', this.minText);
|
||||
this.maxTextShape.attr('text', this.maxText);
|
||||
this.setText(this.minText, this.maxText)
|
||||
// this.minTextShape.attr('text', this.minText);
|
||||
// this.maxTextShape.attr('text', this.maxText);
|
||||
|
||||
const [minAttrs, maxAttrs] = this.dodgeText([min, max]);
|
||||
// 2. 左侧滑块和文字位置
|
||||
@ -524,4 +682,45 @@ export default class Slider {
|
||||
|
||||
return !sorted ? [minAttrs, maxAttrs] : [maxAttrs, minAttrs];
|
||||
}
|
||||
|
||||
private startPlay() {
|
||||
return window.requestAnimationFrame(() => {
|
||||
const { controllerCfg, ticks, width } = this
|
||||
const { speed } = controllerCfg
|
||||
|
||||
|
||||
const tickInterval = width / ticks.length;
|
||||
const offsetX = tickInterval / ((speed * 1000) / 60);
|
||||
|
||||
const offsetXRange = this.adjustOffsetRange(offsetX / this.width);
|
||||
|
||||
this.updateStartEnd(offsetXRange)
|
||||
this.updateUI()
|
||||
// this.setTimeSelectX(offsetX);
|
||||
|
||||
if (this.isPlay) {
|
||||
this.playHandler = this.startPlay();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private changePlayStatus(isSync = true) {
|
||||
this.controllerBtnGroup.playButton.update({
|
||||
isPlay: this.isPlay,
|
||||
});
|
||||
if (this.isPlay) {
|
||||
// 开始播放
|
||||
this.playHandler = this.startPlay();
|
||||
this.graph.emit(TIMELINE_START, null);
|
||||
} else {
|
||||
// 结束播放
|
||||
if (this.playHandler) {
|
||||
window.cancelAnimationFrame(this.playHandler);
|
||||
if (isSync) {
|
||||
// this.syncCurrnentTick();
|
||||
this.graph.emit(TIMELINE_END, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1500
stories/Issues/forceLayout/multiLayout.tsx
Normal file
1500
stories/Issues/forceLayout/multiLayout.tsx
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ import ChangeAttr from './attrs';
|
||||
import DomClick from './component/dom-click';
|
||||
import ForceLayout from './forceLayout';
|
||||
import GGEditorNode from './ggeditorNode';
|
||||
import MultiLayout from './forceLayout/multiLayout'
|
||||
|
||||
export default { title: 'Issues' };
|
||||
|
||||
@ -17,4 +18,5 @@ storiesOf('Issues', module)
|
||||
.add('change attr', () => <ChangeAttr />)
|
||||
.add('dom click', () => <DomClick />)
|
||||
.add('forcelayout', () => <ForceLayout />)
|
||||
.add('ggeditor node issue', () => <GGEditorNode />);
|
||||
.add('ggeditor node issue', () => <GGEditorNode />)
|
||||
.add('multi layout', () => <MultiLayout />)
|
||||
|
@ -198,97 +198,41 @@ describe.only('timeline play with timebar', () => {
|
||||
});
|
||||
}
|
||||
|
||||
const TrendData = [
|
||||
21,
|
||||
13,
|
||||
19,
|
||||
25,
|
||||
18,
|
||||
4,
|
||||
21,
|
||||
19,
|
||||
22,
|
||||
26,
|
||||
3,
|
||||
13,
|
||||
5,
|
||||
23,
|
||||
24,
|
||||
9,
|
||||
29,
|
||||
2,
|
||||
27,
|
||||
26,
|
||||
29,
|
||||
7,
|
||||
19,
|
||||
29,
|
||||
15,
|
||||
3,
|
||||
9,
|
||||
11,
|
||||
29,
|
||||
30,
|
||||
4,
|
||||
24,
|
||||
23,
|
||||
16,
|
||||
21,
|
||||
7,
|
||||
24,
|
||||
19,
|
||||
18,
|
||||
16,
|
||||
1,
|
||||
15,
|
||||
27,
|
||||
13,
|
||||
23,
|
||||
16,
|
||||
4,
|
||||
1,
|
||||
1,
|
||||
20,
|
||||
];
|
||||
|
||||
const timeLine = new TimeBar({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 500,
|
||||
height: 200,
|
||||
padding: 10,
|
||||
trend: {
|
||||
data: timeBarData,
|
||||
isArea: true,
|
||||
// areaStyle: {
|
||||
// fill: 'pink'
|
||||
// },
|
||||
// lineStyle: {
|
||||
// stroke: 'green',
|
||||
// lineWidth: 3
|
||||
lineStyle: {
|
||||
stroke: 'green',
|
||||
lineWidth: 3
|
||||
}
|
||||
},
|
||||
slider: {
|
||||
// backgroundStyle: {
|
||||
// fill: 'blue',
|
||||
// opacity: 0.2
|
||||
// },
|
||||
// slider: {
|
||||
// backgroundStyle: {
|
||||
// fill: 'blue',
|
||||
// opacity: 0.2
|
||||
// },
|
||||
// foregroundStyle: {
|
||||
// fill: 'yellow'
|
||||
// },
|
||||
// textStyle: {
|
||||
// fill: 'red',
|
||||
// fontSize: 16,
|
||||
// },
|
||||
// handlerStyle: {
|
||||
// style: {
|
||||
// fill: 'green',
|
||||
// stroke: 'red'
|
||||
// }
|
||||
// foregroundStyle: {
|
||||
// fill: 'yellow'
|
||||
// },
|
||||
textStyle: {
|
||||
fill: 'red',
|
||||
fontSize: 16,
|
||||
},
|
||||
// handlerStyle: {
|
||||
// style: {
|
||||
// fill: '#1890ff',
|
||||
// stroke: '#1890ff'
|
||||
// }
|
||||
// }
|
||||
},
|
||||
timeline: {
|
||||
ticks: TrendData,
|
||||
|
||||
}
|
||||
// loop: true
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user