blob: b09a5a8f104c82cfcc1f38a45a78f8b7b3673524 [file] [log] [blame]
(window.webpackJsonp=window.webpackJsonp||[]).push([[85],{379:function(n,t,e){"use strict";e.r(t),t.default="# 拖拽的实现\n\n本篇通过介绍一个实现拖拽的小例子来介绍如何在 Apache ECharts<sup>TM</sup> 中实现复杂的交互。\n\n<md-example src=\"line-draggable\" height=\"400\"></md-example>\n\n这个例子主要做到了这样一件事,用鼠标可以拖拽曲线的点,从而改变曲线的形状。例子很简单,但是有了这个基础我们还可以做更多的事情,比如在图中进行可视化得编辑。所以我们从这个简单的例子开始。\n\necharts 本身没有提供封装好的“拖拽改变图表”这样比较业务定制的功能。但是这个功能开发者可以通过 API 扩展实现。\n\n## 实现基本的拖拽功能\n\n在这个例子中,基础的图表是一个 [折线图 (series-line)](${optionPath}series-line)。参见如下配置:\n\n```js\nvar symbolSize = 20;\n\n// 这个 data 变量在这里单独声明,在后面也会用到。\nvar data = [\n [15, 0],\n [-50, 10],\n [-56.5, 20],\n [-46.5, 30],\n [-22.1, 40]\n];\n\nmyChart.setOption({\n xAxis: {\n min: -100,\n max: 80,\n type: 'value',\n axisLine: { onZero: false }\n },\n yAxis: {\n min: -30,\n max: 60,\n type: 'value',\n axisLine: { onZero: false }\n },\n series: [\n {\n id: 'a',\n type: 'line',\n smooth: true,\n symbolSize: symbolSize, // 为了方便拖拽,把 symbolSize 尺寸设大了。\n data: data\n }\n ]\n});\n```\n\n既然折线中原生的点没有拖拽功能,我们就为它加上拖拽功能:用 [graphic](${optionPath}graphic) 组件,在每个点上面,覆盖一个隐藏的可拖拽的圆点。\n\n```js\nmyChart.setOption({\n // 声明一个 graphic component,里面有若干个 type 为 'circle' 的 graphic elements。\n // 这里使用了 echarts.util.map 这个帮助方法,其行为和 Array.prototype.map 一样,但是兼容 es5 以下的环境。\n // 用 map 方法遍历 data 的每项,为每项生成一个圆点。\n graphic: echarts.util.map(data, function(dataItem, dataIndex) {\n return {\n // 'circle' 表示这个 graphic element 的类型是圆点。\n type: 'circle',\n\n shape: {\n // 圆点的半径。\n r: symbolSize / 2\n },\n // 用 transform 的方式对圆点进行定位。position: [x, y] 表示将圆点平移到 [x, y] 位置。\n // 这里使用了 convertToPixel 这个 API 来得到每个圆点的位置,下面介绍。\n position: myChart.convertToPixel('grid', dataItem),\n\n // 这个属性让圆点不可见(但是不影响他响应鼠标事件)。\n invisible: true,\n // 这个属性让圆点可以被拖拽。\n draggable: true,\n // 把 z 值设得比较大,表示这个圆点在最上方,能覆盖住已有的折线图的圆点。\n z: 100,\n // 此圆点的拖拽的响应事件,在拖拽过程中会不断被触发。下面介绍详情。\n // 这里使用了 echarts.util.curry 这个帮助方法,意思是生成一个与 onPointDragging\n // 功能一样的新的函数,只不过第一个参数永远为此时传入的 dataIndex 的值。\n ondrag: echarts.util.curry(onPointDragging, dataIndex)\n };\n })\n});\n```\n\n上面的代码中,使用 [convertToPixel](api.html#echartsInstance.convertToPixel) 这个 API,进行了从 data 到“像素坐标”的转换,从而得到了每个圆点应该在的位置,从而能绘制这些圆点。`myChart.convertToPixel('grid', dataItem)` 这句话中,第一个参数 `'grid'` 表示 `dataItem` 在 [grid](${optionPath}grid) 这个组件中(即直角坐标系)中进行转换。所谓“像素坐标”,就是以 echarts 容器 dom element 的左上角为零点的以像素为单位的坐标系中的坐标。\n\n注意这件事需要在第一次 setOption 后再进行,也就是说,须在坐标系([grid](${optionPath}grid))初始化后才能调用 `myChart.convertToPixel('grid', dataItem)`。\n\n有了这段代码后,就有了诸个能拖拽的点。接下来要为每个点,加上拖拽响应的事件:\n\n```js\n// 拖拽某个圆点的过程中会不断调用此函数。\n// 此函数中会根据拖拽后的新位置,改变 data 中的值,并用新的 data 值,重绘折线图,从而使折线图同步于被拖拽的隐藏圆点。\nfunction onPointDragging(dataIndex) {\n // 这里的 data 就是本文最初的代码块中声明的 data,在这里会被更新。\n // 这里的 this 就是被拖拽的圆点。this.position 就是圆点当前的位置。\n data[dataIndex] = myChart.convertFromPixel('grid', this.position);\n // 用更新后的 data,重绘折线图。\n myChart.setOption({\n series: [\n {\n id: 'a',\n data: data\n }\n ]\n });\n}\n```\n\n上面的代码中,使用了 [convertFromPixel](${mainSitePath}/api.html#echartsInstance.convertFromPixel) 这个 API。它是 [convertToPixel](${mainSitePath}/api.html#echartsInstance.convertToPixel) 的逆向过程。`myChart.convertFromPixel('grid', this.position)` 表示把当前像素坐标转换成 [grid](${optionPath}grid) 组件中直角坐标系的 dataItem 值。\n\n最后,为了使 dom 尺寸改变时,图中的元素能自适应得变化,加上这些代码:\n\n```js\nwindow.addEventListener('resize', function() {\n // 对每个拖拽圆点重新计算位置,并用 setOption 更新。\n myChart.setOption({\n graphic: echarts.util.map(data, function(item, dataIndex) {\n return {\n position: myChart.convertToPixel('grid', item)\n };\n })\n });\n});\n```\n\n## 添加 tooltip 组件\n\n到此,拖拽的基本功能就完成了。但是想要更进一步得实时看到拖拽过程中,被拖拽的点的 data 值的变化状况,我们可以使用 [tooltip](${optionPath}tooltip) 组件来实时显示这个值。但是,tooltip 有其默认的“显示”“隐藏”触发规则,在我们拖拽的场景中并不适用,所以我们还要手动定制 tooltip 的“显示”“隐藏”行为。\n\n在上述代码中分别添加如下定义:\n\n```js\nmyChart.setOption({\n // ...,\n tooltip: {\n // 表示不使用默认的“显示”“隐藏”触发规则。\n triggerOn: 'none',\n formatter: function(params) {\n return (\n 'X: ' +\n params.data[0].toFixed(2) +\n '<br>Y: ' +\n params.data[1].toFixed(2)\n );\n }\n }\n});\n```\n\n```js\nmyChart.setOption({\n graphic: data.map(function(item, dataIndex) {\n return {\n type: 'circle',\n // ...,\n // 在 mouseover 的时候显示,在 mouseout 的时候隐藏。\n onmousemove: echarts.util.curry(showTooltip, dataIndex),\n onmouseout: echarts.util.curry(hideTooltip, dataIndex)\n };\n })\n});\n\nfunction showTooltip(dataIndex) {\n myChart.dispatchAction({\n type: 'showTip',\n seriesIndex: 0,\n dataIndex: dataIndex\n });\n}\n\nfunction hideTooltip(dataIndex) {\n myChart.dispatchAction({\n type: 'hideTip'\n });\n}\n```\n\n这里使用了 [dispatchAction](api.html#echartsInstance.dispatchAction) 来显示隐藏 tooltip。用到了 [showTip](api.html#action.tooltip.showTip)、[hideTip](api.html#action.tooltip.hideTip)。\n\n## 全部代码\n\n总结一下,全部的代码如下:\n\n```js\nimport echarts from 'echarts';\n\nvar symbolSize = 20;\nvar data = [\n [15, 0],\n [-50, 10],\n [-56.5, 20],\n [-46.5, 30],\n [-22.1, 40]\n];\nvar myChart = echarts.init(document.getElementById('main'));\nmyChart.setOption({\n tooltip: {\n triggerOn: 'none',\n formatter: function(params) {\n return (\n 'X: ' +\n params.data[0].toFixed(2) +\n '<br />Y: ' +\n params.data[1].toFixed(2)\n );\n }\n },\n xAxis: { min: -100, max: 80, type: 'value', axisLine: { onZero: false } },\n yAxis: { min: -30, max: 60, type: 'value', axisLine: { onZero: false } },\n series: [\n { id: 'a', type: 'line', smooth: true, symbolSize: symbolSize, data: data }\n ]\n});\nmyChart.setOption({\n graphic: echarts.util.map(data, function(item, dataIndex) {\n return {\n type: 'circle',\n position: myChart.convertToPixel('grid', item),\n shape: { r: symbolSize / 2 },\n invisible: true,\n draggable: true,\n ondrag: echarts.util.curry(onPointDragging, dataIndex),\n onmousemove: echarts.util.curry(showTooltip, dataIndex),\n onmouseout: echarts.util.curry(hideTooltip, dataIndex),\n z: 100\n };\n })\n});\nwindow.addEventListener('resize', function() {\n myChart.setOption({\n graphic: echarts.util.map(data, function(item, dataIndex) {\n return { position: myChart.convertToPixel('grid', item) };\n })\n });\n});\nfunction showTooltip(dataIndex) {\n myChart.dispatchAction({\n type: 'showTip',\n seriesIndex: 0,\n dataIndex: dataIndex\n });\n}\nfunction hideTooltip(dataIndex) {\n myChart.dispatchAction({ type: 'hideTip' });\n}\nfunction onPointDragging(dataIndex, dx, dy) {\n data[dataIndex] = myChart.convertFromPixel('grid', this.position);\n myChart.setOption({\n series: [\n {\n id: 'a',\n data: data\n }\n ]\n });\n}\n```\n\n有了这些基础,就可以定制更多的功能了。可以加 [dataZoom](${optionPath}dataZoom) 组件,可以制作一个直角坐标系上的绘图板等等。可以发挥想象力。\n"}}]);