GraphView.js 13.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
* 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.
*/

S
sushuang 已提交
20
import * as echarts from '../../echarts';
S
sushuang 已提交
21
import * as zrUtil from 'zrender/src/core/util';
S
sushuang 已提交
22 23 24
import SymbolDraw from '../helper/SymbolDraw';
import LineDraw from '../helper/LineDraw';
import RoamController from '../../component/helper/RoamController';
S
sushuang 已提交
25 26 27
import * as roamHelper from '../../component/helper/roamHelper';
import {onIrrelevantElement} from '../../component/helper/cursorHelper';
import * as graphic from '../../util/graphic';
S
sushuang 已提交
28 29
import adjustEdge from './adjustEdge';

30 31
var FOCUS_ADJACENCY = '__focusNodeAdjacency';
var UNFOCUS_ADJACENCY = '__unfocusNodeAdjacency';
S
sushuang 已提交
32

33 34
var nodeOpacityPath = ['itemStyle', 'opacity'];
var lineOpacityPath = ['lineStyle', 'opacity'];
S
sushuang 已提交
35 36

function getItemOpacity(item, opacityPath) {
37 38
    var opacity = item.getVisual('opacity');
    return opacity != null ? opacity : item.getModel().get(opacityPath);
S
sushuang 已提交
39 40 41 42 43
}

function fadeOutItem(item, opacityPath, opacityRatio) {
    var el = item.getGraphicEl();
    var opacity = getItemOpacity(item, opacityPath);
44

S
sushuang 已提交
45 46 47
    if (opacityRatio != null) {
        opacity == null && (opacity = 1);
        opacity *= opacityRatio;
L
lang 已提交
48
    }
L
lang 已提交
49

S
sushuang 已提交
50 51
    el.downplay && el.downplay();
    el.traverse(function (child) {
52
        if (!child.isGroup) {
53 54 55 56 57
            var opct = child.lineLabelOriginalOpacity;
            if (opct == null || opacityRatio != null) {
                opct = opacity;
            }
            child.setStyle('opacity', opct);
58
        }
S
sushuang 已提交
59 60
    });
}
61

S
sushuang 已提交
62 63 64
function fadeInItem(item, opacityPath) {
    var opacity = getItemOpacity(item, opacityPath);
    var el = item.getGraphicEl();
65
    // Should go back to normal opacity first, consider hoverLayer,
66 67
    // where current state is copied to elMirror, and support
    // emphasis opacity here.
S
sushuang 已提交
68
    el.traverse(function (child) {
69
        !child.isGroup && child.setStyle('opacity', opacity);
S
sushuang 已提交
70
    });
71
    el.highlight && el.highlight();
S
sushuang 已提交
72
}
73

S
sushuang 已提交
74
export default echarts.extendChartView({
L
lang 已提交
75

S
sushuang 已提交
76
    type: 'graph',
L
lang 已提交
77

S
sushuang 已提交
78 79 80 81
    init: function (ecModel, api) {
        var symbolDraw = new SymbolDraw();
        var lineDraw = new LineDraw();
        var group = this.group;
L
lang 已提交
82

S
sushuang 已提交
83 84
        this._controller = new RoamController(api.getZr());
        this._controllerHost = {target: group};
L
lang 已提交
85

S
sushuang 已提交
86 87
        group.add(symbolDraw.group);
        group.add(lineDraw.group);
L
lang 已提交
88

S
sushuang 已提交
89 90
        this._symbolDraw = symbolDraw;
        this._lineDraw = lineDraw;
L
lang 已提交
91

S
sushuang 已提交
92 93
        this._firstRender = true;
    },
L
lang 已提交
94

S
sushuang 已提交
95 96
    render: function (seriesModel, ecModel, api) {
        var coordSys = seriesModel.coordinateSystem;
L
lang 已提交
97

S
sushuang 已提交
98 99
        this._model = seriesModel;
        this._nodeScaleRatio = seriesModel.get('nodeScaleRatio');
L
lang 已提交
100

S
sushuang 已提交
101 102
        var symbolDraw = this._symbolDraw;
        var lineDraw = this._lineDraw;
L
lang 已提交
103

S
sushuang 已提交
104
        var group = this.group;
105

S
sushuang 已提交
106 107 108 109 110 111 112
        if (coordSys.type === 'view') {
            var groupNewProp = {
                position: coordSys.position,
                scale: coordSys.scale
            };
            if (this._firstRender) {
                group.attr(groupNewProp);
L
lang 已提交
113
            }
S
sushuang 已提交
114 115 116 117 118 119 120 121 122
            else {
                graphic.updateProps(group, groupNewProp, seriesModel);
            }
        }
        // Fix edge contact point with node
        adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));

        var data = seriesModel.getData();
        symbolDraw.updateData(data);
L
lang 已提交
123

S
sushuang 已提交
124 125
        var edgeData = seriesModel.getEdgeData();
        lineDraw.updateData(edgeData);
126

S
sushuang 已提交
127
        this._updateNodeAndLinkScale();
L
lang 已提交
128

S
sushuang 已提交
129
        this._updateController(seriesModel, ecModel, api);
L
lang 已提交
130

S
sushuang 已提交
131 132 133 134 135 136
        clearTimeout(this._layoutTimeout);
        var forceLayout = seriesModel.forceLayout;
        var layoutAnimation = seriesModel.get('force.layoutAnimation');
        if (forceLayout) {
            this._startForceLayoutIteration(forceLayout, layoutAnimation);
        }
L
lang 已提交
137

S
sushuang 已提交
138 139 140 141
        data.eachItemGraphicEl(function (el, idx) {
            var itemModel = data.getItemModel(idx);
            // Update draggable
            el.off('drag').off('dragend');
142
            var draggable = itemModel.get('draggable');
S
sushuang 已提交
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
            if (draggable) {
                el.on('drag', function () {
                    if (forceLayout) {
                        forceLayout.warmUp();
                        !this._layouting
                            && this._startForceLayoutIteration(forceLayout, layoutAnimation);
                        forceLayout.setFixed(idx);
                        // Write position back to layout
                        data.setItemLayout(idx, el.position);
                    }
                }, this).on('dragend', function () {
                    if (forceLayout) {
                        forceLayout.setUnfixed(idx);
                    }
                }, this);
L
lang 已提交
158
            }
S
sushuang 已提交
159
            el.setDraggable(draggable && forceLayout);
160

161 162
            el[FOCUS_ADJACENCY] && el.off('mouseover', el[FOCUS_ADJACENCY]);
            el[UNFOCUS_ADJACENCY] && el.off('mouseout', el[UNFOCUS_ADJACENCY]);
S
sushuang 已提交
163 164

            if (itemModel.get('focusNodeAdjacency')) {
165
                el.on('mouseover', el[FOCUS_ADJACENCY] = function () {
S
sushuang 已提交
166 167 168 169
                    api.dispatchAction({
                        type: 'focusNodeAdjacency',
                        seriesId: seriesModel.id,
                        dataIndex: el.dataIndex
1
100pah 已提交
170
                    });
S
sushuang 已提交
171
                });
172
                el.on('mouseout', el[UNFOCUS_ADJACENCY] = function () {
S
sushuang 已提交
173 174 175
                    api.dispatchAction({
                        type: 'unfocusNodeAdjacency',
                        seriesId: seriesModel.id
1
100pah 已提交
176
                    });
S
sushuang 已提交
177 178
                });
            }
1
100pah 已提交
179

S
sushuang 已提交
180
        }, this);
L
lang 已提交
181

S
sushuang 已提交
182 183
        data.graph.eachEdge(function (edge) {
            var el = edge.getGraphicEl();
184

185 186
            el[FOCUS_ADJACENCY] && el.off('mouseover', el[FOCUS_ADJACENCY]);
            el[UNFOCUS_ADJACENCY] && el.off('mouseout', el[UNFOCUS_ADJACENCY]);
187

S
sushuang 已提交
188
            if (edge.getModel().get('focusNodeAdjacency')) {
189
                el.on('mouseover', el[FOCUS_ADJACENCY] = function () {
S
sushuang 已提交
190 191 192 193
                    api.dispatchAction({
                        type: 'focusNodeAdjacency',
                        seriesId: seriesModel.id,
                        edgeDataIndex: edge.dataIndex
194
                    });
S
sushuang 已提交
195
                });
196
                el.on('mouseout', el[UNFOCUS_ADJACENCY] = function () {
S
sushuang 已提交
197 198 199
                    api.dispatchAction({
                        type: 'unfocusNodeAdjacency',
                        seriesId: seriesModel.id
200
                    });
S
sushuang 已提交
201 202 203
                });
            }
        });
204

S
sushuang 已提交
205 206 207 208 209
        var circularRotateLabel = seriesModel.get('layout') === 'circular'
            && seriesModel.get('circular.rotateLabel');
        var cx = data.getLayout('cx');
        var cy = data.getLayout('cy');
        data.eachItemGraphicEl(function (el, idx) {
210 211
            var itemModel = data.getItemModel(idx);
            var labelRotate = itemModel.get('label.rotate') || 0;
S
sushuang 已提交
212 213 214 215 216 217
            var symbolPath = el.getSymbolPath();
            if (circularRotateLabel) {
                var pos = data.getItemLayout(idx);
                var rad = Math.atan2(pos[1] - cy, pos[0] - cx);
                if (rad < 0) {
                    rad = Math.PI * 2 + rad;
218
                }
S
sushuang 已提交
219 220 221
                var isLeft = pos[0] < cx;
                if (isLeft) {
                    rad = rad - Math.PI;
222
                }
S
sushuang 已提交
223
                var textPosition = isLeft ? 'left' : 'right';
224 225 226 227 228 229 230 231 232 233 234
                graphic.modifyLabelStyle(
                    symbolPath,
                    {
                        textRotation: -rad,
                        textPosition: textPosition,
                        textOrigin: 'center'
                    },
                    {
                        textPosition: textPosition
                    }
                );
S
sushuang 已提交
235 236
            }
            else {
237 238 239 240 241 242
                graphic.modifyLabelStyle(
                    symbolPath,
                    {
                        textRotation: labelRotate *= Math.PI / 180
                    }
                );
S
sushuang 已提交
243 244
            }
        });
245

S
sushuang 已提交
246 247
        this._firstRender = false;
    },
L
lang 已提交
248

S
sushuang 已提交
249 250 251 252
    dispose: function () {
        this._controller && this._controller.dispose();
        this._controllerHost = {};
    },
1
100pah 已提交
253

S
sushuang 已提交
254 255 256 257 258
    focusNodeAdjacency: function (seriesModel, ecModel, api, payload) {
        var data = this._model.getData();
        var graph = data.graph;
        var dataIndex = payload.dataIndex;
        var edgeDataIndex = payload.edgeDataIndex;
259

S
sushuang 已提交
260 261
        var node = graph.getNodeByIndex(dataIndex);
        var edge = graph.getEdgeByIndex(edgeDataIndex);
1
100pah 已提交
262

S
sushuang 已提交
263 264 265
        if (!node && !edge) {
            return;
        }
1
100pah 已提交
266

S
sushuang 已提交
267 268 269 270 271 272
        graph.eachNode(function (node) {
            fadeOutItem(node, nodeOpacityPath, 0.1);
        });
        graph.eachEdge(function (edge) {
            fadeOutItem(edge, lineOpacityPath, 0.1);
        });
L
lang 已提交
273

S
sushuang 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
        if (node) {
            fadeInItem(node, nodeOpacityPath);
            zrUtil.each(node.edges, function (adjacentEdge) {
                if (adjacentEdge.dataIndex < 0) {
                    return;
                }
                fadeInItem(adjacentEdge, lineOpacityPath);
                fadeInItem(adjacentEdge.node1, nodeOpacityPath);
                fadeInItem(adjacentEdge.node2, nodeOpacityPath);
            });
        }
        if (edge) {
            fadeInItem(edge, lineOpacityPath);
            fadeInItem(edge.node1, nodeOpacityPath);
            fadeInItem(edge.node2, nodeOpacityPath);
        }
    },
L
lang 已提交
291

S
sushuang 已提交
292 293
    unfocusNodeAdjacency: function (seriesModel, ecModel, api, payload) {
        var graph = this._model.getData().graph;
294

S
sushuang 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
        graph.eachNode(function (node) {
            fadeOutItem(node, nodeOpacityPath);
        });
        graph.eachEdge(function (edge) {
            fadeOutItem(edge, lineOpacityPath);
        });
    },

    _startForceLayoutIteration: function (forceLayout, layoutAnimation) {
        var self = this;
        (function step() {
            forceLayout.step(function (stopped) {
                self.updateLayout(self._model);
                (self._layouting = !stopped) && (
                    layoutAnimation
                        ? (self._layoutTimeout = setTimeout(step, 16))
                        : step()
                );
313
            });
S
sushuang 已提交
314 315 316 317 318 319 320 321 322 323 324 325
        })();
    },

    _updateController: function (seriesModel, ecModel, api) {
        var controller = this._controller;
        var controllerHost = this._controllerHost;
        var group = this.group;

        controller.setPointerChecker(function (e, x, y) {
            var rect = group.getBoundingRect();
            rect.applyTransform(group.transform);
            return rect.contain(x, y)
S
sushuang 已提交
326
                && !onIrrelevantElement(e, api, seriesModel);
S
sushuang 已提交
327
        });
328

S
sushuang 已提交
329 330 331 332 333 334 335 336 337 338 339
        if (seriesModel.coordinateSystem.type !== 'view') {
            controller.disable();
            return;
        }
        controller.enable(seriesModel.get('roam'));
        controllerHost.zoomLimit = seriesModel.get('scaleLimit');
        controllerHost.zoom = seriesModel.coordinateSystem.getZoom();

        controller
            .off('pan')
            .off('zoom')
340 341
            .on('pan', function (e) {
                roamHelper.updateViewOnPan(controllerHost, e.dx, e.dy);
S
sushuang 已提交
342 343 344
                api.dispatchAction({
                    seriesId: seriesModel.id,
                    type: 'graphRoam',
345 346
                    dx: e.dx,
                    dy: e.dy
S
sushuang 已提交
347 348
                });
            })
349 350
            .on('zoom', function (e) {
                roamHelper.updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);
S
sushuang 已提交
351 352 353
                api.dispatchAction({
                    seriesId: seriesModel.id,
                    type: 'graphRoam',
354 355 356
                    zoom: e.scale,
                    originX: e.originX,
                    originY: e.originY
S
sushuang 已提交
357 358 359 360 361 362
                });
                this._updateNodeAndLinkScale();
                adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
                this._lineDraw.updateLayout();
            }, this);
    },
L
lang 已提交
363

S
sushuang 已提交
364 365 366
    _updateNodeAndLinkScale: function () {
        var seriesModel = this._model;
        var data = seriesModel.getData();
367

S
sushuang 已提交
368 369
        var nodeScale = this._getNodeGlobalScale(seriesModel);
        var invScale = [nodeScale, nodeScale];
370

S
sushuang 已提交
371 372 373 374
        data.eachItemGraphicEl(function (el, idx) {
            el.attr('scale', invScale);
        });
    },
375

S
sushuang 已提交
376 377 378 379 380
    _getNodeGlobalScale: function (seriesModel) {
        var coordSys = seriesModel.coordinateSystem;
        if (coordSys.type !== 'view') {
            return 1;
        }
L
lang 已提交
381

S
sushuang 已提交
382
        var nodeScaleRatio = this._nodeScaleRatio;
L
lang 已提交
383

S
sushuang 已提交
384 385 386 387 388
        var groupScale = coordSys.scale;
        var groupZoom = (groupScale && groupScale[0]) || 1;
        // Scale node when zoom changes
        var roamZoom = coordSys.getZoom();
        var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;
L
lang 已提交
389

S
sushuang 已提交
390 391
        return nodeScale / groupZoom;
    },
L
lang 已提交
392

S
sushuang 已提交
393 394
    updateLayout: function (seriesModel) {
        adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel));
L
lang 已提交
395

S
sushuang 已提交
396 397 398
        this._symbolDraw.updateLayout();
        this._lineDraw.updateLayout();
    },
L
lang 已提交
399

S
sushuang 已提交
400 401 402 403
    remove: function (ecModel, api) {
        this._symbolDraw && this._symbolDraw.remove();
        this._lineDraw && this._lineDraw.remove();
    }
C
cuijian-dexter 已提交
404
});