diff --git a/packages/f2/src/components/candlestick/candlestickView.tsx b/packages/f2/src/components/candlestick/candlestickView.tsx new file mode 100644 index 000000000..ce0b8795e --- /dev/null +++ b/packages/f2/src/components/candlestick/candlestickView.tsx @@ -0,0 +1,87 @@ +import { jsx } from '@antv/f-engine'; +import { deepMix } from '@antv/util'; + +export default (props) => { + const { records, animation, y0, onClick } = props; + return ( + + {records.map((record) => { + const { key, children } = record; + return ( + + {children.map((item) => { + const { key, xMin, xMax, yMin, yMax, x, y, color, shape } = item; + if (isNaN(xMin) || isNaN(xMax) || isNaN(yMin) || isNaN(yMax)) { + return null; + } + return ( + + + + + ); + })} + + ); + })} + + ); +}; diff --git a/packages/f2/src/components/candlestick/index.tsx b/packages/f2/src/components/candlestick/index.tsx new file mode 100644 index 000000000..17264b7f0 --- /dev/null +++ b/packages/f2/src/components/candlestick/index.tsx @@ -0,0 +1,5 @@ +import withCandlestick, { CandlestickProps } from './withCandlestick'; +import CandlestickView from './candlestickView'; + +export { CandlestickProps, CandlestickView }; +export default withCandlestick(CandlestickView); diff --git a/packages/f2/src/components/candlestick/withCandlestick.tsx b/packages/f2/src/components/candlestick/withCandlestick.tsx new file mode 100644 index 000000000..d613d10d7 --- /dev/null +++ b/packages/f2/src/components/candlestick/withCandlestick.tsx @@ -0,0 +1,86 @@ +import { jsx, ComponentType } from '@antv/f-engine'; +import { isNil, mix } from '@antv/util'; +import Geometry, { GeometryProps } from '../geometry'; +import { DataRecord } from '../../chart/Data'; + +// 默认配色 +const COLORS = [ + '#E62C3B', // 上涨 + '#0E9976', // 下跌 + '#999999', // 平盘 +]; + +export interface CandlestickProps + extends GeometryProps { + /** + * 柱子的显示比例,默认 0.5 + */ + sizeRatio?: number; +} + +export default (View: ComponentType) => { + return class Candlestick< + TRecord extends DataRecord = DataRecord, + IProps extends CandlestickProps = CandlestickProps + > extends Geometry { + getDefaultCfg() { + return { + geomType: 'candlestick', + }; + } + + getSize() { + const { attrs, props } = this; + const { sizeRatio = 0.5 } = props; + const { x } = attrs; + const { scale } = x; + const { values } = scale; + + return (1 / values.length) * sizeRatio; + } + + mapping() { + const records = super.mapping(); + const { props } = this; + const { coord } = props; + const y0 = this.getY0Value(); + const defaultSize = this.getSize(); + const colorAttr = this.getAttr('color'); + + const colors = colorAttr ? colorAttr.range : COLORS; + + for (let i = 0, len = records.length; i < len; i++) { + const record = records[i]; + const { children } = record; + for (let j = 0, len = children.length; j < len; j++) { + const child = children[j]; + const { normalized, size: mappedSize } = child; + + // 没有指定size,则根据数据来计算默认size + if (isNil(mappedSize)) { + const { x, y, size = defaultSize } = normalized; + mix(child, coord.convertRect({ x, y, y0, size: size })); + } else { + const { x, y } = child; + const rect = { x, y, y0, size: mappedSize }; + mix(child, coord.transformToRect(rect)); + } + + // 处理颜色 + const { y } = normalized; + const [open, close] = y; + child.color = close > open ? colors[0] : close < open ? colors[1] : colors[2]; + + mix(child.shape, this.getSelectionStyle(child)); + } + } + return records; + } + + render() { + const { props } = this; + const records = this.mapping(); + return ; + } + }; +}; diff --git a/packages/f2/src/components/index.ts b/packages/f2/src/components/index.ts index 748c126ac..197936bcc 100644 --- a/packages/f2/src/components/index.ts +++ b/packages/f2/src/components/index.ts @@ -25,3 +25,4 @@ export { default as PieLabel, PieLabelProps, withPieLabel, PieLabelView } from ' export { default as Gauge, GaugeProps, withGauge, GaugeView } from './gauge'; export { default as Zoom, ZoomProps } from './zoom'; export { default as ScrollBar, ScrollBarProps, withScrollBar, ScrollBarView } from './scrollBar'; +export { default as Candlestick } from './candlestick'; diff --git a/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-basic-1-snap.png b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-basic-1-snap.png new file mode 100644 index 000000000..f474bd8fa Binary files /dev/null and b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-basic-1-snap.png differ diff --git a/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-color-1-snap.png b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-color-1-snap.png new file mode 100644 index 000000000..ad0f9b8c7 Binary files /dev/null and b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-color-1-snap.png differ diff --git a/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-1-snap.png b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-1-snap.png new file mode 100644 index 000000000..7531e2c98 Binary files /dev/null and b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-1-snap.png differ diff --git a/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-ratio-1-snap.png b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-ratio-1-snap.png new file mode 100644 index 000000000..ab21c88a4 Binary files /dev/null and b/packages/f2/test/components/candlestick/__image_snapshots__/basic-test-tsx-candlestick-size-ratio-1-snap.png differ diff --git a/packages/f2/test/components/candlestick/basic.test.tsx b/packages/f2/test/components/candlestick/basic.test.tsx new file mode 100644 index 000000000..00f8fb58a --- /dev/null +++ b/packages/f2/test/components/candlestick/basic.test.tsx @@ -0,0 +1,101 @@ +import { jsx } from '../../../src'; +import { Canvas, Chart, Candlestick, Axis } from '../../../src'; +import { createContext, delay } from '../../util'; +const context = createContext(); + +const data = [ + { + time: '2017-10-24', + value: [20, 34, 10, 38], // [open, close, lowest, highest] + }, + { + time: '2017-10-25', + value: [40, 35, 30, 50], + }, + { + time: '2017-10-26', + value: [31, 38, 33, 44], + }, + { + time: '2017-10-27', + value: [38, 38, 5, 42], + }, + { + time: '2017-10-28', + value: [38, 15, 5, 42], + }, +]; + +describe('candlestick', () => { + it('basic', async () => { + const { props } = ( + + + + + + + + ); + + const canvas = new Canvas(props); + await canvas.render(); + + await delay(1000); + expect(context).toMatchImageSnapshot(); + }); + + it('color', async () => { + const { props } = ( + + + + + + + + ); + + const canvas = new Canvas(props); + await canvas.render(); + + await delay(1000); + expect(context).toMatchImageSnapshot(); + }); + + it('size', async () => { + const { props } = ( + + + + + + + + ); + + const canvas = new Canvas(props); + await canvas.render(); + + await delay(1000); + expect(context).toMatchImageSnapshot(); + }); + + it('sizeRatio', async () => { + const { props } = ( + + + + + + + + ); + + const canvas = new Canvas(props); + await canvas.render(); + + await delay(1000); + expect(context).toMatchImageSnapshot(); + }); +}); diff --git a/site/.dumirc.ts b/site/.dumirc.ts index 426e604e7..10dee0dc1 100644 --- a/site/.dumirc.ts +++ b/site/.dumirc.ts @@ -302,6 +302,14 @@ export default defineConfig({ en: 'Relation Charts', }, }, + { + slug: 'candlestick', + icon: 'candlestick', + title: { + zh: 'K 线图', + en: 'Candlestick Charts', + }, + }, { slug: 'component', icon: 'component', diff --git a/site/docs/api/chart/candlestick.zh.md b/site/docs/api/chart/candlestick.zh.md new file mode 100644 index 000000000..bdb510979 --- /dev/null +++ b/site/docs/api/chart/candlestick.zh.md @@ -0,0 +1,68 @@ +--- +title: K 线图 - Candlestick +order: 5 +--- + +用于 K 线图, 继承自 [几何标记 Geometry](geometry) + +## Usage + +```jsx +import { Axis, Candlestick, Canvas, Chart, jsx } from '@antv/f2'; + +const data = [ + { + time: '2017-10-24', + // 格式为:[open, close, lowest, highest] + value: [20, 34, 10, 38], + }, + { + time: '2017-10-25', + value: [40, 35, 30, 50], + }, + { + time: '2017-10-26', + value: [31, 38, 33, 44], + }, + { + time: '2017-10-27', + value: [38, 15, 5, 42], + }, +]; + +const { props } = ( + + + + + + + +); +``` + +## 数据结构说明 + +y 轴字段格式为:`[open, close, lowest, highest]` 分别代表:`[开盘价, 收盘价, 最低价, 最高价]` + +## Props + +几何标记统一 Props 详见:[几何标记](geometry#props) + +### color + +设置「涨」、「跌」、「平盘」颜色,格式为:`[上涨颜色, 下跌颜色, 平盘颜色]`, 默认值为: `['#E62C3B', '#0E9976', '#999999']` + +```jsx + +``` + +### sizeRatio + +矩形的大小比例,范围 `[0, 1]`, 默认为 `0.5`, 表示矩形的宽度和空白处各占 `50%` + +```jsx + +``` + +## 方法 diff --git a/site/examples/candlestick/candlestick/demo/base.jsx b/site/examples/candlestick/candlestick/demo/base.jsx new file mode 100644 index 000000000..ea63b3262 --- /dev/null +++ b/site/examples/candlestick/candlestick/demo/base.jsx @@ -0,0 +1,37 @@ +/** @jsx jsx */ +import { Axis, Candlestick, Canvas, Chart, jsx } from '@antv/f2'; + +const context = document.getElementById('container').getContext('2d'); + +const data = [ + { + time: '2017-10-24', + // 格式为:[open, close, lowest, highest] + value: [20, 34, 10, 38], + }, + { + time: '2017-10-25', + value: [40, 35, 30, 50], + }, + { + time: '2017-10-26', + value: [31, 38, 33, 44], + }, + { + time: '2017-10-27', + value: [38, 15, 5, 42], + }, +]; + +const { props } = ( + + + + + + + +); + +const canvas = new Canvas(props); +canvas.render(); diff --git a/site/examples/candlestick/candlestick/demo/meta.json b/site/examples/candlestick/candlestick/demo/meta.json new file mode 100644 index 000000000..c3c4092a8 --- /dev/null +++ b/site/examples/candlestick/candlestick/demo/meta.json @@ -0,0 +1,13 @@ +{ + "title": { + "zh": "中文分类", + "en": "Category" + }, + "demos": [ + { + "filename": "base.jsx", + "title": "基础 K 线图", + "screenshot": "https://gw.alipayobjects.com/zos/finxbff/compress-tinypng/9e61f9de-14eb-4ecc-becf-1bb2a9c29ea9.png" + } + ] +} diff --git a/site/examples/candlestick/candlestick/index.en.md b/site/examples/candlestick/candlestick/index.en.md new file mode 100644 index 000000000..73a61fee6 --- /dev/null +++ b/site/examples/candlestick/candlestick/index.en.md @@ -0,0 +1,5 @@ +--- +title: Candlestick Chart +order: 0 +icon: candlestick +--- diff --git a/site/examples/candlestick/candlestick/index.zh.md b/site/examples/candlestick/candlestick/index.zh.md new file mode 100644 index 000000000..b1a82c07b --- /dev/null +++ b/site/examples/candlestick/candlestick/index.zh.md @@ -0,0 +1,5 @@ +--- +title: K 线图 +order: 0 +icon: candlestick +---