From a03dd3b8e43c29addacf10cac5b0f3a4dc15191e Mon Sep 17 00:00:00 2001 From: lkd01632719 Date: Tue, 16 Jan 2024 15:36:59 +0800 Subject: [PATCH] docs: add demos --- .../statistics/interaction/demo/brush-emit.js | 92 +++++++++++++ .../statistics/interaction/demo/brush.js | 100 +++++++++++++++ .../interaction/demo/chart-index.js | 33 +++++ .../statistics/interaction/demo/fisheye.js | 24 ++++ .../interaction/demo/focus-context.js | 121 ++++++++++++++++++ .../statistics/interaction/demo/highlight.js | 20 +++ .../statistics/interaction/demo/meta.json | 64 +++++++++ .../statistics/interaction/demo/select.js | 21 +++ .../statistics/interaction/index.en.md | 4 + .../statistics/interaction/index.zh.md | 4 + 10 files changed, 483 insertions(+) create mode 100644 site/examples/statistics/interaction/demo/brush-emit.js create mode 100644 site/examples/statistics/interaction/demo/brush.js create mode 100644 site/examples/statistics/interaction/demo/chart-index.js create mode 100644 site/examples/statistics/interaction/demo/fisheye.js create mode 100644 site/examples/statistics/interaction/demo/focus-context.js create mode 100644 site/examples/statistics/interaction/demo/highlight.js create mode 100644 site/examples/statistics/interaction/demo/meta.json create mode 100644 site/examples/statistics/interaction/demo/select.js create mode 100644 site/examples/statistics/interaction/index.en.md create mode 100644 site/examples/statistics/interaction/index.zh.md diff --git a/site/examples/statistics/interaction/demo/brush-emit.js b/site/examples/statistics/interaction/demo/brush-emit.js new file mode 100644 index 000000000..0c81d89d4 --- /dev/null +++ b/site/examples/statistics/interaction/demo/brush-emit.js @@ -0,0 +1,92 @@ +import { Line } from '@ant-design/plots'; +import React from 'react'; +import ReactDOM from 'react-dom'; + +function tip({ container, onRemove = () => {}, offsetX = 20, offsetY = 0 }) { + let div; + + const render = (data, [x, y]) => { + if (div) remove(); + div = document.createElement('div'); + div.innerHTML = ` + Select a node: + + `; + div.style.position = 'absolute'; + div.style.background = '#eee'; + div.style.padding = '0.5em'; + div.style.left = x + offsetX + 'px'; + div.style.top = y + offsetY + 'px'; + div.onclick = () => { + remove(); + onRemove(); + }; + container.append(div); + }; + + const remove = () => { + if (div) div.remove(); + div = null; + }; + + return [render, remove]; +} + +const DemoInteraction = () => { + const data = [ + { date: '2007-04-23', close: 93.24 }, + { date: '2007-04-24', close: 95.35 }, + { date: '2007-04-25', close: 98.84 }, + { date: '2007-04-26', close: 99.92 }, + { date: '2007-04-29', close: 99.8 }, + { date: '2007-05-01', close: 99.47 }, + { date: '2007-05-02', close: 100.39 }, + { date: '2007-05-03', close: 100.4 }, + { date: '2007-05-04', close: 100.81 }, + { date: '2007-05-07', close: 103.92 }, + ]; + + const config = { + data, + xField: (d) => new Date(d.date), + yField: 'close', + scale: { y: { nice: true } }, + interaction: { brushXHighlight: true }, + }; + return ( + { + const chart = plot.chart; + const [render, remove] = tip({ + container: document.getElementById('container'), + onRemove: () => plot.emit('brush:remove', {}), + }); + + chart.on('brush:start', () => { + chart.emit('tooltip:disable'); + remove(); + }); + + chart.on('brush:end', async (e) => { + const { canvas } = chart.getContext(); + const [mask] = canvas.document.getElementsByClassName('mask'); + const bounds = mask.getBounds(); + const x = bounds.max[0]; + const y = bounds.center[1]; + const [X] = e.data.selection; + const filtered = data.filter(({ date }) => new Date(date) >= X[0] && new Date(date) <= X[1]); + render(filtered, [x, y]); + }); + + chart.on('brush:remove', (e) => { + const { nativeEvent } = e; + if (nativeEvent) remove(); + chart.emit('tooltip:enable'); + }); + }} + /> + ); +}; + +ReactDOM.render(, document.getElementById('container')); diff --git a/site/examples/statistics/interaction/demo/brush.js b/site/examples/statistics/interaction/demo/brush.js new file mode 100644 index 000000000..cad8414a2 --- /dev/null +++ b/site/examples/statistics/interaction/demo/brush.js @@ -0,0 +1,100 @@ +import { Scatter } from '@ant-design/plots'; +import React from 'react'; +import ReactDOM from 'react-dom'; + +function tip({ container, onRemove = () => {}, offsetX = 20, offsetY = 0 }) { + let div; + + const render = (data, [x, y]) => { + if (div) remove(); + div = document.createElement('div'); + div.innerHTML = ` + Select a node: +
    ${data.map((d) => `
  • x:${d.weight},y:${d.height}
  • `).join('')}
+ `; + div.style.position = 'absolute'; + div.style.background = '#eee'; + div.style.padding = '0.5em'; + div.style.left = x + offsetX + 'px'; + div.style.top = y + offsetY + 'px'; + div.onclick = () => { + remove(); + onRemove(); + }; + container.append(div); + }; + + const remove = () => { + if (div) div.remove(); + div = null; + }; + + return [render, remove]; +} + +const DemoInteraction = () => { + const config = { + data: { + type: 'fetch', + value: 'https://gw.alipayobjects.com/os/antvdemo/assets/data/scatter.json', + }, + xField: 'weight', + yField: 'height', + colorField: 'gender', + shapeField: 'point', + style: { + fillOpacity: 0.2, + lineWidth: 1, + transform: 'scale(1, 1)', + transformOrigin: 'center center', + }, + state: { + inactive: { fill: 'black', fillOpacity: 0.5, transform: 'scale(0.5, 0.5)' }, + }, + interaction: { brushHighlight: true }, + }; + return ( + { + const chart = plot.chart; + const [render, remove] = tip({ + container: document.getElementById('container'), + onRemove: () => plot.emit('brush:remove', {}), + }); + + chart.on('brush:start', () => { + chart.emit('tooltip:disable'); + remove(); + }); + + chart.on('brush:end', async (e) => { + const data = await fetch('https://gw.alipayobjects.com/os/antvdemo/assets/data/scatter.json').then((res) => + res.json(), + ); + + const { canvas } = chart.getContext(); + const [mask] = canvas.document.getElementsByClassName('mask'); + const bounds = mask.getBounds(); + const x = bounds.max[0]; + const y = bounds.center[1]; + const [X, Y] = e.data.selection; + + const filtered = data.filter(({ weight, height }) => { + return weight >= X[0] && weight <= X[1] && height >= Y[0] && height <= Y[1]; + }); + + render(filtered, [x, y]); + }); + + chart.on('brush:remove', (e) => { + const { nativeEvent } = e; + if (nativeEvent) remove(); + chart.emit('tooltip:enable'); + }); + }} + /> + ); +}; + +ReactDOM.render(, document.getElementById('container')); diff --git a/site/examples/statistics/interaction/demo/chart-index.js b/site/examples/statistics/interaction/demo/chart-index.js new file mode 100644 index 000000000..6ea61052a --- /dev/null +++ b/site/examples/statistics/interaction/demo/chart-index.js @@ -0,0 +1,33 @@ +import { Line } from '@ant-design/plots'; +import React from 'react'; +import ReactDOM from 'react-dom'; + +const DemoInteraction = () => { + const config = { + data: { + type: 'fetch', + value: 'https://assets.antv.antgroup.com/g2/indices.json', + }, + xField: (d) => new Date(d.Date), + yField: 'Close', + colorField: 'Symbol', + legend: false, + scale: { y: { type: 'log' } }, + axis: { y: { title: '↑ Change in price (%)', labelAutoRotate: false } }, + labels: [{ text: 'Symbol', selector: 'last', fontSize: 10 }], + interaction: { + chartIndex: { + ruleStroke: '#aaa', + labelDx: 5, + labelTextAlign: 'center', + labelStroke: '#fff', + labelLineWidth: 5, + labelFormatter: (d) => `${d.toLocaleDateString()}`, + }, + tooltip: false, + }, + }; + return ; +}; + +ReactDOM.render(, document.getElementById('container')); diff --git a/site/examples/statistics/interaction/demo/fisheye.js b/site/examples/statistics/interaction/demo/fisheye.js new file mode 100644 index 000000000..c99eff4b2 --- /dev/null +++ b/site/examples/statistics/interaction/demo/fisheye.js @@ -0,0 +1,24 @@ +import { Scatter } from '@ant-design/plots'; +import React from 'react'; +import ReactDOM from 'react-dom'; + +const DemoInteraction = () => { + const config = { + data: { + type: 'fetch', + value: 'https://gw.alipayobjects.com/os/antvdemo/assets/data/bubble.json', + }, + xField: 'GDP', + yField: 'LifeExpectancy', + sizeField: 'Population', + colorField: 'continent', + shapeField: 'point', + scale: { size: { type: 'log', range: [4, 20] } }, + style: { fillOpacity: 0.3, lineWidth: 1 }, + legend: { size: false }, + interaction: { fisheye: true }, + }; + return ; +}; + +ReactDOM.render(, document.getElementById('container')); diff --git a/site/examples/statistics/interaction/demo/focus-context.js b/site/examples/statistics/interaction/demo/focus-context.js new file mode 100644 index 000000000..a09b0d846 --- /dev/null +++ b/site/examples/statistics/interaction/demo/focus-context.js @@ -0,0 +1,121 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Area } from '@ant-design/plots'; +import { useRef } from 'react'; +import { useEffect } from 'react'; + +document.getElementById('container').innerHTML = ` +
+
+`; + +const commonConfig = { + autoFit: false, + data: { + type: 'fetch', + value: 'https://gw.alipayobjects.com/os/bmw-prod/551d80c6-a6be-4f3c-a82a-abd739e12977.csv', + }, + xField: 'date', + yField: 'close', + animate: false, +}; + +function createPathRender(compute) { + return (group, options, document) => { + if (!group.handle) { + const path = document.createElement('path'); + group.handle = path; + group.appendChild(group.handle); + } + const { handle } = group; + const { width, height, ...rest } = options; + if (width === undefined || height === undefined) return handle; + handle.attr({ ...compute(width, height), ...rest }); + return handle; + }; +} + +const DemoInteraction = () => { + const focusRef = useRef(); + const contextRef = useRef(); + + useEffect(() => { + const focusChart = focusRef.current.chart; + const contextChart = contextRef.current.chart; + + focusChart.on('brush:filter', (e) => { + const { nativeEvent } = e; + if (!nativeEvent) return; + const { selection } = e.data; + const { x: scaleX } = focusChart.getScale(); + const [[x1, x2]] = selection; + const domainX = scaleX.getOptions().domain; + if (x1 === domainX[0] && x2 === domainX[1]) { + contextChart.emit('brush:remove', {}); + } else { + contextChart.emit('brush:highlight', { data: { selection } }); + } + }); + + contextChart.on('brush:highlight', (e) => { + const { nativeEvent, data } = e; + if (!nativeEvent) return; + const { selection } = data; + focusChart.emit('brush:filter', { data: { selection } }); + }); + + contextChart.on('brush:remove', (e) => { + const { nativeEvent } = e; + if (!nativeEvent) return; + const { x: scaleX, y: scaleY } = contextChart.getScale(); + const selection = [scaleX.getOptions().domain, scaleY.getOptions().domain]; + focusChart.emit('brush:filter', { data: { selection } }); + }); + }, []); + + const focusConfig = { + ...commonConfig, + height: 360, + paddingLeft: 60, + interaction: { + tooltip: false, + brushXFilter: true, + }, + }; + + const contextConfig = { + ...commonConfig, + paddingTop: 0, + paddingBottom: 0, + height: 90, + paddingLeft: 60, + axis: false, + interaction: { + tooltip: false, + brushXHighlight: { + series: true, + maskOpacity: 0.3, + maskFill: '#777', + maskHandleWRender: createPathRender((width, height) => ({ + d: 'M-0.5,31.5c-2.5,0,-4.5,2,-4.5,4.5v30c0,2.5,2,4.5,4.5,4.5V31.5z', + transform: `translate(${width / 2}, ${-height / 2})`, + })), + maskHandleERender: createPathRender((width, height) => ({ + d: 'M0.5,31.5c2.5,0,4.5,2,4.5,4.5v30c0,2.5,-2,4.5,-4.5,4.5V31.5z', + transform: `translate(${width / 2}, ${-height / 2})`, + })), + maskHandleEFill: '#D3D8E0', + maskHandleWFill: '#D3D8E0', + }, + }, + }; + + return ( +
+ (focusRef.current = plot)} /> + (contextRef.current = plot)} /> +
+ ); +}; + +ReactDOM.render(, document.getElementById('container')); diff --git a/site/examples/statistics/interaction/demo/highlight.js b/site/examples/statistics/interaction/demo/highlight.js new file mode 100644 index 000000000..e2e7f13f2 --- /dev/null +++ b/site/examples/statistics/interaction/demo/highlight.js @@ -0,0 +1,20 @@ +import { Column } from '@ant-design/plots'; +import React from 'react'; +import ReactDOM from 'react-dom'; + +const DemoInteraction = () => { + const config = { + data: { + type: 'fetch', + value: 'https://gw.alipayobjects.com/os/bmw-prod/fb9db6b7-23a5-4c23-bbef-c54a55fee580.csv', + }, + xField: 'letter', + yField: 'frequency', + transform: [{ type: 'sortX', by: 'y', reverse: true, slice: 5 }], + axis: { y: { labelFormatter: '.0%' } }, + interaction: { elementHighlight: { background: true } }, + }; + return ; +}; + +ReactDOM.render(, document.getElementById('container')); diff --git a/site/examples/statistics/interaction/demo/meta.json b/site/examples/statistics/interaction/demo/meta.json new file mode 100644 index 000000000..94018d0f3 --- /dev/null +++ b/site/examples/statistics/interaction/demo/meta.json @@ -0,0 +1,64 @@ +{ + "title": { + "zh": "中文分类", + "en": "Category" + }, + "demos": [ + { + "filename": "highlight.js", + "title": { + "zh": "高亮", + "en": "Highlight" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*h3zzSqWMrDkAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "select.js", + "title": { + "zh": "选择", + "en": "Select" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*gwt_SpgIGsMAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "brush.js", + "title": { + "zh": "刷选", + "en": "Brush" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*ESblSJqW3zYAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "brush-emit.js", + "title": { + "zh": "刷选事件", + "en": "Brush" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*kWbhQKMQp9wAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "chart-index.js", + "title": { + "zh": "索引图", + "en": "Chart Index" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*LaVjRbRk8EcAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "fisheye.js", + "title": { + "zh": "鱼眼", + "en": "Fisheye" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*FwusQ6ohkqkAAAAAAAAAAAAADmJ7AQ/original" + }, + { + "filename": "focus-context.js", + "title": { + "zh": "图表联动", + "en": "Focus and Context" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*IwNsRKKyMlgAAAAAAAAAAAAADmJ7AQ/original" + } + ] +} diff --git a/site/examples/statistics/interaction/demo/select.js b/site/examples/statistics/interaction/demo/select.js new file mode 100644 index 000000000..1d5b4301f --- /dev/null +++ b/site/examples/statistics/interaction/demo/select.js @@ -0,0 +1,21 @@ +import { Column } from '@ant-design/plots'; +import React from 'react'; +import ReactDOM from 'react-dom'; + +const DemoInteraction = () => { + const config = { + data: { + type: 'fetch', + value: 'https://render.alipay.com/p/yuyan/180020010001215413/antd-charts/column-column.json', + }, + xField: 'letter', + yField: 'frequency', + transform: [{ type: 'sortX', by: 'y', reverse: true, slice: 5 }], + state: { selected: { fill: '#f4bb51' }, unselected: { opacity: 0.6 } }, + axis: { y: { labelFormatter: '.0%' } }, + interaction: { elementSelect: true, elementHighlightByColor: false }, + }; + return ; +}; + +ReactDOM.render(, document.getElementById('container')); diff --git a/site/examples/statistics/interaction/index.en.md b/site/examples/statistics/interaction/index.en.md new file mode 100644 index 000000000..ed6f35679 --- /dev/null +++ b/site/examples/statistics/interaction/index.en.md @@ -0,0 +1,4 @@ +--- +title: interaction +order: 23 +--- \ No newline at end of file diff --git a/site/examples/statistics/interaction/index.zh.md b/site/examples/statistics/interaction/index.zh.md new file mode 100644 index 000000000..3245093c1 --- /dev/null +++ b/site/examples/statistics/interaction/index.zh.md @@ -0,0 +1,4 @@ +--- +title: 交互 +order: 23 +---