提交 35bf17fc 编写于 作者: P pissang

ts: add types for heatmap and effectScatter

上级 3152a5be
......@@ -48,6 +48,9 @@ export interface BoxplotDataItemOption {
}
export interface BoxplotSeriesOption extends SeriesOption, SeriesOnCartesianOptionMixin {
coordinateSystem?: 'cartesian2d'
hoverAnimation?: boolean
layout?: LayoutOrient
/**
......
......@@ -54,6 +54,9 @@ interface CandlestickItemStyleOption extends ItemStyleOption {
}
export interface CandlestickSeriesOption extends SeriesOption, SeriesOnCartesianOptionMixin, SeriesLargeOptionMixin {
coordinateSystem?: 'cartesian2d'
hoverAnimation?: boolean
layout?: LayoutOrient
clip?: boolean
......
......@@ -17,24 +17,83 @@
* under the License.
*/
// @ts-nocheck
import createListFromArray from '../helper/createListFromArray';
import SeriesModel from '../../model/Series';
import {
SeriesOption,
SeriesOnPolarOptionMixin,
SeriesOnCartesianOptionMixin,
SeriesOnCalendarOptionMixin,
SeriesOnGeoOptionMixin,
SeriesOnSingleOptionMixin,
SeriesSymbolOptionMixin,
OptionDataValue,
ItemStyleOption,
LabelOption,
ZRColor
} from '../../util/types';
import GlobalModel from '../../model/Global';
import List from '../../data/List';
type ScatterDataValue = OptionDataValue | OptionDataValue[]
export interface EffectScatterDataItemOption {
name?: string
value?: ScatterDataValue
itemStyle?: ItemStyleOption
label?: LabelOption
emphasis?: {
itemStyle?: ItemStyleOption
label?: LabelOption
}
}
export default SeriesModel.extend({
export interface EffectScatterSeriesOption extends SeriesOption,
SeriesOnCartesianOptionMixin, SeriesOnPolarOptionMixin, SeriesOnCalendarOptionMixin,
SeriesOnGeoOptionMixin, SeriesOnSingleOptionMixin, SeriesSymbolOptionMixin {
type: 'series.effectScatter',
coordinateSystem?: string
dependencies: ['grid', 'polar'],
effectType?: 'ripple'
getInitialData: function (option, ecModel) {
return createListFromArray(this.getSource(), this, {useEncodeDefaulter: true});
},
/**
* When to show the effect
*/
showEffectOn?: 'render' | 'emphasis'
/**
* Ripple effect config
*/
rippleEffect?: {
period?: number
/**
* Scale of ripple
*/
scale?: number
brushType?: 'fill' | 'stroke'
color?: ZRColor
}
brushSelector: 'point',
data?: (EffectScatterDataItemOption | OptionDataValue)[]
}
class EffectScatterSeriesModel extends SeriesModel<EffectScatterSeriesOption> {
static readonly type = 'series.effectScatter'
type = EffectScatterSeriesModel.type
defaultOption: {
static readonly dependencies = ['grid', 'polar']
brushSelector = 'point'
getInitialData(option: EffectScatterSeriesOption, ecModel: GlobalModel): List {
return createListFromArray(this.getSource(), this, {useEncodeDefaulter: true});
}
defaultOption: EffectScatterSeriesOption = {
coordinateSystem: 'cartesian2d',
zlevel: 0,
z: 2,
......@@ -70,13 +129,12 @@ export default SeriesModel.extend({
symbolSize: 10 // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
// symbolRotate: null, // 图形旋转控制
// large: false,
// Available when large is true
// largeThreshold: 2000,
// itemStyle: {
// opacity: 1
// }
}
}
SeriesModel.registerClass(EffectScatterSeriesModel);
});
\ No newline at end of file
export default EffectScatterSeriesModel;
\ No newline at end of file
......@@ -17,54 +17,66 @@
* under the License.
*/
// @ts-nocheck
import * as echarts from '../../echarts';
import SymbolDraw from '../helper/SymbolDraw';
import EffectSymbol from '../helper/EffectSymbol';
import * as matrix from 'zrender/src/core/matrix';
import pointsLayout from '../../layout/points';
import ChartView from '../../view/Chart';
import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../ExtensionAPI';
import EffectScatterSeriesModel from './EffectScatterSeries';
import { StageHandlerProgressExecutor } from '../../util/types';
export default echarts.extendChartView({
class EffectScatterView extends ChartView {
static readonly type = 'effectScatter'
readonly type = EffectScatterView.type
type: 'effectScatter',
private _symbolDraw: SymbolDraw
init: function () {
init() {
this._symbolDraw = new SymbolDraw(EffectSymbol);
},
}
render: function (seriesModel, ecModel, api) {
render(seriesModel: EffectScatterSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
var data = seriesModel.getData();
var effectSymbolDraw = this._symbolDraw;
effectSymbolDraw.updateData(data);
this.group.add(effectSymbolDraw.group);
},
}
updateTransform: function (seriesModel, ecModel, api) {
updateTransform(seriesModel: EffectScatterSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
var data = seriesModel.getData();
this.group.dirty();
var res = pointsLayout().reset(seriesModel);
var res = pointsLayout().reset(seriesModel, ecModel, api) as StageHandlerProgressExecutor;
if (res.progress) {
res.progress({ start: 0, end: data.count() }, data);
res.progress({
start: 0,
end: data.count(),
count: data.count()
}, data);
}
this._symbolDraw.updateLayout(data);
},
this._symbolDraw.updateLayout();
}
_updateGroupTransform: function (seriesModel) {
_updateGroupTransform(seriesModel: EffectScatterSeriesModel) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys && coordSys.getRoamTransform) {
this.group.transform = matrix.clone(coordSys.getRoamTransform());
this.group.decomposeTransform();
}
},
}
remove(ecModel: GlobalModel, api: ExtensionAPI) {
this._symbolDraw && this._symbolDraw.remove(true);
}
}
ChartView.registerClass(EffectScatterView);
remove: function (ecModel, api) {
this._symbolDraw && this._symbolDraw.remove(api);
},
dispose: function () {}
});
export default EffectScatterView;
\ No newline at end of file
......@@ -17,43 +17,50 @@
* under the License.
*/
// @ts-nocheck
/* global Uint8ClampedArray */
import * as zrUtil from 'zrender/src/core/util';
var GRADIENT_LEVELS = 256;
/**
* Heatmap Chart
*
* @class
*/
function Heatmap() {
var canvas = zrUtil.createCanvas();
this.canvas = canvas;
type ColorFunc = (grad: number, fastMode: boolean, output: number[]) => void
this.blurSize = 30;
this.pointSize = 20;
type ColorState = 'inRange' | 'outOfRange'
this.maxOpacity = 1;
this.minOpacity = 0;
class HeatmapLayer {
canvas: HTMLCanvasElement
blurSize = 30
pointSize = 20
this._gradientPixels = {};
}
maxOpacity = 1
minOpacity = 0
private _brushCanvas: HTMLCanvasElement
private _gradientPixels: Record<ColorState, Uint8ClampedArray>
constructor() {
var canvas = zrUtil.createCanvas();
this.canvas = canvas;
}
Heatmap.prototype = {
/**
* Renders Heatmap and returns the rendered canvas
* @param {Array} data array of data, each has x, y, value
* @param {number} width canvas width
* @param {number} height canvas height
* @param data array of data, each has x, y, value
* @param width canvas width
* @param height canvas height
*/
update: function (data, width, height, normalize, colorFunc, isInRange) {
update(
data: number[][],
width: number,
height: number,
normalize: (value: number) => number,
colorFunc: Record<ColorState, ColorFunc>,
isInRange?: (grad?: number) => boolean
) {
var brush = this._getBrush();
var gradientInRange = this._getGradient(data, colorFunc, 'inRange');
var gradientOutOfRange = this._getGradient(data, colorFunc, 'outOfRange');
var gradientInRange = this._getGradient(colorFunc, 'inRange');
var gradientOutOfRange = this._getGradient(colorFunc, 'outOfRange');
var r = this.pointSize + this.blurSize;
var canvas = this.canvas;
......@@ -111,14 +118,12 @@ Heatmap.prototype = {
ctx.putImageData(imageData, 0, 0);
return canvas;
},
}
/**
* get canvas of a black circle brush used for canvas to draw later
* @private
* @returns {Object} circle brush canvas
*/
_getBrush: function () {
_getBrush() {
var brushCanvas = this._brushCanvas || (this._brushCanvas = zrUtil.createCanvas());
// set brush size
var r = this.pointSize + this.blurSize;
......@@ -144,13 +149,13 @@ Heatmap.prototype = {
ctx.closePath();
ctx.fill();
return brushCanvas;
},
}
/**
* get gradient color map
* @private
*/
_getGradient: function (data, colorFunc, state) {
_getGradient(colorFunc: Record<ColorState, ColorFunc>, state: ColorState) {
var gradientPixels = this._gradientPixels;
var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4));
var color = [0, 0, 0, 0];
......@@ -164,6 +169,6 @@ Heatmap.prototype = {
}
return pixelsSingleState;
}
};
}
export default Heatmap;
export default HeatmapLayer;
......@@ -17,31 +17,83 @@
* under the License.
*/
// @ts-nocheck
import SeriesModel from '../../model/Series';
import createListFromArray from '../helper/createListFromArray';
import CoordinateSystem from '../../CoordinateSystem';
import {
SeriesOption,
SeriesOnCartesianOptionMixin,
SeriesOnGeoOptionMixin,
ItemStyleOption,
LabelOption,
OptionDataValue
} from '../../util/types';
import GlobalModel from '../../model/Global';
import List from '../../data/List';
import type Geo from '../../coord/geo/Geo';
import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
import type Calendar from '../../coord/calendar/Calendar';
type HeatmapDataValue = OptionDataValue[];
export interface HeatmapDataItemOption {
value: HeatmapDataValue
itemStyle?: ItemStyleOption
label?: LabelOption
emphasis?: {
itemStyle: ItemStyleOption
label?: LabelOption
}
export default SeriesModel.extend({
type: 'series.heatmap',
}
export interface HeatmapSeriesOption extends SeriesOption,
SeriesOnCartesianOptionMixin, SeriesOnGeoOptionMixin {
coordinateSystem?: 'cartesian2d' | 'geo' | 'calendar'
// Available on cartesian2d coordinate system
itemStyle?: ItemStyleOption
label?: LabelOption
emphasis?: {
itemStyle?: ItemStyleOption
label?: LabelOption
}
getInitialData: function (option, ecModel) {
// Available on geo coordinate system
blurSize?: number
pointSize?: number
maxOpacity?: number
minOpacity?: number
data?: (HeatmapDataItemOption | HeatmapDataValue)[]
}
class HeatmapSeriesModel extends SeriesModel<HeatmapSeriesOption> {
static readonly type = 'series.heatmap'
readonly type = HeatmapSeriesModel.type
// @ts-ignore
coordinateSystem: Cartesian2D | Geo | Calendar
getInitialData(option: HeatmapSeriesOption, ecModel: GlobalModel): List {
return createListFromArray(this.getSource(), this, {
generateCoord: 'value'
});
},
}
preventIncremental: function () {
preventIncremental() {
var coordSysCreator = CoordinateSystem.get(this.get('coordinateSystem'));
if (coordSysCreator && coordSysCreator.dimensions) {
return coordSysCreator.dimensions[0] === 'lng' && coordSysCreator.dimensions[1] === 'lat';
}
},
}
defaultOption: {
defaultOption: HeatmapSeriesOption = {
// Cartesian2D or geo
coordinateSystem: 'cartesian2d',
zlevel: 0,
......@@ -63,4 +115,8 @@ export default SeriesModel.extend({
minOpacity: 0
}
});
\ No newline at end of file
}
SeriesModel.registerClass(HeatmapSeriesModel);
export default HeatmapSeriesModel;
\ No newline at end of file
......@@ -17,15 +17,36 @@
* under the License.
*/
// @ts-nocheck
import {__DEV__} from '../../config';
import * as echarts from '../../echarts';
import * as graphic from '../../util/graphic';
import HeatmapLayer from './HeatmapLayer';
import * as zrUtil from 'zrender/src/core/util';
import ChartView from '../../view/Chart';
import HeatmapSeriesModel from './HeatmapSeries';
import type GlobalModel from '../../model/Global';
import type ExtensionAPI from '../../ExtensionAPI';
import type VisualMapModel from '../../component/visualMap/VisualMapModel';
import type PiecewiseModel from '../../component/visualMap/PiecewiseModel';
import type ContinuousModel from '../../component/visualMap/ContinuousModel';
import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
import { CoordinateSystem } from '../../coord/CoordinateSystem';
import { StageHandlerProgressParams, Dictionary } from '../../util/types';
// Coord can be 'geo' 'bmap' 'amap' 'leaflet'...
interface GeoLikeCoordSys extends CoordinateSystem {
dimensions: ['lng', 'lat']
getViewRect(): graphic.BoundingRect
}
function isCartesian2D(coord: CoordinateSystem): coord is Cartesian2D {
return coord.type === 'cartesian2d';
}
function getIsInPiecewiseRange(dataExtent, pieceList, selected) {
function getIsInPiecewiseRange(
dataExtent: number[],
pieceList: ReturnType<PiecewiseModel['getPieceList']>,
selected: Dictionary<boolean>
) {
var dataSpan = dataExtent[1] - dataExtent[0];
pieceList = zrUtil.map(pieceList, function (piece) {
return {
......@@ -38,7 +59,7 @@ function getIsInPiecewiseRange(dataExtent, pieceList, selected) {
var len = pieceList.length;
var lastIndex = 0;
return function (val) {
return function (val: number) {
// Try to find in the location of the last found
for (var i = lastIndex; i < len; i++) {
var interval = pieceList[i].interval;
......@@ -60,30 +81,35 @@ function getIsInPiecewiseRange(dataExtent, pieceList, selected) {
};
}
function getIsInContinuousRange(dataExtent, range) {
function getIsInContinuousRange(dataExtent: number[], range: number[]) {
var dataSpan = dataExtent[1] - dataExtent[0];
range = [
(range[0] - dataExtent[0]) / dataSpan,
(range[1] - dataExtent[0]) / dataSpan
];
return function (val) {
return function (val: number) {
return val >= range[0] && val <= range[1];
};
}
function isGeoCoordSys(coordSys) {
function isGeoCoordSys(coordSys: CoordinateSystem): coordSys is GeoLikeCoordSys {
var dimensions = coordSys.dimensions;
// Not use coorSys.type === 'geo' because coordSys maybe extended
return dimensions[0] === 'lng' && dimensions[1] === 'lat';
}
export default echarts.extendChartView({
class HeatmapView extends ChartView {
static readonly type = 'heatmap'
readonly type = HeatmapView.type
private _incrementalDisplayable: boolean
type: 'heatmap',
private _hmLayer: HeatmapLayer
render: function (seriesModel, ecModel, api) {
render(seriesModel: HeatmapSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
var visualMapOfThisSeries;
ecModel.eachComponent('visualMap', function (visualMap) {
ecModel.eachComponent('visualMap', function (visualMap: VisualMapModel) {
visualMap.eachTargetSeries(function (targetSeries) {
if (targetSeries === seriesModel) {
visualMapOfThisSeries = visualMap;
......@@ -110,26 +136,37 @@ export default echarts.extendChartView({
coordSys, seriesModel, visualMapOfThisSeries, api
);
}
},
}
incrementalPrepareRender: function (seriesModel, ecModel, api) {
incrementalPrepareRender(seriesModel: HeatmapSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
this.group.removeAll();
},
incrementalRender: function (params, seriesModel, ecModel, api) {
}
incrementalRender(
params: StageHandlerProgressParams,
seriesModel: HeatmapSeriesModel,
ecModel: GlobalModel,
api: ExtensionAPI
) {
var coordSys = seriesModel.coordinateSystem;
if (coordSys) {
this._renderOnCartesianAndCalendar(seriesModel, api, params.start, params.end, true);
}
},
}
_renderOnCartesianAndCalendar: function (seriesModel, api, start, end, incremental) {
_renderOnCartesianAndCalendar(
seriesModel: HeatmapSeriesModel,
api: ExtensionAPI,
start: number,
end: number,
incremental?: boolean
) {
var coordSys = seriesModel.coordinateSystem;
var width;
var height;
if (coordSys.type === 'cartesian2d') {
if (isCartesian2D(coordSys)) {
var xAxis = coordSys.getAxis('x');
var yAxis = coordSys.getAxis('y');
......@@ -149,18 +186,12 @@ export default echarts.extendChartView({
var group = this.group;
var data = seriesModel.getData();
var itemStyleQuery = 'itemStyle';
var hoverItemStyleQuery = 'emphasis.itemStyle';
var labelQuery = 'label';
var hoverLabelQuery = 'emphasis.label';
var style = seriesModel.getModel(itemStyleQuery).getItemStyle(['color']);
var hoverStl = seriesModel.getModel(hoverItemStyleQuery).getItemStyle();
var labelModel = seriesModel.getModel(labelQuery);
var hoverLabelModel = seriesModel.getModel(hoverLabelQuery);
var coordSysType = coordSys.type;
var style = seriesModel.getModel('itemStyle').getItemStyle(['color']);
var hoverStl = seriesModel.getModel(['emphasis', 'itemStyle']).getItemStyle();
var labelModel = seriesModel.getModel('label');
var hoverLabelModel = seriesModel.getModel(['emphasis', 'label']);
var dataDims = coordSysType === 'cartesian2d'
var dataDims = isCartesian2D(coordSys)
? [
data.mapDimension('x'),
data.mapDimension('y'),
......@@ -174,9 +205,9 @@ export default echarts.extendChartView({
for (var idx = start; idx < end; idx++) {
var rect;
if (coordSysType === 'cartesian2d') {
if (isCartesian2D(coordSys)) {
// Ignore empty data
if (isNaN(data.get(dataDims[2], idx))) {
if (isNaN(data.get(dataDims[2], idx) as number)) {
continue;
}
......@@ -200,7 +231,7 @@ export default echarts.extendChartView({
}
else {
// Ignore empty data
if (isNaN(data.get(dataDims[1], idx))) {
if (isNaN(data.get(dataDims[1], idx) as number)) {
continue;
}
......@@ -218,10 +249,10 @@ export default echarts.extendChartView({
// Optimization for large datset
if (data.hasItemOption) {
style = itemModel.getModel(itemStyleQuery).getItemStyle(['color']);
hoverStl = itemModel.getModel(hoverItemStyleQuery).getItemStyle();
labelModel = itemModel.getModel(labelQuery);
hoverLabelModel = itemModel.getModel(hoverLabelQuery);
style = itemModel.getModel('itemStyle').getItemStyle(['color']);
hoverStl = itemModel.getModel(['emphasis', 'itemStyle']).getItemStyle();
labelModel = itemModel.getModel('label');
hoverLabelModel = itemModel.getModel(['emphasis', 'label']);
}
var rawValue = seriesModel.getRawValue(idx);
......@@ -253,9 +284,14 @@ export default echarts.extendChartView({
group.add(rect);
data.setItemGraphicEl(idx, rect);
}
},
_renderOnGeo: function (geo, seriesModel, visualMapModel, api) {
}
_renderOnGeo(
geo: GeoLikeCoordSys,
seriesModel: HeatmapSeriesModel,
visualMapModel: VisualMapModel,
api: ExtensionAPI
) {
var inRangeVisuals = visualMapModel.targetVisuals.inRange;
var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange;
// if (!visualMapping) {
......@@ -287,7 +323,7 @@ export default echarts.extendChartView({
data.mapDimension('value')
];
var points = data.mapArray(dims, function (lng, lat, value) {
var points = data.mapArray(dims, function (lng: number, lat: number, value: number) {
var pt = geo.dataToPoint([lng, lat]);
pt[0] -= x;
pt[1] -= y;
......@@ -297,9 +333,11 @@ export default echarts.extendChartView({
var dataExtent = visualMapModel.getExtent();
var isInRange = visualMapModel.type === 'visualMap.continuous'
? getIsInContinuousRange(dataExtent, visualMapModel.option.range)
? getIsInContinuousRange(dataExtent, (visualMapModel as ContinuousModel).option.range)
: getIsInPiecewiseRange(
dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected
dataExtent,
(visualMapModel as PiecewiseModel).getPieceList(),
(visualMapModel as PiecewiseModel).option.selected
);
hmLayer.update(
......@@ -322,7 +360,9 @@ export default echarts.extendChartView({
silent: true
});
this.group.add(img);
},
}
}
ChartView.registerClass(HeatmapView);
dispose: function () {}
});
\ No newline at end of file
export default HeatmapView;
\ No newline at end of file
......@@ -17,8 +17,6 @@
* under the License.
*/
// @ts-nocheck
/**
* Symbol with ripple effect
* @module echarts/chart/helper/EffectSymbol
......@@ -26,22 +24,38 @@
import * as zrUtil from 'zrender/src/core/util';
import {createSymbol} from '../../util/symbol';
import {Group} from '../../util/graphic';
import {Group, Path} from '../../util/graphic';
import {parsePercent} from '../../util/number';
import SymbolClz from './Symbol';
import List from '../../data/List';
import type { ZRColor } from '../../util/types';
import type Displayable from 'zrender/src/graphic/Displayable';
var EFFECT_RIPPLE_NUMBER = 3;
function normalizeSymbolSize(symbolSize) {
interface RippleEffectCfg {
showEffectOn?: 'emphasis' | 'render'
rippleScale?: number
brushType?: 'fill' | 'stroke'
period?: number
effectOffset?: number
z?: number
zlevel?: number
symbolType?: string
color?: ZRColor
rippleEffectColor?: ZRColor
}
function normalizeSymbolSize(symbolSize: number | number[]): number[] {
if (!zrUtil.isArray(symbolSize)) {
symbolSize = [+symbolSize, +symbolSize];
}
return symbolSize;
}
function updateRipplePath(rippleGroup, effectCfg) {
function updateRipplePath(rippleGroup: Group, effectCfg: RippleEffectCfg) {
var color = effectCfg.rippleEffectColor || effectCfg.color;
rippleGroup.eachChild(function (ripplePath) {
rippleGroup.eachChild(function (ripplePath: Displayable) {
ripplePath.attr({
z: effectCfg.z,
zlevel: effectCfg.zlevel,
......@@ -52,195 +66,189 @@ function updateRipplePath(rippleGroup, effectCfg) {
});
});
}
/**
* @constructor
* @param {module:echarts/data/List} data
* @param {number} idx
* @extends {module:zrender/graphic/Group}
*/
function EffectSymbol(data, idx) {
Group.call(this);
var symbol = new SymbolClz(data, idx);
var rippleGroup = new Group();
this.add(symbol);
this.add(rippleGroup);
class EffectSymbol extends Group {
rippleGroup.beforeUpdate = function () {
this.attr(symbol.getScale());
};
this.updateData(data, idx);
}
private _effectCfg: RippleEffectCfg
var effectSymbolProto = EffectSymbol.prototype;
constructor(data: List, idx: number) {
super();
effectSymbolProto.stopEffectAnimation = function () {
this.childAt(1).removeAll();
};
var symbol = new SymbolClz(data, idx);
var rippleGroup = new Group();
this.add(symbol);
this.add(rippleGroup);
effectSymbolProto.startEffectAnimation = function (effectCfg) {
var symbolType = effectCfg.symbolType;
var color = effectCfg.color;
var rippleGroup = this.childAt(1);
this.updateData(data, idx);
}
for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) {
// If width/height are set too small (e.g., set to 1) on ios10
// and macOS Sierra, a circle stroke become a rect, no matter what
// the scale is set. So we set width/height as 2. See #4136.
var ripplePath = createSymbol(
symbolType, -1, -1, 2, 2, color
);
ripplePath.attr({
style: {
strokeNoScale: true
},
z2: 99,
silent: true,
scale: [0.5, 0.5]
});
var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset;
// TODO Configurable effectCfg.period
ripplePath.animate('', true)
.when(effectCfg.period, {
scale: [effectCfg.rippleScale / 2, effectCfg.rippleScale / 2]
})
.delay(delay)
.start();
ripplePath.animateStyle(true)
.when(effectCfg.period, {
opacity: 0
})
.delay(delay)
.start();
rippleGroup.add(ripplePath);
stopEffectAnimation() {
(this.childAt(1) as Group).removeAll();
}
updateRipplePath(rippleGroup, effectCfg);
};
/**
* Update effect symbol
*/
effectSymbolProto.updateEffectAnimation = function (effectCfg) {
var oldEffectCfg = this._effectCfg;
var rippleGroup = this.childAt(1);
// Must reinitialize effect if following configuration changed
var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale'];
for (var i = 0; i < DIFFICULT_PROPS.length; i++) {
var propName = DIFFICULT_PROPS[i];
if (oldEffectCfg[propName] !== effectCfg[propName]) {
this.stopEffectAnimation();
this.startEffectAnimation(effectCfg);
return;
startEffectAnimation(effectCfg: RippleEffectCfg) {
var symbolType = effectCfg.symbolType;
var color = effectCfg.color;
var rippleGroup = this.childAt(1) as Group;
for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) {
// If width/height are set too small (e.g., set to 1) on ios10
// and macOS Sierra, a circle stroke become a rect, no matter what
// the scale is set. So we set width/height as 2. See #4136.
var ripplePath = createSymbol(
symbolType, -1, -1, 2, 2, color
);
ripplePath.attr({
style: {
strokeNoScale: true
},
z2: 99,
silent: true,
scale: [0.5, 0.5]
});
var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset;
// TODO Configurable effectCfg.period
ripplePath.animate('', true)
.when(effectCfg.period, {
scale: [effectCfg.rippleScale / 2, effectCfg.rippleScale / 2]
})
.delay(delay)
.start();
ripplePath.animateStyle(true)
.when(effectCfg.period, {
opacity: 0
})
.delay(delay)
.start();
rippleGroup.add(ripplePath);
}
updateRipplePath(rippleGroup, effectCfg);
}
updateRipplePath(rippleGroup, effectCfg);
};
/**
* Update effect symbol
*/
updateEffectAnimation(effectCfg: RippleEffectCfg) {
var oldEffectCfg = this._effectCfg;
var rippleGroup = this.childAt(1) as Group;
// Must reinitialize effect if following configuration changed
var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale'] as const;
for (var i = 0; i < DIFFICULT_PROPS.length; i++) {
var propName = DIFFICULT_PROPS[i];
if (oldEffectCfg[propName] !== effectCfg[propName]) {
this.stopEffectAnimation();
this.startEffectAnimation(effectCfg);
return;
}
}
/**
* Highlight symbol
*/
effectSymbolProto.highlight = function () {
this.trigger('emphasis');
};
updateRipplePath(rippleGroup, effectCfg);
}
/**
* Downplay symbol
*/
effectSymbolProto.downplay = function () {
this.trigger('normal');
};
/**
* Highlight symbol
*/
highlight() {
this.trigger('emphasis');
}
/**
* Update symbol properties
* @param {module:echarts/data/List} data
* @param {number} idx
*/
effectSymbolProto.updateData = function (data, idx) {
var seriesModel = data.hostModel;
/**
* Downplay symbol
*/
downplay() {
this.trigger('normal');
}
this.childAt(0).updateData(data, idx);
/**
* Update symbol properties
* @param {module:echarts/data/List} data
* @param {number} idx
*/
updateData(data: List, idx: number) {
var seriesModel = data.hostModel;
var rippleGroup = this.childAt(1);
var itemModel = data.getItemModel(idx);
var symbolType = data.getItemVisual(idx, 'symbol');
var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
var color = data.getItemVisual(idx, 'color');
(this.childAt(0) as SymbolClz).updateData(data, idx);
rippleGroup.attr('scale', symbolSize);
var rippleGroup = this.childAt(1);
var itemModel = data.getItemModel(idx);
var symbolType = data.getItemVisual(idx, 'symbol');
var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
var color = data.getItemVisual(idx, 'color');
rippleGroup.traverse(function (ripplePath) {
ripplePath.attr({
fill: color
rippleGroup.attr('scale', symbolSize);
rippleGroup.traverse(function (ripplePath: Path) {
ripplePath.setStyle('fill', color);
});
});
var symbolOffset = itemModel.getShallow('symbolOffset');
if (symbolOffset) {
var pos = rippleGroup.position;
pos[0] = parsePercent(symbolOffset[0], symbolSize[0]);
pos[1] = parsePercent(symbolOffset[1], symbolSize[1]);
}
rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0;
var symbolOffset = itemModel.getShallow('symbolOffset');
if (symbolOffset) {
var pos = rippleGroup.position;
pos[0] = parsePercent(symbolOffset[0], symbolSize[0]);
pos[1] = parsePercent(symbolOffset[1], symbolSize[1]);
}
rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0;
var effectCfg = {};
var effectCfg: RippleEffectCfg = {};
effectCfg.showEffectOn = seriesModel.get('showEffectOn');
effectCfg.rippleScale = itemModel.get('rippleEffect.scale');
effectCfg.brushType = itemModel.get('rippleEffect.brushType');
effectCfg.period = itemModel.get('rippleEffect.period') * 1000;
effectCfg.effectOffset = idx / data.count();
effectCfg.z = itemModel.getShallow('z') || 0;
effectCfg.zlevel = itemModel.getShallow('zlevel') || 0;
effectCfg.symbolType = symbolType;
effectCfg.color = color;
effectCfg.rippleEffectColor = itemModel.get('rippleEffect.color');
effectCfg.showEffectOn = seriesModel.get('showEffectOn');
effectCfg.rippleScale = itemModel.get('rippleEffect.scale');
effectCfg.brushType = itemModel.get('rippleEffect.brushType');
effectCfg.period = itemModel.get('rippleEffect.period') * 1000;
effectCfg.effectOffset = idx / data.count();
effectCfg.z = itemModel.getShallow('z') || 0;
effectCfg.zlevel = itemModel.getShallow('zlevel') || 0;
effectCfg.symbolType = symbolType;
effectCfg.color = color;
effectCfg.rippleEffectColor = itemModel.get('rippleEffect.color');
this.off('mouseover').off('mouseout').off('emphasis').off('normal');
this.off('mouseover').off('mouseout').off('emphasis').off('normal');
if (effectCfg.showEffectOn === 'render') {
this._effectCfg
? this.updateEffectAnimation(effectCfg)
: this.startEffectAnimation(effectCfg);
if (effectCfg.showEffectOn === 'render') {
this._effectCfg
? this.updateEffectAnimation(effectCfg)
: this.startEffectAnimation(effectCfg);
this._effectCfg = effectCfg;
}
else {
// Not keep old effect config
this._effectCfg = null;
this.stopEffectAnimation();
var symbol = this.childAt(0);
var onEmphasis = function () {
symbol.highlight();
if (effectCfg.showEffectOn !== 'render') {
this.startEffectAnimation(effectCfg);
}
};
var onNormal = function () {
symbol.downplay();
if (effectCfg.showEffectOn !== 'render') {
this.stopEffectAnimation();
}
};
this.on('mouseover', onEmphasis, this)
.on('mouseout', onNormal, this)
.on('emphasis', onEmphasis, this)
.on('normal', onNormal, this);
}
this._effectCfg = effectCfg;
}
else {
// Not keep old effect config
this._effectCfg = null;
this.stopEffectAnimation();
var symbol = this.childAt(0) as SymbolClz;
var onEmphasis = function (this: EffectSymbol) {
symbol.highlight();
if (effectCfg.showEffectOn !== 'render') {
this.startEffectAnimation(effectCfg);
}
};
var onNormal = function (this: EffectSymbol) {
symbol.downplay();
if (effectCfg.showEffectOn !== 'render') {
this.stopEffectAnimation();
}
};
this.on('mouseover', onEmphasis, this)
.on('mouseout', onNormal, this)
.on('emphasis', onEmphasis, this)
.on('normal', onNormal, this);
}
this._effectCfg = effectCfg;
};
this._effectCfg = effectCfg;
};
effectSymbolProto.fadeOut = function (cb) {
this.off('mouseover').off('mouseout').off('emphasis').off('normal');
cb && cb();
};
fadeOut(cb: () => void) {
this.off('mouseover').off('mouseout').off('emphasis').off('normal');
cb && cb();
};
}
zrUtil.inherits(EffectSymbol, Group);
export default EffectSymbol;
\ No newline at end of file
......@@ -31,7 +31,7 @@ import { SOURCE_FORMAT_ORIGINAL, DimensionDefinitionLoose, DimensionDefinition }
import SeriesModel from '../../model/Series';
function createListFromArray(source: Source | any[], seriesModel: SeriesModel, opt?: {
generateCoord?: boolean
generateCoord?: string
useEncodeDefaulter?: boolean
}): List {
opt = opt || {};
......
......@@ -58,7 +58,9 @@ export interface ScatterSeriesOption extends SeriesOption,
SeriesLargeOptionMixin, SeriesStackOptionMixin,
SeriesSymbolOptionMixin {
hoverAnimation: boolean
coordinateSystem?: string
hoverAnimation?: boolean
cursor?: string
clip?: boolean
......@@ -70,6 +72,8 @@ export interface ScatterSeriesOption extends SeriesOption,
itemStyle?: ItemStyleOption
label?: LabelOption
}
data?: (ScatterDataItemOption | OptionDataValue)[]
}
......
......@@ -34,7 +34,7 @@ export type CreateDimensionsParams = {
encodeDefine?: HashMap<OptionEncodeValue> | OptionEncode,
dimensionsCount?: number,
encodeDefaulter?: EncodeDefaulter,
generateCoord?: boolean,
generateCoord?: string,
generateCoordCount?: number
};
......
......@@ -60,6 +60,20 @@ var inner = modelUtil.makeInner<{
dataBeforeProcessed: List
}>();
interface SeriesModel {
/**
* Convinient for override in extended class.
* Implement it if needed.
*/
preventIncremental(): boolean;
/**
* See tooltip.
* Implement it if needed.
* @return Point of tooltip. null/undefined can be returned.
*/
getTooltipPosition(dataIndex: number): number[];
}
class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentModel<Opt> {
// [Caution]: for compat the previous "class extend"
......@@ -526,24 +540,11 @@ class SeriesModel<Opt extends SeriesOption = SeriesOption> extends ComponentMode
nestestValue: any
};
/**
* See tooltip.
* Implement it if needed.
* @return Point of tooltip. null/undefined can be returned.
*/
getTooltipPosition: (dataIndex: number) => number[];
// /**
// * @see {module:echarts/stream/Scheduler}
// */
// abstract pipeTask: null
/**
* Convinient for override in extended class.
* Implement it if needed.
*/
preventIncremental: () => boolean;
static registerClass(clz: Constructor): Constructor {
return ComponentModel.registerClass(clz);
}
......
......@@ -24,10 +24,11 @@ import * as graphic from './graphic';
import BoundingRect from 'zrender/src/core/BoundingRect';
import {calculateTextPosition} from 'zrender/src/contain/text';
import { Dictionary } from 'zrender/src/core/types';
import { ZRColor } from './types';
type ECSymbol = graphic.Path & {
__isEmptyBrush?: boolean
setColor: (color: string, innerColor?: string) => void
setColor: (color: ZRColor, innerColor?: string) => void
}
type SymbolCtor = { new(): ECSymbol }
type SymbolShapeMaker = (x: number, y: number, w: number, h: number, shape: Dictionary<any>) => void
......@@ -306,7 +307,7 @@ var SymbolClz = graphic.Path.extend({
});
// Provide setColor helper method to avoid determine if set the fill or stroke outside
function symbolPathSetColor(this: ECSymbol, color: string, innerColor?: string) {
function symbolPathSetColor(this: ECSymbol, color: ZRColor, innerColor?: string) {
if (this.type !== 'image') {
var symbolStyle = this.style;
var symbolShape = this.shape;
......@@ -328,14 +329,6 @@ function symbolPathSetColor(this: ECSymbol, color: string, innerColor?: string)
/**
* Create a symbol element with given symbol configuration: shape, x, y, width, height, color
* @param {string} symbolType
* @param {number} x
* @param {number} y
* @param {number} w
* @param {number} h
* @param {string} color
* @param {boolean} [keepAspect=false] whether to keep the ratio of w/h,
* for path and image only.
*/
export function createSymbol(
symbolType: string,
......@@ -343,7 +336,8 @@ export function createSymbol(
y: number,
w: number,
h: number,
color?: string,
color?: ZRColor,
// whether to keep the ratio of w/h,
keepAspect?: boolean
) {
// TODO Support image object, DynamicImage.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册