This is an automated email from the ASF dual-hosted git repository. ovilia pushed a commit to branch feat-sankey-roam in repository https://gitbox.apache.org/repos/asf/echarts.git
commit 396236405809dd697d6f9c64093f3c4a7e1636bd Author: Ovilia <[email protected]> AuthorDate: Thu Sep 5 14:49:03 2024 +0800 feat(sankey): sankey support roam --- src/chart/sankey/SankeySeries.ts | 14 +++- src/chart/sankey/SankeyView.ts | 71 ++++++++++++++++++++ test/sankey-roam.html | 135 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 2 deletions(-) diff --git a/src/chart/sankey/SankeySeries.ts b/src/chart/sankey/SankeySeries.ts index 9dca447ca..1826ea4e7 100644 --- a/src/chart/sankey/SankeySeries.ts +++ b/src/chart/sankey/SankeySeries.ts @@ -34,12 +34,14 @@ import { GraphEdgeItemObject, OptionDataValueNumeric, DefaultEmphasisFocus, - CallbackDataParams + CallbackDataParams, + RoamOptionMixin } from '../../util/types'; import GlobalModel from '../../model/Global'; import SeriesData from '../../data/SeriesData'; import { LayoutRect } from '../../util/layout'; import { createTooltipMarkup } from '../../component/tooltip/tooltipMarkup'; +import View from '../../coord/View'; type FocusNodeAdjacency = boolean | 'inEdges' | 'outEdges' | 'allEdges'; @@ -95,7 +97,8 @@ export interface SankeyLevelOption extends SankeyNodeStateOption, SankeyEdgeStat export interface SankeySeriesOption extends SeriesOption<SankeyBothStateOption<CallbackDataParams>, ExtraStateOption>, SankeyBothStateOption<CallbackDataParams>, - BoxLayoutOptionMixin { + BoxLayoutOptionMixin, + RoamOptionMixin { type?: 'sankey' /** @@ -148,6 +151,8 @@ class SankeySeriesModel extends SeriesModel<SankeySeriesOption> { static readonly type = 'series.sankey'; readonly type = SankeySeriesModel.type; + coordinateSystem: View; + levelModels: Model<SankeyLevelOption>[]; layoutInfo: LayoutRect; @@ -297,6 +302,11 @@ class SankeySeriesModel extends SeriesModel<SankeySeriesOption> { layoutIterations: 32, + // true | false | 'move' | 'scale', see module:component/helper/RoamController. + roam: false, + center: null, + zoom: 1, + label: { show: true, position: 'right', diff --git a/src/chart/sankey/SankeyView.ts b/src/chart/sankey/SankeyView.ts index e84b7f7ee..47cf89e51 100644 --- a/src/chart/sankey/SankeyView.ts +++ b/src/chart/sankey/SankeyView.ts @@ -31,6 +31,11 @@ import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle'; import { getECData } from '../../util/innerStore'; import { isString, retrieve3 } from 'zrender/src/core/util'; import type { GraphEdge } from '../../data/Graph'; +import RoamController from '../../component/helper/RoamController'; +import type { RoamControllerHost } from '../../component/helper/roamHelper'; +import { onIrrelevantElement } from '../../component/helper/cursorHelper'; +import * as roamHelper from '../../component/helper/roamHelper'; +import View from '../../coord/View'; class SankeyPathShape { x1 = 0; @@ -111,6 +116,17 @@ class SankeyView extends ChartView { private _data: SeriesData; + private _controller: RoamController; + private _controllerHost: RoamControllerHost; + + init(ecModel: GlobalModel, api: ExtensionAPI): void { + this._controller = new RoamController(api.getZr()); + + this._controllerHost = { + target: this.group + } as RoamControllerHost; + } + render(seriesModel: SankeySeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { const sankeyView = this; const graph = seriesModel.getGraph(); @@ -131,6 +147,14 @@ class SankeyView extends ChartView { group.x = layoutInfo.x; group.y = layoutInfo.y; + const viewCoordSys = seriesModel.coordinateSystem = new View(); + viewCoordSys.zoomLimit = seriesModel.get('scaleLimit'); + viewCoordSys.setBoundingRect(layoutInfo.x, layoutInfo.y, width, height); + viewCoordSys.setCenter(seriesModel.get('center'), api); + viewCoordSys.setZoom(seriesModel.get('zoom')); + + this._updateController(seriesModel, ecModel, api); + // generate a bezire Curve for each edge graph.eachEdge(function (edge) { const curve = new SankeyPath(); @@ -346,6 +370,53 @@ class SankeyView extends ChartView { } dispose() { + this._controller && this._controller.dispose(); + this._controllerHost = null; + } + + private _updateController( + seriesModel: SankeySeriesModel, + ecModel: GlobalModel, + api: ExtensionAPI + ) { + const controller = this._controller; + const controllerHost = this._controllerHost; + const group = this.group; + controller.setPointerChecker(function (e, x, y) { + const rect = group.getBoundingRect(); + rect.applyTransform(group.transform); + return rect.contain(x, y) + && !onIrrelevantElement(e, api, seriesModel); + }); + + controller.enable(seriesModel.get('roam')); + controllerHost.zoomLimit = seriesModel.get('scaleLimit'); + controllerHost.zoom = seriesModel.coordinateSystem.getZoom(); + + controller + .off('pan') + .off('zoom') + .on('pan', (e) => { + roamHelper.updateViewOnPan(controllerHost, e.dx, e.dy); + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'sankeyRoam', + dx: e.dx, + dy: e.dy + }); + }) + .on('zoom', (e) => { + roamHelper.updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY); + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'sankeyRoam', + zoom: e.scale, + originX: e.originX, + originY: e.originY + }); + // Only update label layout on zoom + api.updateLabelLayout(); + }); } } diff --git a/test/sankey-roam.html b/test/sankey-roam.html new file mode 100644 index 000000000..ad749941f --- /dev/null +++ b/test/sankey-roam.html @@ -0,0 +1,135 @@ +<!DOCTYPE html> +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +--> + + +<html> + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <script src="lib/simpleRequire.js"></script> + <script src="lib/config.js"></script> + <script src="lib/jquery.min.js"></script> + <script src="lib/facePrint.js"></script> + <script src="lib/testHelper.js"></script> + <!-- <script src="ut/lib/canteen.js"></script> --> + <link rel="stylesheet" href="lib/reset.css" /> + </head> + <body> + <style> + </style> + + + + <div id="main0"></div> + + + + + + + <script> + require([ + 'echarts', + // 'map/js/china', + // './data/nutrients.json' + ], function (echarts) { + var option; + option = { + series: { + type: 'sankey', + layout: 'none', + roam: true, + scaleLimit: { + min: 0.5, + max: 1.5 + }, + emphasis: { + focus: 'adjacency' + }, + data: [ + { + name: 'a' + }, + { + name: 'b' + }, + { + name: 'a1' + }, + { + name: 'a2' + }, + { + name: 'b1' + }, + { + name: 'c' + } + ], + links: [ + { + source: 'a', + target: 'a1', + value: 5 + }, + { + source: 'a', + target: 'a2', + value: 3 + }, + { + source: 'b', + target: 'b1', + value: 8 + }, + { + source: 'a', + target: 'b1', + value: 3 + }, + { + source: 'b1', + target: 'a1', + value: 1 + }, + { + source: 'b1', + target: 'c', + value: 2 + } + ] + } + }; + var chart = testHelper.create(echarts, 'main0', { + title: [ + 'Sankey Roam' + ], + option: option, + // height: 300, + // buttons: [{text: 'btn-txt', onclick: function () {}}], + // recordCanvas: true, + }); + }); + </script> + + + </body> +</html> + --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
