LineView.js 16.5 KB
Newer Older
L
lang 已提交
1 2 3 4
define(function(require) {

    'use strict';

L
lang 已提交
5
    var zrUtil = require('zrender/core/util');
L
lang 已提交
6
    var SymbolDraw = require('../helper/SymbolDraw');
L
lang 已提交
7
    var Symbol = require('../helper/Symbol');
8
    var lineAnimationDiff = require('./lineAnimationDiff');
9
    var graphic = require('../../util/graphic');
L
lang 已提交
10 11

    var polyHelper = require('./poly');
12

L
lang 已提交
13 14
    var ChartView = require('../../view/Chart');

15 16 17 18 19
    function isPointsSame(points1, points2) {
        if (points1.length !== points2.length) {
            return;
        }
        for (var i = 0; i < points1.length; i++) {
L
lang 已提交
20 21
            var p1 = points1[i];
            var p2 = points2[i];
22 23 24 25 26 27
            if (p1[0] !== p2[0] || p1[1] !== p2[1]) {
                return;
            }
        }
        return true;
    }
L
lang 已提交
28

L
lang 已提交
29
    function getSmooth(smooth) {
30
        return typeof (smooth) === 'number' ? smooth : (smooth ? 0.3 : 0);
L
lang 已提交
31 32
    }

L
lang 已提交
33
    function getAxisExtentWithGap(axis) {
34
        var extent = axis.getGlobalExtent();
L
lang 已提交
35 36 37 38 39 40 41 42 43 44
        if (axis.onBand) {
            // Remove extra 1px to avoid line miter in clipped edge
            var halfBandWidth = axis.getBandWidth() / 2 - 1;
            var dir = extent[1] > extent[0] ? 1 : -1;
            extent[0] += dir * halfBandWidth;
            extent[1] -= dir * halfBandWidth;
        }
        return extent;
    }

L
lang 已提交
45 46
    function sign(val) {
        return val >= 0 ? 1 : -1;
L
lang 已提交
47 48 49 50 51 52 53
    }
    /**
     * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys
     * @param {module:echarts/data/List} data
     * @param {Array.<Array.<number>>} points
     * @private
     */
L
lang 已提交
54
    function getStackedOnPoints(coordSys, data) {
L
lang 已提交
55 56
        var baseAxis = coordSys.getBaseAxis();
        var valueAxis = coordSys.getOtherAxis(baseAxis);
L
lang 已提交
57 58
        var valueStart = baseAxis.onZero
            ? 0 : valueAxis.scale.getExtent()[0];
L
lang 已提交
59 60

        var valueDim = valueAxis.dim;
L
lang 已提交
61 62

        var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0;
L
lang 已提交
63

L
lang 已提交
64
        return data.mapArray([valueDim], function (val, idx) {
L
lang 已提交
65 66
            var stackedOnSameSign;
            var stackedOn = data.stackedOn;
L
lang 已提交
67 68 69 70 71 72
            // Find first stacked value with same sign
            while (stackedOn &&
                sign(stackedOn.get(valueDim, idx)) === sign(val)
            ) {
                stackedOnSameSign = stackedOn;
                break;
L
lang 已提交
73
            }
L
lang 已提交
74 75 76 77 78 79
            var stackedData = [];
            stackedData[baseDataOffset] = data.get(baseAxis.dim, idx);
            stackedData[1 - baseDataOffset] = stackedOnSameSign
                ? stackedOnSameSign.get(valueDim, idx, true) : valueStart;

            return coordSys.dataToPoint(stackedData);
L
lang 已提交
80
        }, true);
L
lang 已提交
81 82
    }

L
lang 已提交
83 84 85 86 87 88 89 90 91
    function queryDataIndex(data, payload) {
        if (payload.dataIndex != null) {
            return payload.dataIndex;
        }
        else if (payload.name != null) {
            return data.indexOfName(payload.name);
        }
    }

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    function createGridClipShape(cartesian, hasAnimation, seriesModel) {
        var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));
        var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));

        var clipPath = new graphic.Rect({
            shape: {
                x: xExtent[0],
                y: yExtent[0],
                width: xExtent[1] - xExtent[0],
                height: yExtent[1] - yExtent[0]
            }
        });

        if (hasAnimation) {
            clipPath.shape[cartesian.getBaseAxis().isHorizontal() ? 'width' : 'height'] = 0;
            graphic.initProps(clipPath, {
                shape: {
                    width: xExtent[1] - xExtent[0],
                    height: yExtent[1] - yExtent[0]
                }
            }, seriesModel);
        }

        return clipPath;
    }

    function createPolarClipShape(polar, hasAnimation, seriesModel) {
        var angleAxis = polar.getAngleAxis();
        var radiusAxis = polar.getRadiusAxis();

        var radiusExtent = radiusAxis.getExtent();
        var angleExtent = angleAxis.getExtent();

        var RADIAN = Math.PI / 180;

        var clipPath = new graphic.Sector({
            shape: {
                cx: polar.cx,
                cy: polar.cy,
                r0: radiusExtent[0],
                r: radiusExtent[1],
                startAngle: -angleExtent[0] * RADIAN,
                endAngle: -angleExtent[1] * RADIAN,
                clockwise: angleAxis.inverse
            }
        });

        if (hasAnimation) {
            clipPath.shape.endAngle = -angleExtent[0] * RADIAN;
            graphic.initProps(clipPath, {
                shape: {
                    endAngle: -angleExtent[1] * RADIAN
                }
            }, seriesModel);
        }

        return clipPath;
    }

    function createClipShape(coordSys, hasAnimation, seriesModel) {
        return coordSys.type === 'polar'
            ? createPolarClipShape(coordSys, hasAnimation, seriesModel)
            : createGridClipShape(coordSys, hasAnimation, seriesModel);
    }

L
lang 已提交
157
    return ChartView.extend({
L
lang 已提交
158 159 160

        type: 'line',

L
lang 已提交
161
        init: function () {
L
tweak  
lang 已提交
162 163
            var lineGroup = new graphic.Group();

L
lang 已提交
164 165
            var symbolDraw = new SymbolDraw();
            this.group.add(symbolDraw.group);
L
tweak  
lang 已提交
166 167
            this.group.add(lineGroup);

L
lang 已提交
168
            this._symbolDraw = symbolDraw;
L
tweak  
lang 已提交
169
            this._lineGroup = lineGroup;
L
lang 已提交
170
        },
L
lang 已提交
171

L
lang 已提交
172
        render: function (seriesModel, ecModel, api) {
L
lang 已提交
173
            var coordSys = seriesModel.coordinateSystem;
L
lang 已提交
174
            var group = this.group;
L
lang 已提交
175
            var data = seriesModel.getData();
176 177
            var lineStyleModel = seriesModel.getModel('lineStyle.normal');
            var areaStyleModel = seriesModel.getModel('areaStyle.normal');
L
lang 已提交
178

L
lang 已提交
179
            var points = data.mapArray(data.getItemLayout, true);
180

L
lang 已提交
181
            var isCoordSysPolar = coordSys.type === 'polar';
L
lang 已提交
182
            var prevCoordSys = this._coordSys;
L
lang 已提交
183

L
lang 已提交
184
            var symbolDraw = this._symbolDraw;
L
lang 已提交
185
            var polyline = this._polyline;
L
lang 已提交
186 187
            var polygon = this._polygon;

L
tweak  
lang 已提交
188 189
            var lineGroup = this._lineGroup;

L
tweak  
lang 已提交
190
            var hasAnimation = seriesModel.get('animation');
L
lang 已提交
191 192

            var isAreaChart = !areaStyleModel.isEmpty();
L
lang 已提交
193
            var stackedOnPoints = getStackedOnPoints(coordSys, data);
L
lang 已提交
194

L
lang 已提交
195
            var showSymbol = seriesModel.get('showSymbol');
196 197

            var isSymbolIgnore = showSymbol && !isCoordSysPolar && !seriesModel.get('showAllSymbol')
L
lang 已提交
198
                && this._getSymbolIgnoreFunc(data, coordSys);
L
lang 已提交
199

200 201 202 203 204 205 206 207 208
            // Remove temporary symbols
            var oldData = this._data;
            oldData && oldData.eachItemGraphicEl(function (el, idx) {
                if (el.__temp) {
                    group.remove(el);
                    oldData.setItemGraphicEl(idx, null);
                }
            });

L
lang 已提交
209
            // Remove previous created symbols if showSymbol changed to false
210 211 212 213
            if (!showSymbol) {
                symbolDraw.remove();
            }

L
lang 已提交
214
            // Initialization animation or coordinate system changed
L
lang 已提交
215 216
            if (
                !(polyline
L
lang 已提交
217
                && prevCoordSys.type === coordSys.type)
L
lang 已提交
218
            ) {
219 220
                showSymbol && symbolDraw.updateData(data, isSymbolIgnore);

L
lang 已提交
221 222 223 224 225 226 227
                polyline = this._newPolyline(group, points, coordSys, hasAnimation);
                if (isAreaChart) {
                    polygon = this._newPolygon(
                        group, points,
                        stackedOnPoints,
                        coordSys, hasAnimation
                    );
L
lang 已提交
228
                }
229
                lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel));
L
lang 已提交
230 231
            }
            else {
L
lang 已提交
232
                // Update clipPath
L
lang 已提交
233
                if (hasAnimation) {
234
                    lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel));
L
lang 已提交
235
                }
L
lang 已提交
236

237 238 239
                // Always update, or it is wrong in the case turning on legend
                // because points are not changed
                showSymbol && symbolDraw.updateData(data, isSymbolIgnore);
L
tweak  
lang 已提交
240

L
tweak  
lang 已提交
241 242 243
                // Stop symbol animation and sync with line points
                // FIXME performance?
                data.eachItemGraphicEl(function (el) {
L
lang 已提交
244
                    el.stopSymbolAnimation(true);
L
tweak  
lang 已提交
245 246
                });

247 248
                // In the case data zoom triggerred refreshing frequently
                // Data may not change if line has a category axis. So it should animate nothing
L
lang 已提交
249 250 251
                if (!isPointsSame(this._stackedOnPoints, stackedOnPoints)
                    || !isPointsSame(this._points, points)
                ) {
L
lang 已提交
252 253 254 255 256 257 258 259 260 261 262 263 264 265
                    if (hasAnimation) {
                        this._updateAnimation(
                            data, stackedOnPoints, coordSys, api
                        );
                    }
                    else {
                        polyline.setShape({
                            points: points
                        });
                        polygon && polygon.setShape({
                            points: points,
                            stackedOnPoints: stackedOnPoints
                        });
                    }
L
lang 已提交
266
                }
L
lang 已提交
267
                // Add back
L
tweak  
lang 已提交
268
                group.add(lineGroup);
L
lang 已提交
269 270
            }

271 272
            polyline.setStyle(zrUtil.defaults(
                // Use color in lineStyle first
L
lang 已提交
273
                lineStyleModel.getLineStyle(),
L
lang 已提交
274 275 276 277 278
                {
                    stroke: data.getVisual('color'),
                    lineJoin: 'bevel'
                }
            ));
L
lang 已提交
279 280 281 282 283

            var smooth = seriesModel.get('smooth');
            smooth = getSmooth(seriesModel.get('smooth'));
            polyline.shape.smooth = smooth;

L
lang 已提交
284
            if (polygon) {
L
lang 已提交
285 286 287 288
                var polygonShape = polygon.shape;
                var stackedOn = data.stackedOn;
                var stackedOnSmooth = 0;

L
lang 已提交
289
                polygon.style.opacity = 0.7;
L
lang 已提交
290
                polygon.setStyle(zrUtil.defaults(
L
lang 已提交
291 292 293 294 295 296
                    areaStyleModel.getAreaStyle(),
                    {
                        fill: data.getVisual('color'),
                        lineJoin: 'bevel'
                    }
                ));
L
lang 已提交
297 298 299 300
                polygonShape.smooth = smooth;

                if (stackedOn) {
                    var stackedOnSeries = stackedOn.hostModel;
L
lang 已提交
301
                    stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));
L
lang 已提交
302 303 304
                }

                polygonShape.stackedOnSmooth = stackedOnSmooth;
L
lang 已提交
305
            }
L
lang 已提交
306

L
lang 已提交
307
            this._data = data;
L
lang 已提交
308
            // Save the coordinate system for transition animation when data changed
L
lang 已提交
309
            this._coordSys = coordSys;
L
lang 已提交
310
            this._stackedOnPoints = stackedOnPoints;
L
lang 已提交
311
            this._points = points;
312 313
        },

L
lang 已提交
314 315 316 317
        highlight: function (seriesModel, ecModel, api, payload) {
            var data = seriesModel.getData();
            var dataIndex = queryDataIndex(data, payload);

318
            if (dataIndex != null && dataIndex >= 0) {
L
lang 已提交
319 320 321 322 323 324 325 326 327 328 329 330
                var symbol = data.getItemGraphicEl(dataIndex);
                if (!symbol) {
                    // Create a temporary symbol if it is not exists
                    symbol = new Symbol(data, dataIndex, api);
                    symbol.position = data.getItemLayout(dataIndex);
                    symbol.setZ(
                        seriesModel.get('zlevel'),
                        seriesModel.get('z')
                    );
                    symbol.__temp = true;
                    data.setItemGraphicEl(dataIndex, symbol);

L
lang 已提交
331 332
                    // Stop scale animation
                    symbol.stopAnimation(true);
333

L
lang 已提交
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
                    this.group.add(symbol);
                }
                symbol.highlight();
            }
            else {
                // Highlight whole series
                ChartView.prototype.highlight.call(
                    this, seriesModel, ecModel, api, payload
                );
            }
        },

        downplay: function (seriesModel, ecModel, api, payload) {
            var data = seriesModel.getData();
            var dataIndex = queryDataIndex(data, payload);
349
            if (dataIndex != null && dataIndex >= 0) {
L
lang 已提交
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
                var symbol = data.getItemGraphicEl(dataIndex);
                if (symbol) {
                    if (symbol.__temp) {
                        data.setItemGraphicEl(dataIndex, null);
                        this.group.remove(symbol);
                    }
                    else {
                        symbol.downplay();
                    }
                }
            }
            else {
                // Downplay whole series
                ChartView.prototype.downplay.call(
                    this, seriesModel, ecModel, api, payload
                );
            }
        },

L
lang 已提交
369 370 371 372 373
        /**
         * @param {module:zrender/container/Group} group
         * @param {Array.<Array.<number>>} points
         * @private
         */
L
tweak  
lang 已提交
374
        _newPolyline: function (group, points) {
L
lang 已提交
375 376 377 378 379 380
            var polyline = this._polyline;
            // Remove previous created polyline
            if (polyline) {
                group.remove(polyline);
            }

L
lang 已提交
381
            polyline = new polyHelper.Polyline({
L
lang 已提交
382 383 384
                shape: {
                    points: points
                },
L
lang 已提交
385 386
                silent: true,
                z2: 10
L
lang 已提交
387 388
            });

L
tweak  
lang 已提交
389
            this._lineGroup.add(polyline);
L
lang 已提交
390 391 392 393 394 395 396 397 398 399 400 401

            this._polyline = polyline;

            return polyline;
        },

        /**
         * @param {module:zrender/container/Group} group
         * @param {Array.<Array.<number>>} stackedOnPoints
         * @param {Array.<Array.<number>>} points
         * @private
         */
L
tweak  
lang 已提交
402
        _newPolygon: function (group, points, stackedOnPoints) {
L
lang 已提交
403 404 405 406 407 408
            var polygon = this._polygon;
            // Remove previous created polygon
            if (polygon) {
                group.remove(polygon);
            }

L
lang 已提交
409
            polygon = new polyHelper.Polygon({
L
lang 已提交
410 411 412 413 414 415 416
                shape: {
                    points: points,
                    stackedOnPoints: stackedOnPoints
                },
                silent: true
            });

L
tweak  
lang 已提交
417
            this._lineGroup.add(polygon);
L
lang 已提交
418 419 420 421

            this._polygon = polygon;
            return polygon;
        },
L
lang 已提交
422 423 424
        /**
         * @private
         */
L
lang 已提交
425
        _getSymbolIgnoreFunc: function (data, coordSys) {
L
lang 已提交
426
            var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
L
lang 已提交
427
            // `getLabelInterval` is provided by echarts/component/axis
L
lang 已提交
428 429
            if (categoryAxis && categoryAxis.isLabelIgnored) {
                return zrUtil.bind(categoryAxis.isLabelIgnored, categoryAxis);
L
lang 已提交
430 431 432 433 434 435
            }
        },

        /**
         * @private
         */
L
tweak  
lang 已提交
436
        // FIXME Two value axis
L
lang 已提交
437
        _updateAnimation: function (data, stackedOnPoints, coordSys, api) {
L
lang 已提交
438
            var polyline = this._polyline;
L
lang 已提交
439
            var polygon = this._polygon;
L
lang 已提交
440
            var seriesModel = data.hostModel;
L
lang 已提交
441

L
lang 已提交
442
            var diff = lineAnimationDiff(
L
lang 已提交
443 444
                this._data, data,
                this._stackedOnPoints, stackedOnPoints,
L
lang 已提交
445
                this._coordSys, coordSys
L
lang 已提交
446
            );
L
lang 已提交
447
            polyline.shape.points = diff.current;
L
lang 已提交
448

L
lang 已提交
449
            graphic.updateProps(polyline, {
L
lang 已提交
450 451 452
                shape: {
                    points: diff.next
                }
L
lang 已提交
453
            }, seriesModel);
L
lang 已提交
454

L
lang 已提交
455
            if (polygon) {
L
lang 已提交
456 457 458 459
                polygon.setShape({
                    points: diff.current,
                    stackedOnPoints: diff.stackedOnCurrent
                });
L
lang 已提交
460
                graphic.updateProps(polygon, {
L
lang 已提交
461 462 463 464
                    shape: {
                        points: diff.next,
                        stackedOnPoints: diff.stackedOnNext
                    }
L
lang 已提交
465
                }, seriesModel);
L
lang 已提交
466 467
            }

L
lang 已提交
468
            var updatedDataInfo = [];
L
lang 已提交
469 470 471
            var diffStatus = diff.status;

            for (var i = 0; i < diffStatus.length; i++) {
L
lang 已提交
472 473
                var cmd = diffStatus[i].cmd;
                if (cmd === '=') {
L
lang 已提交
474 475 476 477 478 479 480
                    var el = data.getItemGraphicEl(diffStatus[i].idx1);
                    if (el) {
                        updatedDataInfo.push({
                            el: el,
                            ptIdx: i    // Index of points
                        });
                    }
L
lang 已提交
481
                }
L
lang 已提交
482 483
            }

L
tweak  
lang 已提交
484
            if (polyline.animators && polyline.animators.length) {
L
lang 已提交
485
                polyline.animators[0].during(function () {
L
lang 已提交
486 487
                    for (var i = 0; i < updatedDataInfo.length; i++) {
                        var el = updatedDataInfo[i].el;
L
tweak  
lang 已提交
488
                        el.attr('position', polyline.shape.points[updatedDataInfo[i].ptIdx]);
L
lang 已提交
489 490 491
                    }
                });
            }
L
lang 已提交
492 493
        },

494
        remove: function (ecModel) {
L
lang 已提交
495
            var group = this.group;
L
tweak  
lang 已提交
496
            group.remove(this._lineGroup);
497
            this._symbolDraw.remove(true);
L
lang 已提交
498 499 500
        }
    });
});