blob: 619f468f3a13ab37a9bce6d7b82fa643a37caca9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import ComponentModel from '../../model/Component';
import List from '../../data/List';
import {
ComponentOption,
BoxLayoutOptionMixin,
LayoutOrient,
SymbolOptionMixin,
LineStyleOption,
ItemStyleOption,
LabelOption,
OptionDataValue,
ZRColor,
ColorString,
CommonTooltipOption,
CallbackDataParams,
ZREasing
} from '../../util/types';
import Model from '../../model/Model';
import GlobalModel, { GlobalModelSetOptionOpts } from '../../model/Global';
import { each, isObject, clone } from 'zrender/src/core/util';
import { convertOptionIdName, getDataItemValue } from '../../util/model';
export interface TimelineControlStyle extends ItemStyleOption {
show?: boolean
showPlayBtn?: boolean
showPrevBtn?: boolean
showNextBtn?: boolean
itemSize?: number
itemGap?: number
position?: 'left' | 'right' | 'top' | 'bottom'
playIcon?: string
stopIcon?: string
prevIcon?: string
nextIcon?: string
// Can be a percent value relative to itemSize
playBtnSize?: number | string
stopBtnSize?: number | string
nextBtnSize?: number | string
prevBtnSize?: number | string
}
export interface TimelineCheckpointStyle extends ItemStyleOption,
SymbolOptionMixin {
animation?: boolean
animationDuration?: number
animationEasing?: ZREasing
}
interface TimelineLineStyleOption extends LineStyleOption {
show?: boolean
}
interface TimelineLabelOption extends Omit<LabelOption, 'position'> {
show?: boolean
// number can be distance to the timeline axis. sign will determine the side.
position?: 'auto' | 'left' | 'right' | 'top' | 'bottom' | number
interval?: 'auto' | number
formatter?: string | ((value: string | number, index: number) => string)
}
export interface TimelineDataItemOption extends SymbolOptionMixin {
value?: OptionDataValue
itemStyle?: ItemStyleOption
label?: TimelineLabelOption
checkpointStyle?: TimelineCheckpointStyle
emphasis?: {
itemStyle?: ItemStyleOption
label?: TimelineLabelOption
checkpointStyle?: TimelineCheckpointStyle
}
// Style in progress
progress?: {
lineStyle?: TimelineLineStyleOption
itemStyle?: ItemStyleOption
label?: TimelineLabelOption
}
tooltip?: boolean
}
export interface TimelineOption extends ComponentOption, BoxLayoutOptionMixin, SymbolOptionMixin {
mainType?: 'timeline'
backgroundColor?: ZRColor
borderColor?: ColorString
borderWidth?: number
tooltip?: CommonTooltipOption<CallbackDataParams> & {
trigger?: 'item'
}
show?: boolean
axisType?: 'category' | 'time' | 'value'
currentIndex?: number
autoPlay?: boolean
rewind?: boolean
loop?: boolean
playInterval?: number
realtime?: boolean
controlPosition?: 'left' | 'right' | 'top' | 'bottom'
padding?: number | number[]
orient?: LayoutOrient
inverse?: boolean
// If not specified, options will be changed by "normalMerge".
// If specified, options will be changed by "replaceMerge".
replaceMerge?: GlobalModelSetOptionOpts['replaceMerge']
lineStyle?: TimelineLineStyleOption
itemStyle?: ItemStyleOption
checkpointStyle?: TimelineCheckpointStyle
controlStyle?: TimelineControlStyle
label?: TimelineLabelOption
emphasis?: {
lineStyle?: TimelineLineStyleOption
itemStyle?: ItemStyleOption
checkpointStyle?: TimelineCheckpointStyle
controlStyle?: TimelineControlStyle
label?: TimelineLabelOption
}
// Style in progress
progress?: {
lineStyle?: TimelineLineStyleOption
itemStyle?: ItemStyleOption
label?: TimelineLabelOption
}
data?: (OptionDataValue | TimelineDataItemOption)[]
}
class TimelineModel extends ComponentModel<TimelineOption> {
static type = 'timeline';
type = TimelineModel.type;
layoutMode = 'box';
private _data: List<TimelineModel>;
private _names: string[];
/**
* @override
*/
init(option: TimelineOption, parentModel: Model, ecModel: GlobalModel) {
this.mergeDefaultAndTheme(option, ecModel);
this._initData();
}
/**
* @override
*/
mergeOption(option: TimelineOption) {
super.mergeOption.apply(this, arguments as any);
this._initData();
}
setCurrentIndex(currentIndex: number) {
if (currentIndex == null) {
currentIndex = this.option.currentIndex;
}
const count = this._data.count();
if (this.option.loop) {
currentIndex = (currentIndex % count + count) % count;
}
else {
currentIndex >= count && (currentIndex = count - 1);
currentIndex < 0 && (currentIndex = 0);
}
this.option.currentIndex = currentIndex;
}
/**
* @return {number} currentIndex
*/
getCurrentIndex() {
return this.option.currentIndex;
}
/**
* @return {boolean}
*/
isIndexMax() {
return this.getCurrentIndex() >= this._data.count() - 1;
}
/**
* @param {boolean} state true: play, false: stop
*/
setPlayState(state: boolean) {
this.option.autoPlay = !!state;
}
/**
* @return {boolean} true: play, false: stop
*/
getPlayState() {
return !!this.option.autoPlay;
}
/**
* @private
*/
_initData() {
const thisOption = this.option;
const dataArr = thisOption.data || [];
const axisType = thisOption.axisType;
const names: string[] = this._names = [];
let processedDataArr: TimelineOption['data'];
if (axisType === 'category') {
processedDataArr = [];
each(dataArr, function (item, index) {
const value = convertOptionIdName(getDataItemValue(item), '');
let newItem;
if (isObject(item)) {
newItem = clone(item);
(newItem as TimelineDataItemOption).value = index;
}
else {
newItem = index;
}
processedDataArr.push(newItem);
names.push(value);
});
}
else {
processedDataArr = dataArr;
}
const dimType = ({
category: 'ordinal',
time: 'time',
value: 'number'
})[axisType] || 'number';
const data = this._data = new List([{
name: 'value', type: dimType
}], this);
data.initData(processedDataArr, names);
}
getData() {
return this._data;
}
/**
* @public
* @return {Array.<string>} categoreis
*/
getCategories() {
if (this.get('axisType') === 'category') {
return this._names.slice();
}
}
/**
* @protected
*/
static defaultOption: TimelineOption = {
zlevel: 0, // 一级层叠
z: 4, // 二级层叠
show: true,
axisType: 'time', // 模式是时间类型,支持 value, category
realtime: true,
left: '20%',
top: null,
right: '20%',
bottom: 0,
width: null,
height: 40,
padding: 5,
controlPosition: 'left', // 'left' 'right' 'top' 'bottom' 'none'
autoPlay: false,
rewind: false, // 反向播放
loop: true,
playInterval: 2000, // 播放时间间隔,单位ms
currentIndex: 0,
itemStyle: {},
label: {
color: '#000'
},
data: []
};
}
export default TimelineModel;