LineView.js 17.0 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
    function createGridClipShape(cartesian, hasAnimation, seriesModel) {
        var xExtent = getAxisExtentWithGap(cartesian.getAxis('x'));
        var yExtent = getAxisExtentWithGap(cartesian.getAxis('y'));
L
lang 已提交
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
        var isHorizontal = cartesian.getBaseAxis().isHorizontal();

        var x = xExtent[0];
        var y = yExtent[0];
        var width = xExtent[1] - x;
        var height = yExtent[1] - y;
        // Expand clip shape to avoid line value exceeds axis
        if (isHorizontal) {
            height *= 10;
            y -= height / 2;
        }
        else {
            width *= 10;
            x -= width / 2;
        }
        isHorizontal ? (height *= 10) : (width *= 10);
111 112
        var clipPath = new graphic.Rect({
            shape: {
L
lang 已提交
113 114 115 116
                x: x,
                y: y,
                width: width,
                height: height
117 118 119 120
            }
        });

        if (hasAnimation) {
L
lang 已提交
121
            clipPath.shape[isHorizontal ? 'width' : 'height'] = 0;
122 123
            graphic.initProps(clipPath, {
                shape: {
L
lang 已提交
124 125
                    width: width,
                    height: height
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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
                }
            }, 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 已提交
172
    return ChartView.extend({
L
lang 已提交
173 174 175

        type: 'line',

L
lang 已提交
176
        init: function () {
L
tweak  
lang 已提交
177 178
            var lineGroup = new graphic.Group();

L
lang 已提交
179 180
            var symbolDraw = new SymbolDraw();
            this.group.add(symbolDraw.group);
L
tweak  
lang 已提交
181 182
            this.group.add(lineGroup);

L
lang 已提交
183
            this._symbolDraw = symbolDraw;
L
tweak  
lang 已提交
184
            this._lineGroup = lineGroup;
L
lang 已提交
185
        },
L
lang 已提交
186

L
lang 已提交
187
        render: function (seriesModel, ecModel, api) {
L
lang 已提交
188
            var coordSys = seriesModel.coordinateSystem;
L
lang 已提交
189
            var group = this.group;
L
lang 已提交
190
            var data = seriesModel.getData();
191 192
            var lineStyleModel = seriesModel.getModel('lineStyle.normal');
            var areaStyleModel = seriesModel.getModel('areaStyle.normal');
L
lang 已提交
193

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

L
lang 已提交
196
            var isCoordSysPolar = coordSys.type === 'polar';
L
lang 已提交
197
            var prevCoordSys = this._coordSys;
L
lang 已提交
198

L
lang 已提交
199
            var symbolDraw = this._symbolDraw;
L
lang 已提交
200
            var polyline = this._polyline;
L
lang 已提交
201 202
            var polygon = this._polygon;

L
tweak  
lang 已提交
203 204
            var lineGroup = this._lineGroup;

L
tweak  
lang 已提交
205
            var hasAnimation = seriesModel.get('animation');
L
lang 已提交
206 207

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

L
lang 已提交
210
            var showSymbol = seriesModel.get('showSymbol');
211 212

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

215 216 217 218 219 220 221 222 223
            // 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 已提交
224
            // Remove previous created symbols if showSymbol changed to false
225 226 227 228
            if (!showSymbol) {
                symbolDraw.remove();
            }

L
lang 已提交
229
            // Initialization animation or coordinate system changed
L
lang 已提交
230 231
            if (
                !(polyline
L
lang 已提交
232
                && prevCoordSys.type === coordSys.type)
L
lang 已提交
233
            ) {
234 235
                showSymbol && symbolDraw.updateData(data, isSymbolIgnore);

L
lang 已提交
236 237 238 239 240 241 242
                polyline = this._newPolyline(group, points, coordSys, hasAnimation);
                if (isAreaChart) {
                    polygon = this._newPolygon(
                        group, points,
                        stackedOnPoints,
                        coordSys, hasAnimation
                    );
L
lang 已提交
243
                }
244
                lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel));
L
lang 已提交
245 246
            }
            else {
L
lang 已提交
247
                // Update clipPath
L
lang 已提交
248
                if (hasAnimation) {
249
                    lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel));
L
lang 已提交
250
                }
L
lang 已提交
251

252 253 254
                // 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 已提交
255

L
tweak  
lang 已提交
256 257 258
                // Stop symbol animation and sync with line points
                // FIXME performance?
                data.eachItemGraphicEl(function (el) {
259
                    el.stopAnimation(true);
L
tweak  
lang 已提交
260 261
                });

262 263
                // 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 已提交
264 265 266
                if (!isPointsSame(this._stackedOnPoints, stackedOnPoints)
                    || !isPointsSame(this._points, points)
                ) {
L
lang 已提交
267 268 269 270 271 272 273 274 275 276 277 278 279 280
                    if (hasAnimation) {
                        this._updateAnimation(
                            data, stackedOnPoints, coordSys, api
                        );
                    }
                    else {
                        polyline.setShape({
                            points: points
                        });
                        polygon && polygon.setShape({
                            points: points,
                            stackedOnPoints: stackedOnPoints
                        });
                    }
L
lang 已提交
281
                }
L
lang 已提交
282
                // Add back
L
tweak  
lang 已提交
283
                group.add(lineGroup);
L
lang 已提交
284 285
            }

286 287
            polyline.setStyle(zrUtil.defaults(
                // Use color in lineStyle first
L
lang 已提交
288
                lineStyleModel.getLineStyle(),
L
lang 已提交
289 290 291 292 293
                {
                    stroke: data.getVisual('color'),
                    lineJoin: 'bevel'
                }
            ));
L
lang 已提交
294 295 296 297 298

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

L
lang 已提交
299
            if (polygon) {
L
lang 已提交
300 301 302 303
                var polygonShape = polygon.shape;
                var stackedOn = data.stackedOn;
                var stackedOnSmooth = 0;

L
lang 已提交
304
                polygon.style.opacity = 0.7;
L
lang 已提交
305
                polygon.setStyle(zrUtil.defaults(
L
lang 已提交
306 307 308 309 310 311
                    areaStyleModel.getAreaStyle(),
                    {
                        fill: data.getVisual('color'),
                        lineJoin: 'bevel'
                    }
                ));
L
lang 已提交
312 313 314 315
                polygonShape.smooth = smooth;

                if (stackedOn) {
                    var stackedOnSeries = stackedOn.hostModel;
L
lang 已提交
316
                    stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth'));
L
lang 已提交
317 318 319
                }

                polygonShape.stackedOnSmooth = stackedOnSmooth;
L
lang 已提交
320
            }
L
lang 已提交
321

L
lang 已提交
322
            this._data = data;
L
lang 已提交
323
            // Save the coordinate system for transition animation when data changed
L
lang 已提交
324
            this._coordSys = coordSys;
L
lang 已提交
325
            this._stackedOnPoints = stackedOnPoints;
L
lang 已提交
326
            this._points = points;
327 328
        },

L
lang 已提交
329 330 331 332
        highlight: function (seriesModel, ecModel, api, payload) {
            var data = seriesModel.getData();
            var dataIndex = queryDataIndex(data, payload);

333
            if (dataIndex != null && dataIndex >= 0) {
L
lang 已提交
334 335 336
                var symbol = data.getItemGraphicEl(dataIndex);
                if (!symbol) {
                    // Create a temporary symbol if it is not exists
L
Typo  
lang 已提交
337
                    var pt = data.getItemLayout(dataIndex);
L
lang 已提交
338
                    symbol = new Symbol(data, dataIndex, api);
L
Typo  
lang 已提交
339
                    symbol.position = pt;
L
lang 已提交
340 341 342 343
                    symbol.setZ(
                        seriesModel.get('zlevel'),
                        seriesModel.get('z')
                    );
L
Typo  
lang 已提交
344
                    symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]);
L
lang 已提交
345 346 347
                    symbol.__temp = true;
                    data.setItemGraphicEl(dataIndex, symbol);

L
lang 已提交
348
                    // Stop scale animation
349
                    symbol.stopSymbolAnimation(true);
350

L
lang 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
                    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);
366
            if (dataIndex != null && dataIndex >= 0) {
L
lang 已提交
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
                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 已提交
386 387 388 389 390
        /**
         * @param {module:zrender/container/Group} group
         * @param {Array.<Array.<number>>} points
         * @private
         */
L
tweak  
lang 已提交
391
        _newPolyline: function (group, points) {
L
lang 已提交
392 393 394 395 396 397
            var polyline = this._polyline;
            // Remove previous created polyline
            if (polyline) {
                group.remove(polyline);
            }

L
lang 已提交
398
            polyline = new polyHelper.Polyline({
L
lang 已提交
399 400 401
                shape: {
                    points: points
                },
L
lang 已提交
402 403
                silent: true,
                z2: 10
L
lang 已提交
404 405
            });

L
tweak  
lang 已提交
406
            this._lineGroup.add(polyline);
L
lang 已提交
407 408 409 410 411 412 413 414 415 416 417 418

            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 已提交
419
        _newPolygon: function (group, points, stackedOnPoints) {
L
lang 已提交
420 421 422 423 424 425
            var polygon = this._polygon;
            // Remove previous created polygon
            if (polygon) {
                group.remove(polygon);
            }

L
lang 已提交
426
            polygon = new polyHelper.Polygon({
L
lang 已提交
427 428 429 430 431 432 433
                shape: {
                    points: points,
                    stackedOnPoints: stackedOnPoints
                },
                silent: true
            });

L
tweak  
lang 已提交
434
            this._lineGroup.add(polygon);
L
lang 已提交
435 436 437 438

            this._polygon = polygon;
            return polygon;
        },
L
lang 已提交
439 440 441
        /**
         * @private
         */
L
lang 已提交
442
        _getSymbolIgnoreFunc: function (data, coordSys) {
L
lang 已提交
443
            var categoryAxis = coordSys.getAxesByScale('ordinal')[0];
L
lang 已提交
444
            // `getLabelInterval` is provided by echarts/component/axis
L
lang 已提交
445 446
            if (categoryAxis && categoryAxis.isLabelIgnored) {
                return zrUtil.bind(categoryAxis.isLabelIgnored, categoryAxis);
L
lang 已提交
447 448 449 450 451 452
            }
        },

        /**
         * @private
         */
L
tweak  
lang 已提交
453
        // FIXME Two value axis
L
lang 已提交
454
        _updateAnimation: function (data, stackedOnPoints, coordSys, api) {
L
lang 已提交
455
            var polyline = this._polyline;
L
lang 已提交
456
            var polygon = this._polygon;
L
lang 已提交
457
            var seriesModel = data.hostModel;
L
lang 已提交
458

L
lang 已提交
459
            var diff = lineAnimationDiff(
L
lang 已提交
460 461
                this._data, data,
                this._stackedOnPoints, stackedOnPoints,
L
lang 已提交
462
                this._coordSys, coordSys
L
lang 已提交
463
            );
L
lang 已提交
464
            polyline.shape.points = diff.current;
L
lang 已提交
465

L
lang 已提交
466
            graphic.updateProps(polyline, {
L
lang 已提交
467 468 469
                shape: {
                    points: diff.next
                }
L
lang 已提交
470
            }, seriesModel);
L
lang 已提交
471

L
lang 已提交
472
            if (polygon) {
L
lang 已提交
473 474 475 476
                polygon.setShape({
                    points: diff.current,
                    stackedOnPoints: diff.stackedOnCurrent
                });
L
lang 已提交
477
                graphic.updateProps(polygon, {
L
lang 已提交
478 479 480 481
                    shape: {
                        points: diff.next,
                        stackedOnPoints: diff.stackedOnNext
                    }
L
lang 已提交
482
                }, seriesModel);
L
lang 已提交
483 484
            }

L
lang 已提交
485
            var updatedDataInfo = [];
L
lang 已提交
486 487 488
            var diffStatus = diff.status;

            for (var i = 0; i < diffStatus.length; i++) {
L
lang 已提交
489 490
                var cmd = diffStatus[i].cmd;
                if (cmd === '=') {
L
lang 已提交
491 492 493 494 495 496 497
                    var el = data.getItemGraphicEl(diffStatus[i].idx1);
                    if (el) {
                        updatedDataInfo.push({
                            el: el,
                            ptIdx: i    // Index of points
                        });
                    }
L
lang 已提交
498
                }
L
lang 已提交
499 500
            }

L
tweak  
lang 已提交
501
            if (polyline.animators && polyline.animators.length) {
L
lang 已提交
502
                polyline.animators[0].during(function () {
L
lang 已提交
503 504
                    for (var i = 0; i < updatedDataInfo.length; i++) {
                        var el = updatedDataInfo[i].el;
L
tweak  
lang 已提交
505
                        el.attr('position', polyline.shape.points[updatedDataInfo[i].ptIdx]);
L
lang 已提交
506 507 508
                    }
                });
            }
L
lang 已提交
509 510
        },

511
        remove: function (ecModel) {
L
lang 已提交
512
            var group = this.group;
L
tweak  
lang 已提交
513
            group.remove(this._lineGroup);
514
            this._symbolDraw.remove(true);
L
lang 已提交
515 516 517
        }
    });
});