提交 e2565174 编写于 作者: O Ovilia

WIP(bar-race): most logic without label animation

上级 e2dac513
/*
* 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.
*/
// @ts-nocheck
import * as echarts from '../echarts';
import * as zrUtil from 'zrender/src/core/util';
/**
* @payload
* @property {string} [componentType=series]
* @property {number} [dx]
* @property {number} [dy]
* @property {number} [zoom]
* @property {number} [originX]
* @property {number} [originY]
*/
echarts.registerAction({
type: 'axisOrderChanged',
event: 'axisOrderChanged',
update: 'update'
}, function (payload, ecModel) {
const componentType = payload.componentType || 'series';
ecModel.eachComponent(
{ mainType: componentType, query: payload },
function (componentModel) {
}
);
});
......@@ -24,6 +24,7 @@ import {layout, largeLayout} from '../layout/barGrid';
import '../coord/cartesian/Grid';
import './bar/BarSeries';
import './bar/BarView';
import '../action/axisOrderChanged';
// In case developer forget to include grid component
import '../component/gridSimple';
......
......@@ -35,10 +35,10 @@ import {throttle} from '../../util/throttle';
import {createClipPath} from '../helper/createClipPathFromCoordSys';
import Sausage from '../../util/shape/sausage';
import ChartView from '../../view/Chart';
import List from '../../data/List';
import List, {DefaultDataVisual} from '../../data/List';
import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../ExtensionAPI';
import { StageHandlerProgressParams, ZRElementEvent, ColorString } from '../../util/types';
import { StageHandlerProgressParams, ZRElementEvent, ColorString, OrdinalNumber, Payload } from '../../util/types';
import BarSeriesModel, { BarSeriesOption, BarDataItemOption } from './BarSeries';
import type Axis2D from '../../coord/cartesian/Axis2D';
import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
......@@ -46,6 +46,7 @@ import type { RectLike } from 'zrender/src/core/BoundingRect';
import type Model from '../../model/Model';
import { isCoordinateSystemType } from '../../coord/CoordinateSystem';
import { getDefaultLabel } from '../helper/labelHelper';
import OrdinalScale from '../../scale/Ordinal';
const BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'borderWidth'] as const;
const _eventPos = [0, 0];
......@@ -138,22 +139,31 @@ class BarView extends ChartView {
}
_renderNormal(seriesModel: BarSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
const that = this;
const group = this.group;
const data = seriesModel.getData();
const oldData = this._data;
const coord = seriesModel.coordinateSystem;
const baseAxis = coord.getBaseAxis();
let valueAxis: Axis2D;
let isHorizontalOrRadial: boolean;
if (coord.type === 'cartesian2d') {
isHorizontalOrRadial = (baseAxis as Axis2D).isHorizontal();
valueAxis = coord.getOtherAxis(baseAxis as Axis2D);
}
else if (coord.type === 'polar') {
isHorizontalOrRadial = baseAxis.dim === 'angle';
}
const animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null;
const axisAnimationModel = baseAxis.model;
const axis2DModel = (baseAxis as Axis2D).model;
const realtimeSort = coord.type === 'cartesian2d'
&& axis2DModel.get('sort') && axis2DModel.get('realtimeSort');
let isFirstSortData = true;
const needsClip = seriesModel.get('clip', true);
const coordSysClipArea = getClipArea(coord, data);
......@@ -170,6 +180,30 @@ class BarView extends ChartView {
const bgEls: BarView['_backgroundEls'] = [];
const oldBgEls = this._backgroundEls;
let during: () => void = null;
if (coord.type === 'cartesian2d' && realtimeSort && isFirstSortData) {
const oldOrder = (baseAxis as Axis2D).model.option.categoryIndices;
const orderMap = (idx: number) => {
return data.get(valueAxis.dim, idx) as number;
};
const newOrder = this._dataSort(data, orderMap);
const orderChanged = !zrUtil.isEqualArray(oldOrder, newOrder);
during = orderChanged
? () => {
that._updateSort(
data,
baseAxis as Axis2D,
isHorizontalOrRadial,
ecModel,
api
);
}
: null;
}
data.diff(oldData)
.add(function (dataIndex) {
const itemModel = data.getItemModel(dataIndex);
......@@ -198,8 +232,10 @@ class BarView extends ChartView {
}
}
isFirstSortData = false;
const el = elementCreator[coord.type](
dataIndex, layout, isHorizontalOrRadial, animationModel, false, roundCap
dataIndex, layout, isHorizontalOrRadial, animationModel, false, during, roundCap
);
data.setItemGraphicEl(dataIndex, el);
group.add(el);
......@@ -238,15 +274,49 @@ class BarView extends ChartView {
}
}
isFirstSortData = false;
if (el) {
clearStates(el);
updateProps(el as Path, {
shape: layout
}, animationModel, newIndex);
if (coord.type === 'cartesian2d'
&& baseAxis.type === 'category' && (baseAxis as Axis2D).model.get('realtimeSort')
) {
const rect = layout as RectShape;
let seriesShape, axisShape;
if (baseAxis.dim === 'x') {
axisShape = {
x: rect.x
};
seriesShape = {
y: rect.y,
width: rect.width,
height: rect.height
};
}
else {
axisShape = {
y: rect.y
};
seriesShape = {
x: rect.x,
width: rect.width,
height: rect.height
};
}
updateProps(el as Path, { shape: seriesShape }, animationModel, newIndex, null, during);
updateProps(el as Path, { shape: axisShape }, axisAnimationModel, newIndex, null);
}
else {
updateProps(el as Path, {
shape: layout
}, animationModel, newIndex, null);
}
}
else {
el = elementCreator[coord.type](
newIndex, layout, isHorizontalOrRadial, animationModel, true, roundCap
newIndex, layout, isHorizontalOrRadial, animationModel, true, during, roundCap
);
}
......@@ -303,6 +373,56 @@ class BarView extends ChartView {
createLarge(seriesModel, this.group, true);
}
_dataSort(
data: List<BarSeriesModel, DefaultDataVisual>,
map: ((idx: number) => number)
): OrdinalNumber[] {
const dataValues: number[] = [];
const order: OrdinalNumber[] = [];
data.each(idx => {
const value = map(idx);
for (let i = 0, len = dataValues.length; i < len; ++i) {
if (dataValues[i] < value) {
dataValues.splice(i, 0, value);
for (let j = 0; j < len; ++j) {
if (order[j] >= i) {
++order[j];
}
}
order.push(i);
return;
}
}
dataValues.push(value);
order.push(order.length);
});
return order;
}
_updateSort(
data: List<BarSeriesModel, DefaultDataVisual>,
baseAxis: Axis2D,
isHorizontal: boolean,
ecModel: GlobalModel,
api: ExtensionAPI
) {
const orderMap = (idx: number) => {
const shape = (data.getItemGraphicEl(idx) as Rect).shape;
return isHorizontal ? shape.y + shape.height : shape.x + shape.width;
}
const newOrder = this._dataSort(data, orderMap);
const hasOrderChanged = baseAxis.setCategoryIndices(newOrder);
if (hasOrderChanged) {
const action = {
type: 'axisOrderChanged',
componentType: baseAxis.dim + 'Axis',
axisId: baseAxis.index
} as Payload;
api.dispatchAction(action);
}
}
remove(ecModel?: GlobalModel) {
this._clear(ecModel);
}
......@@ -387,7 +507,8 @@ const clip: {
interface ElementCreator {
(
dataIndex: number, layout: RectLayout | SectorLayout, isHorizontalOrRadial: boolean,
animationModel: BarSeriesModel, isUpdate: boolean, roundCap?: boolean
animationModel: BarSeriesModel, isUpdate: boolean, during: () => void,
roundCap?: boolean
): BarPossiblePath
}
......@@ -397,7 +518,7 @@ const elementCreator: {
cartesian2d(
dataIndex, layout: RectLayout, isHorizontal,
animationModel, isUpdate
animationModel, isUpdate, during
) {
const rect = new Rect({
shape: zrUtil.extend({}, layout),
......@@ -416,7 +537,7 @@ const elementCreator: {
animateTarget[animateProperty] = layout[animateProperty];
(isUpdate ? updateProps : initProps)(rect, {
shape: animateTarget
}, animationModel, dataIndex);
}, animationModel, dataIndex, null, during);
}
return rect;
......
......@@ -179,7 +179,7 @@ class Axis {
const ticksCoords = map(ticks, function (tickValue) {
return {
coord: this.dataToCoord(tickValue),
tickValue: this.scale instanceof OrdinalScale ? this.scale.getSortedDataIndex(tickValue) : tickValue
tickValue: this.scale instanceof OrdinalScale ? this.scale.getCategoryIndices(tickValue) : tickValue
};
}, this);
......
......@@ -18,11 +18,13 @@
*/
import Axis from '../Axis';
import { DimensionName } from '../../util/types';
import { DimensionName, OrdinalNumber } from '../../util/types';
import Scale from '../../scale/Scale';
import CartesianAxisModel, { CartesianAxisPosition } from './AxisModel';
import Grid from './Grid';
import { OptionAxisType } from '../axisCommonTypes';
import {isEqualArray} from 'zrender/src/core/util';
import OrdinalScale from '../../scale/Ordinal';
interface Axis2D {
......@@ -110,6 +112,23 @@ class Axis2D extends Axis {
return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp);
}
/**
* Set categoryIndices and return if has order changed
* @param newIndices new categoryIndices
* @return if the order has changed
*/
setCategoryIndices(newIndices: OrdinalNumber[], force?: boolean): boolean {
if (this.type !== 'category') {
return false;
}
const isOrderChanged = force || !isEqualArray(this.model.option.categoryIndices, newIndices);
if (isOrderChanged) {
this.model.option.categoryIndices = newIndices;
(this.scale as OrdinalScale).setCategoryIndices(newIndices);
}
return isOrderChanged;
}
}
......
......@@ -25,6 +25,7 @@ import Axis2D from './Axis2D';
import { AxisBaseOption } from '../axisCommonTypes';
import GridModel from './GridModel';
import { AxisBaseModel } from '../AxisBaseModel';
import {OrdinalNumber} from '../../util/types';
export type CartesianAxisPosition = 'top' | 'bottom' | 'left' | 'right';
......@@ -36,6 +37,8 @@ interface CartesianAxisOption extends AxisBaseOption {
// Offset is for multiple axis on the same position.
offset?: number;
sort?: boolean;
realtimeSort?: boolean;
categoryIndices?: OrdinalNumber[];
}
class CartesianAxisModel extends ComponentModel<CartesianAxisOption>
......@@ -78,7 +81,9 @@ const extraOption: CartesianAxisOption = {
// gridIndex: 0,
// gridId: '',
offset: 0,
sort: false
sort: false,
realtimeSort: false,
categoryIndices: []
};
axisModelCreator<CartesianAxisOption, typeof CartesianAxisModel>('x', CartesianAxisModel, extraOption);
......
......@@ -408,12 +408,21 @@ class Grid implements CoordinateSystemMaster {
* Update cartesian properties from series.
*/
private _updateScale(ecModel: GlobalModel, gridModel: GridModel): void {
const sortedDataValue: number[] = [];
const sortedDataIndex: number[] = [];
let hasCategoryIndices = false;
// Reset scale
each(this._axesList, function (axis) {
axis.scale.setExtent(Infinity, -Infinity);
if (axis.type === 'category') {
const categoryIndices = axis.model.get('categoryIndices');
if (categoryIndices && categoryIndices.length > 0) {
hasCategoryIndices = true;
(axis.scale as OrdinalScale).setCategoryIndices(categoryIndices);
}
}
});
const sortedDataValue: number[] = [];
const sortedDataIndex: number[] = [];
ecModel.eachSeries(function (seriesModel) {
if (isCartesian2D(seriesModel)) {
......@@ -437,11 +446,11 @@ class Grid implements CoordinateSystemMaster {
if (data.type === 'list') {
unionExtent(data, xAxis);
unionExtent(data, yAxis);
if (!sortedDataIndex.length) {
// Only sort by the first series
sortCategory(data, xAxis, xAxisModel, yAxis);
sortCategory(data, yAxis, yAxisModel, xAxis);
}
// if (!hasCategoryIndices && !sortedDataIndex.length) {
// // Only sort by the first series
// sortCategory(data, xAxis, xAxisModel, yAxis);
// sortCategory(data, yAxis, yAxisModel, xAxis);
// }
}
}
}, this);
......@@ -476,7 +485,6 @@ class Grid implements CoordinateSystemMaster {
}
}
sortedDataIndex.push(i);
console.log(sortedDataValue, sortedDataIndex);
return;
}
}
......@@ -484,8 +492,7 @@ class Grid implements CoordinateSystemMaster {
sortedDataValue.push(value as number);
sortedDataIndex.push(sortedDataIndex.length);
});
console.log(sortedDataValue, sortedDataIndex);
(axis.scale as OrdinalScale).setSortedDataIndices(sortedDataIndex);
(axis.scale as OrdinalScale).setCategoryIndices(sortedDataIndex);
}
}
}
......
......@@ -39,7 +39,7 @@ class OrdinalScale extends Scale {
readonly type = 'ordinal';
private _ordinalMeta: OrdinalMeta;
private _sortedDataIndices: number[];
private _categoryIndices: OrdinalNumber[];
constructor(setting?: {
......@@ -55,7 +55,7 @@ class OrdinalScale extends Scale {
ordinalMeta = new OrdinalMeta({categories: ordinalMeta});
}
this._ordinalMeta = ordinalMeta;
this._sortedDataIndices = [];
this._categoryIndices = [];
this._extent = this.getSetting('extent') || [0, ordinalMeta.categories.length - 1];
}
......@@ -76,12 +76,12 @@ class OrdinalScale extends Scale {
* Normalize given rank or name to linear [0, 1]
*/
normalize(val: OrdinalRawValue | OrdinalNumber): number {
val = this.getSortedDataIndex(this.parse(val));
val = this.getCategoryIndices(this.parse(val));
return scaleHelper.normalize(val, this._extent);
}
scale(val: number): OrdinalNumber {
val = this.getSortedDataIndex(val);
val = this.getCategoryIndices(val);
return Math.round(scaleHelper.scale(val, this._extent));
}
......@@ -103,9 +103,13 @@ class OrdinalScale extends Scale {
return;
}
getSortedDataIndex(n: OrdinalNumber): OrdinalNumber {
if (this._sortedDataIndices.length) {
return this._sortedDataIndices[n];
setCategoryIndices(indices: OrdinalNumber[]): void {
this._categoryIndices = indices;
}
getCategoryIndices(n: OrdinalNumber): OrdinalNumber {
if (this._categoryIndices.length) {
return this._categoryIndices[n];
}
else {
return n;
......@@ -124,10 +128,6 @@ class OrdinalScale extends Scale {
}
}
setSortedDataIndices(index: number[]): void {
this._sortedDataIndices = index;
}
count(): number {
return this._extent[1] - this._extent[0] + 1;
}
......
......@@ -1041,9 +1041,11 @@ function animateOrSetProps<Props>(
getAnimationDelayParams?: (el: Element<Props>, dataIndex: number) => AnimationDelayCallbackParam
},
dataIndex?: number | (() => void),
cb?: () => void
cb?: () => void,
during?: () => void
) {
if (typeof dataIndex === 'function') {
during = cb;
cb = dataIndex;
dataIndex = null;
}
......@@ -1080,7 +1082,8 @@ function animateOrSetProps<Props>(
delay: animationDelay || 0,
easing: animationEasing,
done: cb,
force: !!cb
force: !!cb || !!during,
during: during
})
: (el.stopAnimation(), el.attr(props), cb && cb());
}
......@@ -1113,9 +1116,10 @@ function updateProps<Props>(
// TODO: TYPE AnimatableModel
animatableModel?: Model<AnimationOptionMixin>,
dataIndex?: number | (() => void),
cb?: () => void
cb?: () => void,
during?: () => void
) {
animateOrSetProps(true, el, props, animatableModel, dataIndex, cb);
animateOrSetProps(true, el, props, animatableModel, dataIndex, cb, during);
}
export {updateProps};
......@@ -1133,9 +1137,10 @@ export function initProps<Props>(
props: Props,
animatableModel?: Model<AnimationOptionMixin>,
dataIndex?: number | (() => void),
cb?: () => void
cb?: () => void,
during?: () => void
) {
animateOrSetProps(false, el, props, animatableModel, dataIndex, cb);
animateOrSetProps(false, el, props, animatableModel, dataIndex, cb, during);
}
/**
......
<!--
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.
-->
<html>
<head>
<meta charset="utf-8">
<script src="lib/esl.js"></script>
<script src="lib/config.js"></script>
<script src="lib/jquery.min.js"></script>
<script src="lib/facePrint.js"></script>
<script src="lib/testHelper.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<style>
h1 {
line-height: 60px;
height: 60px;
background: #146402;
text-align: center;
font-weight: bold;
color: #eee;
font-size: 14px;
}
.chart {
height: 600px;
}
</style>
<button id='btn-1'>A++</button>
<button id='btn-2'>B++</button>
<button id='btn-3'>C++</button>
<button id='btn-4'>D++</button>
<div id="main" class="chart"></div>
<script>
require(
(testHelper.hasURLParam('en')
? [
'echarts',
// 'echarts/lang/en',
]
: [
'echarts'
]
).concat(
[
// 'echarts/chart/bar',
// 'echarts/chart/line',
// 'echarts/component/legend',
// 'echarts/component/graphic',
// 'echarts/component/grid',
// 'echarts/component/tooltip',
// 'echarts/component/brush',
// 'echarts/component/toolbox',
// 'echarts/component/title',
// 'zrender/vml/vml'
]
),
function (echarts) {
var chart = echarts.init(document.getElementById('main'), null, {
// renderer: 'svg'
});
var option = {
xAxis: {
},
yAxis: {
type: 'category',
data: ['A', 'B', 'C', 'D'],
sort: true,
realtimeSort: true,
inverse: true,
axisLabel: {
formatter: function (label, id) {
return label + '-' + id;
}
},
animationDuration: 300,
animationDurationUpdate: 300
},
series: [{
name: 'X',
type: 'bar',
data: [120, 200, 90, 40],
label: {
show: true,
position: 'right',
formatter: '{c}元'
}
// }, {
// name: 'Y',
// type: 'bar',
// data: [20, 200, 40, 90],
// label: {
// show: true,
// position: 'right',
// formatter: '{a}'
// }
// }, {
// name: 'Z',
// type: 'bar',
// data: [100, 20, 70, 80],
// label: {
// show: true,
// position: 'right',
// formatter: '{a}'
// }
}],
legend: {
show: true
},
animationDurationUpdate: 10000
};
chart.setOption(option);
for (var x = 0; x < 4; ++x) {
(function (x) {
var btn = document.getElementById('btn-' + (x + 1));
btn.onclick = function () {
var data = option.series[0].data;
data[x] += Math.round(100 * Math.random());
chart.setOption(option);
};
})(x);
}
// setTimeout(function () {
// console.log('go')
// chart.setOption({});
// }, 5000);
}
);
</script>
</body>
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册