提交 459a9d83 编写于 作者: P pissang

feat(timeline): optimize timeline style. add progress config

上级 26ca0b5b
......@@ -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: []
});
......
......@@ -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();
}
......
......@@ -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> {
......
......@@ -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);
}
}
});
......
......@@ -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: [
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册