blob: 6355e994d08e9250f0f3d8a7f163e2a72f98f2c1 [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.
*/
(function (exports) {
var transitionPlayer = {};
/**
* @usage
* ```js
* // Initialize with an array of echarts option info:
* var player = transitionPlayer.create({
*
* // The echarts instance or chart instance getter.
* chart: function () {
* return myChart;
* },
* seriesIndex: 0,
* replaceMerge: ['xAxis', 'yAxis']
*
* // The data meta info used to determine how to
* // make transition mapping.
* // The strategy: If `uniqueDimension` provided and is a common
* // dimension, use `uniqueDimension`.
* dataMeta: {
* aaa: {
* dimensions: ['qqq', 'www', 'eee', 'rrr']
* },
* bbb: {
* dimensions: ['ccc', 'www', 'eee'],
* uniqueDimension: 'www',
* dividingMethod: 'duplicate'
* },
* ...
* },
*
* // echarts option collection:
* optionList: [
* // dataMetaKey is the key of 'dataMeta'.
* { key: 'Time_Income_Bar', option: option0, dataMetaKey: 'aaa' },
* { key: 'Population_Income_Scatter', option: option1, dataMetaKey: 'bbb' },
* { key: 'Time_Income_Pie', option: option2, dataMetaKey: 'aaa' },
* ...
* ]
* });
*
* // Then start to play:
* player.next(); // Display next option (from the first option).
* player.previous(); // Display previous optoin.
* player.go('Time_Income_Pie'); // Display the specified option.
* player.getOptionKeys(); // return `['Time_Income_Bar', 'Population_Income_Scatter', 'Time_Income_Pie']`
* ```
*
* @parma opt See the constructor of `TransitionPlayer`.
*/
transitionPlayer.create = function (opt) {
return new TransitionPlayer(opt);
};
/**
* @param opt
* @param opt.chart
* (EChartsInstance | () => EChartsInstance)
* echarts instance or echarts instance getter.
* @param opt.dataMeta
* {
* [dataMetaKey in string]: {
* dimensions: string[];
* uniqueDimension?: string;
* dividingMethod?: 'split' | 'duplicate'
* }
* }
* @param opt.optionList
* {
* key: string;
* option: EChartsOption;
* dataMetaKey: string;
* }[]
* @param opt.seriesIndex number
* Target series index to be transitioned.
* @param opt.replaceMerge? string[]
*/
function TransitionPlayer(opt) {
assert(
opt.chart
&& isObject(opt.dataMeta)
&& isArray(opt.optionList)
&& opt.seriesIndex != null
&& opt.optionList.length
);
this._chart = opt.chart;
this._dataMeta = opt.dataMeta;
var optionList = this._optionList = opt.optionList;
var optionMap = this._optionMap = {};
this._replaceMerge = opt.replaceMerge;
this._seriesIndex = opt.seriesIndex;
this._currOptionIdx = null;
for (var i = 0; i < optionList.length; i++) {
var optionWrap = optionList[i];
var optionKey = optionWrap.key;
if (optionKey != null) {
assert(!hasOwn(optionMap, optionKey), 'option key duplicat: ' + optionKey);
optionMap[optionKey] = i;
}
}
}
var proto = TransitionPlayer.prototype;
proto.next = function () {
var optionList = this._optionList;
var newOptionIdx = this._currOptionIdx == null
? 0
: Math.min(optionList.length - 1, this._currOptionIdx + 1);
this._doChangeOption(newOptionIdx);
};
proto.previous = function () {
var optionList = this._optionList;
var newOptionIdx = this._currOptionIdx == null
? optionList.length - 1
: Math.max(0, this._currOptionIdx - 1);
this._doChangeOption(newOptionIdx);
};
/**
* @param optionKey string
*/
proto.go = function (optionKey) {
var newOptionIdx = getMapValue(this._optionMap, optionKey);
assert(newOptionIdx != null, 'Can not find option by option key: ' + optionKey);
this._doChangeOption(newOptionIdx);
};
proto._doChangeOption = function (newOptionIdx) {
var optionList = this._optionList;
var oldOptionWrap = this._currOptionIdx != null ? optionList[this._currOptionIdx] : null;
var newOptionWrap = optionList[newOptionIdx];
var dataMeta = this._dataMeta;
var targetSeriesIndex = this._seriesIndex;
var transitionOpt = {
// If can not find mapped dimensions, do not make transition animation
// by default, becuase this transition probably bring about misleading.
to: { seriesIndex: targetSeriesIndex }
};
if (oldOptionWrap) {
var commonDimension =
findCommonDimension(oldOptionWrap, newOptionWrap)
|| findCommonDimension(newOptionWrap, oldOptionWrap);
if (commonDimension != null) {
transitionOpt = {
from: {
seriesIndex: targetSeriesIndex,
dimension: commonDimension
},
to: {
seriesIndex: targetSeriesIndex,
dimension: commonDimension,
},
dividingMethod: dataMeta.dividingMethod
};
}
}
this._currOptionIdx = newOptionIdx;
this._getChart().setOption(newOptionWrap.option, {
replaceMerge: this._replaceMerge,
transition: transitionOpt
});
function findCommonDimension(optionWrapA, optionWrapB) {
var metaA = getMapValue(dataMeta, optionWrapA.dataMetaKey);
var metaB = getMapValue(dataMeta, optionWrapB.dataMetaKey);
var uniqueDimensionB = metaB.uniqueDimension;
if (uniqueDimensionB != null && metaA.dimensions.indexOf(uniqueDimensionB) >= 0) {
return uniqueDimensionB;
}
}
};
proto._getChart = function () {
return isFunction(this._chart) ? this._chart() : this._chart;
};
/**
* @return string[]
*/
proto.getOptionKeys = function () {
var optionKeys = [];
var optionList = this._optionList;
for (var i = 0; i < optionList.length; i++) {
optionKeys.push(optionList[i].key);
}
return optionKeys;
};
function assert(cond, msg) {
if (!cond) {
throw new Error(msg || 'transition player error');
}
}
function isObject(value) {
const type = typeof value;
return type === 'function' || (!!value && type === 'object');
}
function isArray(value) {
if (Array.isArray) {
return Array.isArray(value);
}
return Object.prototype.toString.call(value) === '[object Array]';
}
function isFunction(value) {
return typeof value === 'function';
}
function hasOwn(obj, key) {
return obj.hasOwnProperty(key);
}
function getMapValue(map, key) {
return (key != null && hasOwn(map, key)) ? map[key] : null;
}
exports.transitionPlayer = transitionPlayer;
})(this);