You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2020/07/22 13:17:15 UTC
[incubator-echarts] branch optimize-style updated: feat(timeline):
optimize timeline style. add progress config
This is an automated email from the ASF dual-hosted git repository.
shenyi pushed a commit to branch optimize-style
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
The following commit(s) were added to refs/heads/optimize-style by this push:
new 459a9d8 feat(timeline): optimize timeline style. add progress config
459a9d8 is described below
commit 459a9d8336da7efcd132dc892cf23158cd5ad1f5
Author: pissang <bm...@gmail.com>
AuthorDate: Wed Jul 22 21:16:35 2020 +0800
feat(timeline): optimize timeline style. add progress config
---
src/component/timeline/SliderTimelineModel.ts | 46 +++++---
src/component/timeline/SliderTimelineView.ts | 147 ++++++++++++++++++--------
src/component/timeline/TimelineModel.ts | 15 +++
src/echarts.ts | 62 ++++++-----
test/timeline-finance.html | 6 +-
5 files changed, 181 insertions(+), 95 deletions(-)
diff --git a/src/component/timeline/SliderTimelineModel.ts b/src/component/timeline/SliderTimelineModel.ts
index 2791773..e6c175c 100644
--- a/src/component/timeline/SliderTimelineModel.ts
+++ b/src/component/timeline/SliderTimelineModel.ts
@@ -48,13 +48,13 @@ class SliderTimelineModel extends TimelineModel {
trigger: 'item' // data item may also have tootip attr.
},
- symbol: 'emptyCircle',
- symbolSize: 10,
+ symbol: 'circle',
+ symbolSize: 12,
lineStyle: {
show: true,
width: 2,
- color: '#304654'
+ color: '#A4BED7'
},
label: { // 文本标签
position: 'auto', // auto left right top bottom
@@ -66,19 +66,24 @@ class SliderTimelineModel extends TimelineModel {
rotate: 0,
// formatter: null,
// 其余属性默认使用全局文本样式,详见TEXTSTYLE
- color: '#304654'
+ color: '#A4BED7'
},
itemStyle: {
- color: '#304654',
+ color: '#A4BED7',
borderWidth: 1
},
checkpointStyle: {
symbol: 'circle',
- symbolSize: 13,
- color: '#c23531',
- borderWidth: 5,
- borderColor: 'rgba(194,53,49, 0.5)',
+ symbolSize: 15,
+ color: '#316bf3',
+ borderColor: '#fff',
+ borderWidth: 2,
+ shadowBlur: 2,
+ shadowOffsetX: 1,
+ shadowOffsetY: 1,
+ shadowColor: 'rgba(0, 0, 0, 0.3)',
+ // borderColor: 'rgba(194,53,49, 0.5)',
animation: true,
animationDuration: 300,
animationEasing: 'quinticInOut'
@@ -97,28 +102,37 @@ class SliderTimelineModel extends TimelineModel {
nextIcon: 'path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z', // jshint ignore:line
prevIcon: 'path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z', // jshint ignore:line
- color: '#304654',
- borderColor: '#304654',
+ color: '#A4BED7',
+ borderColor: '#A4BED7',
borderWidth: 1
},
-
emphasis: {
label: {
show: true,
// 其余属性默认使用全局文本样式,详见TEXTSTYLE
- color: '#c23531'
+ color: '#6f778d'
},
itemStyle: {
- color: '#c23531'
+ color: '#316BF3'
},
controlStyle: {
- color: '#c23531',
- borderColor: '#c23531',
+ color: '#316BF3',
+ borderColor: '#316BF3',
borderWidth: 2
}
},
+
+ progress: {
+ lineStyle: {
+ color: '#316BF3'
+ },
+ itemStyle: {
+ color: '#316BF3'
+ }
+ },
+
data: []
});
diff --git a/src/component/timeline/SliderTimelineView.ts b/src/component/timeline/SliderTimelineView.ts
index e0e6083..f12febd 100644
--- a/src/component/timeline/SliderTimelineView.ts
+++ b/src/component/timeline/SliderTimelineView.ts
@@ -30,19 +30,20 @@ import * as numberUtil from '../../util/number';
import {encodeHTML} from '../../util/format';
import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../ExtensionAPI';
-import { merge, each, extend, clone, isString, bind } from 'zrender/src/core/util';
+import { merge, each, extend, clone, isString, bind, defaults } from 'zrender/src/core/util';
import SliderTimelineModel from './SliderTimelineModel';
import ComponentView from '../../view/Component';
import { LayoutOrient, ZRTextAlign, ZRTextVerticalAlign, ZRElementEvent } from '../../util/types';
import TimelineModel, { TimelineDataItemOption, TimelineCheckpointStyle } from './TimelineModel';
import { TimelineChangePayload, TimelinePlayChangePayload } from './timelineAction';
import Model from '../../model/Model';
-import { PathProps } from 'zrender/src/graphic/Path';
+import { PathProps, PathStyleProps } from 'zrender/src/graphic/Path';
import Scale from '../../scale/Scale';
import OrdinalScale from '../../scale/Ordinal';
import TimeScale from '../../scale/Time';
import IntervalScale from '../../scale/Interval';
import { VectorArray } from 'zrender/src/core/vector';
+import ZRText from 'zrender/src/graphic/Text';
const PI = Math.PI;
@@ -89,10 +90,15 @@ class SliderTimelineView extends TimelineView {
private _currentPointer: TimelineSymbol;
+ private _progressLine: graphic.Line;
+
private _mainGroup: graphic.Group;
private _labelGroup: graphic.Group;
+ private _tickSymbols: graphic.Path[];
+ private _tickLabels: graphic.Text[];
+
init(ecModel: GlobalModel, api: ExtensionAPI) {
this.api = api;
@@ -133,6 +139,8 @@ class SliderTimelineView extends TimelineView {
}
this._doPlayStop();
+
+ this._updateTicksStatus();
}
/**
@@ -150,7 +158,7 @@ class SliderTimelineView extends TimelineView {
this._clearTimer();
}
- _layout(timelineModel: SliderTimelineModel, api: ExtensionAPI): LayoutInfo {
+ private _layout(timelineModel: SliderTimelineModel, api: ExtensionAPI): LayoutInfo {
const labelPosOpt = timelineModel.get(['label', 'position']);
const orient = timelineModel.get('orient');
const viewRect = getViewRect(timelineModel, api);
@@ -250,7 +258,7 @@ class SliderTimelineView extends TimelineView {
};
}
- _position(layoutInfo: LayoutInfo, timelineModel: SliderTimelineModel) {
+ private _position(layoutInfo: LayoutInfo, timelineModel: SliderTimelineModel) {
// Position is be called finally, because bounding rect is needed for
// adapt content to fill viewRect (auto adapt offset).
@@ -321,7 +329,7 @@ class SliderTimelineView extends TimelineView {
}
}
- _createAxis(layoutInfo: LayoutInfo, timelineModel: SliderTimelineModel) {
+ private _createAxis(layoutInfo: LayoutInfo, timelineModel: SliderTimelineModel) {
const data = timelineModel.getData();
const axisType = timelineModel.get('axisType');
@@ -344,13 +352,13 @@ class SliderTimelineView extends TimelineView {
return axis;
}
- _createGroup(key: '_mainGroup' | '_labelGroup') {
+ private _createGroup(key: '_mainGroup' | '_labelGroup') {
const newGroup = this[key] = new graphic.Group();
this.group.add(newGroup);
return newGroup;
}
- _renderAxisLine(
+ private _renderAxisLine(
layoutInfo: LayoutInfo,
group: graphic.Group,
axis: TimelineAxis,
@@ -362,7 +370,7 @@ class SliderTimelineView extends TimelineView {
return;
}
- group.add(new graphic.Line({
+ const line = new graphic.Line({
shape: {
x1: axisExtent[0], y1: 0,
x2: axisExtent[1], y2: 0
@@ -373,13 +381,27 @@ class SliderTimelineView extends TimelineView {
),
silent: true,
z2: 1
- }));
+ });
+ group.add(line);
+
+ const progressLine = this._progressLine = new graphic.Line({
+ shape: {
+ x1: axisExtent[0],
+ x2: this._currentPointer
+ ? this._currentPointer.x : axisExtent[0],
+ y1: 0, y2: 0
+ },
+ style: defaults(
+ { lineCap: 'round', lineWidth: line.style.lineWidth } as PathStyleProps,
+ timelineModel.getModel(['progress', 'lineStyle']).getLineStyle()
+ ),
+ silent: true,
+ z2: 1
+ });
+ group.add(progressLine);
}
- /**
- * @private
- */
- _renderAxisTick(
+ private _renderAxisTick(
layoutInfo: LayoutInfo,
group: graphic.Group,
axis: TimelineAxis,
@@ -389,18 +411,24 @@ class SliderTimelineView extends TimelineView {
// Show all ticks, despite ignoring strategy.
const ticks = axis.scale.getTicks();
+ this._tickSymbols = [];
+
// The value is dataIndex, see the costomized scale.
- each(ticks, function (value) {
+ each(ticks, (value) => {
const tickCoord = axis.dataToCoord(value);
const itemModel = data.getItemModel<TimelineDataItemOption>(value);
const itemStyleModel = itemModel.getModel('itemStyle');
const hoverStyleModel = itemModel.getModel(['emphasis', 'itemStyle']);
+ const progressStyleModel = itemModel.getModel(['progress', 'itemStyle']);
+
const symbolOpt = {
position: [tickCoord, 0],
onclick: bind(this._changeTimeline, this, value)
};
const el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt);
el.ensureState('emphasis').style = hoverStyleModel.getItemStyle();
+ el.ensureState('progress').style = progressStyleModel.getItemStyle();
+
enableHoverEmphasis(el);
const ecData = graphic.getECData(el);
@@ -412,13 +440,11 @@ class SliderTimelineView extends TimelineView {
ecData.dataIndex = ecData.dataModel = null;
}
- }, this);
+ this._tickSymbols.push(el);
+ });
}
- /**
- * @private
- */
- _renderAxisLabel(
+ private _renderAxisLabel(
layoutInfo: LayoutInfo,
group: graphic.Group,
axis: TimelineAxis,
@@ -433,13 +459,17 @@ class SliderTimelineView extends TimelineView {
const data = timelineModel.getData();
const labels = axis.getViewLabels();
- each(labels, function (labelItem) {
+ this._tickLabels = [];
+
+ each(labels, (labelItem) => {
// The tickValue is dataIndex, see the costomized scale.
const dataIndex = labelItem.tickValue;
const itemModel = data.getItemModel<TimelineDataItemOption>(dataIndex);
const normalLabelModel = itemModel.getModel('label');
const hoverLabelModel = itemModel.getModel(['emphasis', 'label']);
+ const progressLabelModel = itemModel.getModel(['progress', 'label']);
+
const tickCoord = axis.dataToCoord(labelItem.tickValue);
const textEl = new graphic.Text({
x: tickCoord,
@@ -455,17 +485,17 @@ class SliderTimelineView extends TimelineView {
});
textEl.ensureState('emphasis').style = createTextStyle(hoverLabelModel);
+ textEl.ensureState('progress').style = createTextStyle(progressLabelModel);
group.add(textEl);
enableHoverEmphasis(textEl);
- }, this);
+ this._tickLabels.push(textEl);
+
+ });
}
- /**
- * @private
- */
- _renderControl(
+ private _renderControl(
layoutInfo: LayoutInfo,
group: graphic.Group,
axis: TimelineAxis,
@@ -521,7 +551,7 @@ class SliderTimelineView extends TimelineView {
}
}
- _renderCurrentPointer(
+ private _renderCurrentPointer(
layoutInfo: LayoutInfo,
group: graphic.Group,
axis: TimelineAxis,
@@ -538,10 +568,10 @@ class SliderTimelineView extends TimelineView {
pointer.draggable = true;
pointer.drift = bind(me._handlePointerDrag, me);
pointer.ondragend = bind(me._handlePointerDragend, me);
- pointerMoveTo(pointer, currentIndex, axis, timelineModel, true);
+ pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel, true);
},
onUpdate(pointer: TimelineSymbol) {
- pointerMoveTo(pointer, currentIndex, axis, timelineModel);
+ pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel);
}
};
@@ -551,7 +581,7 @@ class SliderTimelineView extends TimelineView {
);
}
- _handlePlayClick(nextState: boolean) {
+ private _handlePlayClick(nextState: boolean) {
this._clearTimer();
this.api.dispatchAction({
type: 'timelinePlayChange',
@@ -560,16 +590,16 @@ class SliderTimelineView extends TimelineView {
} as TimelinePlayChangePayload);
}
- _handlePointerDrag(dx: number, dy: number, e: ZRElementEvent) {
+ private _handlePointerDrag(dx: number, dy: number, e: ZRElementEvent) {
this._clearTimer();
this._pointerChangeTimeline([e.offsetX, e.offsetY]);
}
- _handlePointerDragend(e: ZRElementEvent) {
+ private _handlePointerDragend(e: ZRElementEvent) {
this._pointerChangeTimeline([e.offsetX, e.offsetY], true);
}
- _pointerChangeTimeline(mousePos: number[], trigger?: boolean) {
+ private _pointerChangeTimeline(mousePos: number[], trigger?: boolean) {
let toCoord = this._toAxisCoord(mousePos)[0];
const axis = this._axis;
@@ -581,6 +611,9 @@ class SliderTimelineView extends TimelineView {
this._currentPointer.x = toCoord;
this._currentPointer.markRedraw();
+ this._progressLine.shape.x2 = toCoord;
+ this._progressLine.dirty();
+
const targetDataIndex = this._findNearestTick(toCoord);
const timelineModel = this.model;
@@ -592,7 +625,7 @@ class SliderTimelineView extends TimelineView {
}
}
- _doPlayStop() {
+ private _doPlayStop() {
this._clearTimer();
if (this.model.getPlayState()) {
@@ -610,12 +643,12 @@ class SliderTimelineView extends TimelineView {
}
}
- _toAxisCoord(vertex: number[]) {
+ private _toAxisCoord(vertex: number[]) {
const trans = this._mainGroup.getLocalTransform();
return graphic.applyTransform(vertex, trans, true);
}
- _findNearestTick(axisCoord: number) {
+ private _findNearestTick(axisCoord: number) {
const data = this.model.getData();
let dist = Infinity;
let targetDataIndex;
@@ -633,14 +666,14 @@ class SliderTimelineView extends TimelineView {
return targetDataIndex;
}
- _clearTimer() {
+ private _clearTimer() {
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
}
- _changeTimeline(nextIndex: number | '+' | '-') {
+ private _changeTimeline(nextIndex: number | '+' | '-') {
const currentIndex = this.model.getCurrentIndex();
if (nextIndex === '+') {
@@ -657,6 +690,23 @@ class SliderTimelineView extends TimelineView {
} as TimelineChangePayload);
}
+ private _updateTicksStatus() {
+ const currentIndex = this.model.getCurrentIndex();
+ const tickSymbols = this._tickSymbols;
+ const tickLabels = this._tickLabels;
+ if (!(tickSymbols || tickLabels)) {
+ return;
+ }
+
+ const len = (tickSymbols || tickLabels).length;
+
+ for (let i = 0; i < len; i++) {
+ tickSymbols && tickSymbols[i]
+ && tickSymbols[i].toggleState('progress', i <= currentIndex);
+ tickLabels && tickLabels[i]
+ && tickLabels[i].toggleState('progress', i <= currentIndex);
+ }
+ }
}
function createScaleByModel(model: SliderTimelineModel, axisType?: string): Scale {
@@ -782,6 +832,7 @@ function giveSymbol(
function pointerMoveTo(
pointer: TimelineSymbol,
+ progressLine: graphic.Line,
dataIndex: number,
axis: TimelineAxis,
timelineModel: SliderTimelineModel,
@@ -795,20 +846,28 @@ function pointerMoveTo(
const toCoord = axis.dataToCoord(timelineModel.getData().get('value', dataIndex));
if (noAnimation || !pointerModel.get('animation', true)) {
- pointer.x = toCoord;
- pointer.y = 0;
+ pointer.attr({
+ x: toCoord,
+ y: 0
+ });
+ progressLine && progressLine.attr({
+ shape: { x2: toCoord }
+ });
}
else {
+ const animationCfg = {
+ duration: pointerModel.get('animationDuration', true),
+ easing: pointerModel.get('animationEasing', true)
+ };
pointer.stopAnimation(null, true);
pointer.animateTo({
x: toCoord,
y: 0
- }, {
- duration: pointerModel.get('animationDuration', true),
- easing: pointerModel.get('animationEasing', true)
- });
+ }, animationCfg);
+ progressLine && progressLine.animateTo({
+ shape: { x2: toCoord }
+ }, animationCfg);
}
- pointer.markRedraw();
}
diff --git a/src/component/timeline/TimelineModel.ts b/src/component/timeline/TimelineModel.ts
index 385854c..2ddea11 100644
--- a/src/component/timeline/TimelineModel.ts
+++ b/src/component/timeline/TimelineModel.ts
@@ -84,6 +84,13 @@ export interface TimelineDataItemOption extends SymbolOptionMixin {
checkpointStyle?: TimelineCheckpointStyle
}
+ // Style in progress
+ progress?: {
+ lineStyle?: TimelineLineStyleOption
+ itemStyle?: ItemStyleOption
+ label?: TimelineLabelOption
+ }
+
tooltip?: boolean
}
@@ -139,6 +146,14 @@ export interface TimelineOption extends ComponentOption, BoxLayoutOptionMixin, S
label?: TimelineLabelOption
}
+
+ // Style in progress
+ progress?: {
+ lineStyle?: TimelineLineStyleOption
+ itemStyle?: ItemStyleOption
+ label?: TimelineLabelOption
+ }
+
data?: (OptionDataValue | TimelineDataItemOption)[]
}
class TimelineModel extends ComponentModel<TimelineOption> {
diff --git a/src/echarts.ts b/src/echarts.ts
index de8aeab..3e745df 100644
--- a/src/echarts.ts
+++ b/src/echarts.ts
@@ -1968,45 +1968,50 @@ class ECharts extends Eventful {
}
ecIns.getZr().storage.traverse(function (el: ECElement) {
- const newStates = [];
- const oldStates = el.currentStates;
-
// Not applied on removed elements, it may still in fading.
if (graphic.isElementRemoved(el)) {
return;
}
-
- // Keep other states.
- for (let i = 0; i < oldStates.length; i++) {
- const stateName = oldStates[i];
- if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) {
- newStates.push(stateName);
- }
- }
-
- // Only use states when it's exists.
- if (el.selected && el.states.select) {
- newStates.push('select');
- }
- if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) {
- newStates.push('emphasis');
- }
- else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) {
- newStates.push('blur');
- }
- el.useStates(newStates);
+ applyElementStates(el);
});
ecIns[STATUS_NEEDS_UPDATE_KEY] = false;
};
+ function applyElementStates(el: ECElement) {
+ const newStates = [];
+
+ const oldStates = el.currentStates;
+ // Keep other states.
+ for (let i = 0; i < oldStates.length; i++) {
+ const stateName = oldStates[i];
+ if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) {
+ newStates.push(stateName);
+ }
+ }
+
+ // Only use states when it's exists.
+ if (el.selected && el.states.select) {
+ newStates.push('select');
+ }
+ if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) {
+ newStates.push('emphasis');
+ }
+ else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) {
+ newStates.push('blur');
+ }
+ el.useStates(newStates);
+ }
+
function updateHoverLayerStatus(ecIns: ECharts, ecModel: GlobalModel): void {
const zr = ecIns._zr;
const storage = zr.storage;
let elCount = 0;
storage.traverse(function (el) {
- elCount++;
+ if (!el.isGroup) {
+ elCount++;
+ }
});
if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) {
@@ -2159,14 +2164,7 @@ class ECharts extends Eventful {
// The use higlighted and selected flag to toggle states.
if (el.__dirty) {
- const states = [];
- if ((el as ECElement).selected) {
- states.push('select');
- }
- if ((el as ECElement).hoverState) {
- states.push('emphasis');
- }
- el.useStates(states);
+ applyElementStates(el);
}
}
});
diff --git a/test/timeline-finance.html b/test/timeline-finance.html
index 29e9c44..3006f01 100644
--- a/test/timeline-finance.html
+++ b/test/timeline-finance.html
@@ -83,12 +83,12 @@ var option = {
axisType: 'category',
// realtime: false,
// loop: false,
- autoPlay: true,
+ autoPlay: false,
// currentIndex: 2,
playInterval: 1000,
controlStyle: {
- showNextBtn: false,
- showPrevBtn: false,
+ showNextBtn: true,
+ showPrevBtn: true,
position: 'left'
},
data: [
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org