diff --git a/src/chart/boxplot/BoxplotView.ts b/src/chart/boxplot/BoxplotView.ts index f48cdf67b0..0d88309773 100644 --- a/src/chart/boxplot/BoxplotView.ts +++ b/src/chart/boxplot/BoxplotView.ts @@ -46,7 +46,7 @@ class BoxplotView extends ChartView { group.removeAll(); } - const constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0; + const constDim = seriesModel.getWhiskerBoxesLayout() === 'horizontal' ? 1 : 0; data.diff(oldData) .add(function (newIdx) { diff --git a/src/chart/boxplot/boxplotLayout.ts b/src/chart/boxplot/boxplotLayout.ts index cf6017b3cb..053462d2f2 100644 --- a/src/chart/boxplot/boxplotLayout.ts +++ b/src/chart/boxplot/boxplotLayout.ts @@ -144,7 +144,7 @@ function layoutSingleSeries(seriesModel: BoxplotSeriesModel, offset: number, box const coordSys = seriesModel.coordinateSystem; const data = seriesModel.getData(); const halfWidth = boxWidth / 2; - const cDimIdx = seriesModel.get('layout') === 'horizontal' ? 0 : 1; + const cDimIdx = seriesModel.getWhiskerBoxesLayout() === 'horizontal' ? 0 : 1; const vDimIdx = 1 - cDimIdx; const coordDims = ['x', 'y']; const cDim = data.mapDimension(coordDims[cDimIdx]); diff --git a/src/chart/candlestick/CandlestickSeries.ts b/src/chart/candlestick/CandlestickSeries.ts index f152fce2cf..55938e3b48 100644 --- a/src/chart/candlestick/CandlestickSeries.ts +++ b/src/chart/candlestick/CandlestickSeries.ts @@ -37,6 +37,7 @@ import SeriesData from '../../data/SeriesData'; import Cartesian2D from '../../coord/cartesian/Cartesian2D'; import { BrushCommonSelectorsForSeries } from '../../component/brush/selector'; import { mixin } from 'zrender/src/core/util'; +import type Axis2D from '../../coord/cartesian/Axis2D'; type CandlestickDataValue = OptionDataValue[]; @@ -158,6 +159,9 @@ class CandlestickSeriesModel extends SeriesModel { } } +interface CandlestickSeriesModel extends WhiskerBoxCommonMixin { + getBaseAxis(): Axis2D +} mixin(CandlestickSeriesModel, WhiskerBoxCommonMixin, true); export default CandlestickSeriesModel; diff --git a/src/chart/candlestick/CandlestickView.ts b/src/chart/candlestick/CandlestickView.ts index 728291424c..706cae6fcc 100644 --- a/src/chart/candlestick/CandlestickView.ts +++ b/src/chart/candlestick/CandlestickView.ts @@ -106,6 +106,8 @@ class CandlestickView extends ChartView { group.removeAll(); } + const transPointDim = getTransPointDimension(seriesModel); + data.diff(oldData) .add(function (newIdx) { if (data.hasValue(newIdx)) { @@ -115,7 +117,7 @@ class CandlestickView extends ChartView { return; } - const el = createNormalBox(itemLayout, newIdx, true); + const el = createNormalBox(itemLayout, newIdx, transPointDim, true); graphic.initProps(el, {shape: {points: itemLayout.ends}}, seriesModel, newIdx); setBoxCommon(el, data, newIdx, isSimpleBox); @@ -141,7 +143,7 @@ class CandlestickView extends ChartView { } if (!el) { - el = createNormalBox(itemLayout, newIdx); + el = createNormalBox(itemLayout, newIdx, transPointDim); } else { graphic.updateProps(el, { @@ -188,10 +190,12 @@ class CandlestickView extends ChartView { const data = seriesModel.getData(); const isSimpleBox = data.getLayout('isSimpleBox'); + const transPointDim = getTransPointDimension(seriesModel); + let dataIndex; while ((dataIndex = params.next()) != null) { const itemLayout = data.getItemLayout(dataIndex) as CandlestickItemLayout; - const el = createNormalBox(itemLayout, dataIndex); + const el = createNormalBox(itemLayout, dataIndex, transPointDim); setBoxCommon(el, data, dataIndex, isSimpleBox); el.incremental = true; @@ -262,12 +266,17 @@ class NormalBoxPath extends Path { } -function createNormalBox(itemLayout: CandlestickItemLayout, dataIndex: number, isInit?: boolean) { +function createNormalBox( + itemLayout: CandlestickItemLayout, + dataIndex: number, + constDim: number, + isInit?: boolean +) { const ends = itemLayout.ends; return new NormalBoxPath({ shape: { points: isInit - ? transInit(ends, itemLayout) + ? transInit(ends, constDim, itemLayout) : ends }, z2: 100 @@ -310,14 +319,18 @@ function setBoxCommon(el: NormalBoxPath, data: SeriesData, dataIndex: number, is toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); } -function transInit(points: number[][], itemLayout: CandlestickItemLayout) { +function transInit(points: number[][], dim: number, itemLayout: CandlestickItemLayout) { return zrUtil.map(points, function (point) { point = point.slice(); - point[1] = itemLayout.initBaseline; + point[dim] = itemLayout.initBaseline; return point; }); } +function getTransPointDimension(seriesModel: CandlestickSeriesModel): number { + return seriesModel.getWhiskerBoxesLayout() === 'horizontal' ? 1 : 0; +} + class LargeBoxPathShape { diff --git a/src/chart/candlestick/candlestickLayout.ts b/src/chart/candlestick/candlestickLayout.ts index f655b381f0..6b6fa03a6e 100644 --- a/src/chart/candlestick/candlestickLayout.ts +++ b/src/chart/candlestick/candlestickLayout.ts @@ -51,8 +51,8 @@ const candlestickLayout: StageHandler = { const coordSys = seriesModel.coordinateSystem; const data = seriesModel.getData(); const candleWidth = calculateCandleWidth(seriesModel, data); - const cDimIdx = 0; - const vDimIdx = 1; + const cDimIdx = seriesModel.getWhiskerBoxesLayout() === 'horizontal' ? 0 : 1; + const vDimIdx = 1 - cDimIdx; const coordDims = ['x', 'y']; const cDimI = data.getDimensionIndex(data.mapDimension(coordDims[cDimIdx])); const vDimsI = map(data.mapDimensionsAll(coordDims[vDimIdx]), data.getDimensionIndex, data); diff --git a/src/chart/helper/whiskerBoxCommon.ts b/src/chart/helper/whiskerBoxCommon.ts index efd425afa4..294fd0eae2 100644 --- a/src/chart/helper/whiskerBoxCommon.ts +++ b/src/chart/helper/whiskerBoxCommon.ts @@ -30,6 +30,9 @@ import type Axis2D from '../../coord/cartesian/Axis2D'; import { CoordDimensionDefinition } from '../../data/helper/createDimensions'; interface CommonOption extends SeriesOption, SeriesOnCartesianOptionMixin { + // - 'horizontal': Multiple whisker boxes (each drawn vertically) + // are arranged side by side horizontally. + // - 'vertical': The opposite. layout?: LayoutOrient // data?: (DataItemOption | number[])[] @@ -44,14 +47,15 @@ interface DataItemOption { interface WhiskerBoxCommonMixin extends SeriesModel{} class WhiskerBoxCommonMixin { - /** - * @private - * @type {string} - */ - _baseAxisDim: string; + private _baseAxisDim: string; defaultValueDimensions: CoordDimensionDefinition['dimsDef']; + /** + * Computed layout. + */ + private _layout: CommonOption['layout']; + /** * @private */ @@ -76,25 +80,33 @@ class WhiskerBoxCommonMixin { const yAxisType = yAxisModel.get('type'); let addOrdinal; - // FIXME - // Consider time axis. - + // Theoretically, if `encode` and/or `layout` are not specified, they can be derived from + // the specified one (also according to axis types). However, only the logic for deriving + // `encode` from `layout` is implemented; the reverse direction is not implemented yet, + // due to its complexity and low priority. + let layout = option.layout; + // 'category' axis has historically been enforcing `layout` regardless of its presence. + // This behavior is preserved until it causes problems. if (xAxisType === 'category') { - option.layout = 'horizontal'; + layout = 'horizontal'; ordinalMeta = xAxisModel.getOrdinalMeta(); addOrdinal = !this._hasEncodeRule('x'); } else if (yAxisType === 'category') { - option.layout = 'vertical'; + layout = 'vertical'; ordinalMeta = yAxisModel.getOrdinalMeta(); addOrdinal = !this._hasEncodeRule('y'); } - else { - option.layout = option.layout || 'horizontal'; + if (!layout) { + layout = yAxisType === 'time' ? 'vertical' : 'horizontal'; + // It is theoretically possible for an axis with type "time" to serve as the "value axis". + // `layout` can be explicitly specified for that case. } + // Do not assign the computed `layout` to `option.layout`, otherwise the idempotent may be broken. + this._layout = layout; const coordDims = ['x', 'y']; - const baseAxisDimIndex = option.layout === 'horizontal' ? 0 : 1; + const baseAxisDimIndex = layout === 'horizontal' ? 0 : 1; const baseAxisDim = this._baseAxisDim = coordDims[baseAxisDimIndex]; const otherAxisDim = coordDims[1 - baseAxisDimIndex]; const axisModels = [xAxisModel, yAxisModel]; @@ -166,7 +178,11 @@ class WhiskerBoxCommonMixin { ) as CartesianAxisModel).axis; } + getWhiskerBoxesLayout() { + return this._layout; + } + }; -export {WhiskerBoxCommonMixin}; +export { WhiskerBoxCommonMixin }; diff --git a/test/candlestick-horizontal.html b/test/candlestick-horizontal.html new file mode 100644 index 0000000000..bc5fb0db42 --- /dev/null +++ b/test/candlestick-horizontal.html @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ + + +