From 35bf17fc8d43012289f4e8d2d364a54a925f9fc5 Mon Sep 17 00:00:00 2001 From: pissang Date: Thu, 5 Mar 2020 14:41:51 +0800 Subject: [PATCH] ts: add types for heatmap and effectScatter --- src/chart/boxplot/BoxplotSeries.ts | 3 + src/chart/candlestick/CandlestickSeries.ts | 3 + .../effectScatter/EffectScatterSeries.ts | 88 ++++- src/chart/effectScatter/EffectScatterView.ts | 54 +-- src/chart/heatmap/HeatmapLayer.ts | 67 ++-- src/chart/heatmap/HeatmapSeries.ts | 78 +++- src/chart/heatmap/HeatmapView.ts | 134 ++++--- src/chart/helper/EffectSymbol.ts | 340 +++++++++--------- src/chart/helper/createListFromArray.ts | 2 +- src/chart/scatter/ScatterSeries.ts | 6 +- src/data/helper/createDimensions.ts | 2 +- src/model/Series.ts | 27 +- src/util/symbol.ts | 16 +- 13 files changed, 502 insertions(+), 318 deletions(-) diff --git a/src/chart/boxplot/BoxplotSeries.ts b/src/chart/boxplot/BoxplotSeries.ts index 57cd46e9d..b658f14a3 100644 --- a/src/chart/boxplot/BoxplotSeries.ts +++ b/src/chart/boxplot/BoxplotSeries.ts @@ -48,6 +48,9 @@ export interface BoxplotDataItemOption { } export interface BoxplotSeriesOption extends SeriesOption, SeriesOnCartesianOptionMixin { + + coordinateSystem?: 'cartesian2d' + hoverAnimation?: boolean layout?: LayoutOrient /** diff --git a/src/chart/candlestick/CandlestickSeries.ts b/src/chart/candlestick/CandlestickSeries.ts index fde8a358b..737aa3fad 100644 --- a/src/chart/candlestick/CandlestickSeries.ts +++ b/src/chart/candlestick/CandlestickSeries.ts @@ -54,6 +54,9 @@ interface CandlestickItemStyleOption extends ItemStyleOption { } export interface CandlestickSeriesOption extends SeriesOption, SeriesOnCartesianOptionMixin, SeriesLargeOptionMixin { + + coordinateSystem?: 'cartesian2d' + hoverAnimation?: boolean layout?: LayoutOrient clip?: boolean diff --git a/src/chart/effectScatter/EffectScatterSeries.ts b/src/chart/effectScatter/EffectScatterSeries.ts index b2cf53c19..e8258e671 100644 --- a/src/chart/effectScatter/EffectScatterSeries.ts +++ b/src/chart/effectScatter/EffectScatterSeries.ts @@ -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 { + 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 diff --git a/src/chart/effectScatter/EffectScatterView.ts b/src/chart/effectScatter/EffectScatterView.ts index bdeccf8f1..26d695a48 100644 --- a/src/chart/effectScatter/EffectScatterView.ts +++ b/src/chart/effectScatter/EffectScatterView.ts @@ -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 diff --git a/src/chart/heatmap/HeatmapLayer.ts b/src/chart/heatmap/HeatmapLayer.ts index b262575fc..de2619ad5 100644 --- a/src/chart/heatmap/HeatmapLayer.ts +++ b/src/chart/heatmap/HeatmapLayer.ts @@ -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 + + 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, + 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, 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; diff --git a/src/chart/heatmap/HeatmapSeries.ts b/src/chart/heatmap/HeatmapSeries.ts index 6713f5129..c776cc33d 100644 --- a/src/chart/heatmap/HeatmapSeries.ts +++ b/src/chart/heatmap/HeatmapSeries.ts @@ -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 { + 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 diff --git a/src/chart/heatmap/HeatmapView.ts b/src/chart/heatmap/HeatmapView.ts index 9cb5ec629..90b98c78f 100644 --- a/src/chart/heatmap/HeatmapView.ts +++ b/src/chart/heatmap/HeatmapView.ts @@ -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, + selected: Dictionary +) { 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 diff --git a/src/chart/helper/EffectSymbol.ts b/src/chart/helper/EffectSymbol.ts index aa6534195..fed7d5db9 100644 --- a/src/chart/helper/EffectSymbol.ts +++ b/src/chart/helper/EffectSymbol.ts @@ -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 diff --git a/src/chart/helper/createListFromArray.ts b/src/chart/helper/createListFromArray.ts index f360b9793..b9cc20a84 100644 --- a/src/chart/helper/createListFromArray.ts +++ b/src/chart/helper/createListFromArray.ts @@ -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 || {}; diff --git a/src/chart/scatter/ScatterSeries.ts b/src/chart/scatter/ScatterSeries.ts index d8da89c42..db4829c00 100644 --- a/src/chart/scatter/ScatterSeries.ts +++ b/src/chart/scatter/ScatterSeries.ts @@ -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)[] } diff --git a/src/data/helper/createDimensions.ts b/src/data/helper/createDimensions.ts index 8de100fb0..770bfd66d 100644 --- a/src/data/helper/createDimensions.ts +++ b/src/data/helper/createDimensions.ts @@ -34,7 +34,7 @@ export type CreateDimensionsParams = { encodeDefine?: HashMap | OptionEncode, dimensionsCount?: number, encodeDefaulter?: EncodeDefaulter, - generateCoord?: boolean, + generateCoord?: string, generateCoordCount?: number }; diff --git a/src/model/Series.ts b/src/model/Series.ts index 55a247a4c..e0ea2240f 100644 --- a/src/model/Series.ts +++ b/src/model/Series.ts @@ -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 extends ComponentModel { // [Caution]: for compat the previous "class extend" @@ -526,24 +540,11 @@ class SeriesModel 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); } diff --git a/src/util/symbol.ts b/src/util/symbol.ts index e5d82fe66..4cbf395e8 100644 --- a/src/util/symbol.ts +++ b/src/util/symbol.ts @@ -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) => 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. -- GitLab