echarts.js 16.6 KB
Newer Older
L
lang 已提交
1
/**
L
lang 已提交
2
 * TODO setTheme
3
 *      axis position 统一处理
L
lang 已提交
4
 *      规范 Symbol 配置和绘制, customPath
L
lang 已提交
5 6
 *
 *      每次 update 只刷新 model 变化的那些 component(需要做依赖收集)
L
lang 已提交
7
 */
L
lang 已提交
8 9
define(function (require) {

L
lang 已提交
10
    var GlobalModel = require('./model/Global');
L
lang 已提交
11
    var ExtensionAPI = require('./ExtensionAPI');
L
lang 已提交
12
    var CoordinateSystemManager = require('./CoordinateSystem');
L
lang 已提交
13

L
Update  
lang 已提交
14 15 16 17 18 19
    var ComponentModel = require('./model/Component');
    var SeriesModel = require('./model/Series');

    var ComponentView = require('./view/Component');
    var ChartView = require('./view/Chart');

L
lang 已提交
20
    var zrender = require('zrender');
L
lang 已提交
21
    var zrUtil = require('zrender/core/util');
L
lang 已提交
22

23 24
    var VISUAL_CODING_STAGES = ['echarts', 'chart', 'component'];

L
lang 已提交
25
    // TODO Transform first or filter first
L
lang 已提交
26 27
    var PROCESSOR_STAGES = ['transform', 'filter', 'statistic'];

L
lang 已提交
28 29 30
    /**
     * @module echarts~ECharts
     */
L
lang 已提交
31 32
    var ECharts = function (dom, theme, opts) {
        opts = opts || {};
L
lang 已提交
33

L
lang 已提交
34 35 36 37 38
        /**
         * @type {HTMLDomElement}
         * @private
         */
        this._dom = dom;
L
lang 已提交
39 40 41 42
        /**
         * @type {module:zrender/ZRender}
         * @private
         */
L
lang 已提交
43 44 45
        this._zr = zrender.init(dom, {
            renderer: opts.renderer || 'canvas'
        });
L
lang 已提交
46

L
lang 已提交
47 48 49 50
        /**
         * @type {Object}
         * @private
         */
L
lang 已提交
51
        this._theme = zrUtil.clone(theme);
L
lang 已提交
52

L
lang 已提交
53 54 55 56
        /**
         * @type {Array.<module:echarts/view/Chart>}
         * @private
         */
L
lang 已提交
57
        this._chartsList = [];
L
lang 已提交
58 59 60 61 62

        /**
         * @type {Object.<string, module:echarts/view/Chart>}
         * @private
         */
L
lang 已提交
63 64
        this._chartsMap = {};

L
lang 已提交
65 66 67 68
        /**
         * @type {Array.<module:echarts/view/Component>}
         * @private
         */
L
lang 已提交
69
        this._componentsList = [];
L
lang 已提交
70 71 72 73 74

        /**
         * @type {Object.<string, module:echarts/view/Component>}
         * @private
         */
L
lang 已提交
75 76
        this._componentsMap = {};

L
lang 已提交
77
        /**
L
lang 已提交
78
         * @type {module:echarts/ExtensionAPI}
L
lang 已提交
79 80
         * @private
         */
L
lang 已提交
81
        this._extensionAPI = new ExtensionAPI(this);
L
lang 已提交
82

L
lang 已提交
83 84 85 86
        /**
         * @type {module:echarts/CoordinateSystem}
         * @private
         */
L
lang 已提交
87
        this._coordinateSystem = new CoordinateSystemManager();
L
lang 已提交
88

L
lang 已提交
89 90 91 92 93
        /**
         * Layout instances
         * @type {Array}
         * @private
         */
L
lang 已提交
94 95 96
        this._layouts = zrUtil.map(layoutClasses, function (Layout) {
            return new Layout();
        });
L
lang 已提交
97 98 99 100 101 102 103 104 105 106 107 108

        /**
         * @type {boolean}
         * @private
         */
        this._needsUpdate = false;

        this._zr.animation.on('frame', function () {
            if (this._needsUpdate) {
                this.updateImmediately();
            }
        }, this);
L
lang 已提交
109 110 111 112
    };

    ECharts.prototype = {

L
lang 已提交
113 114 115 116
        getDom: function () {
            return this._dom;
        },

L
lang 已提交
117 118 119 120
        getZr: function () {
            return this._zr;
        },

L
lang 已提交
121
        setOption: function (option, notMerge) {
L
lang 已提交
122
            // PENDING
L
lang 已提交
123
            option = zrUtil.clone(option, true);
L
lang 已提交
124

L
lang 已提交
125
            var ecModel = this._model;
L
lang 已提交
126
            if (!ecModel || notMerge) {
P
pah100 已提交
127 128 129
                ecModel = new GlobalModel(
                    option, null, this._theme, this._extensionAPI
                );
L
lang 已提交
130 131 132
                this._model = ecModel;
            }
            else {
P
pah100 已提交
133
                ecModel.restoreData();
L
lang 已提交
134 135
                ecModel.mergeOption(option);
            }
L
lang 已提交
136

L
lang 已提交
137
            this._prepareComponents(ecModel);
L
lang 已提交
138

L
lang 已提交
139
            this._prepareCharts(ecModel);
L
lang 已提交
140 141

            this.updateImmediately();
L
lang 已提交
142 143
        },

L
lang 已提交
144 145 146 147 148 149 150
        setTheme: function (theme) {

        },

        /**
         * @return {number}
         */
L
lang 已提交
151 152 153
        getWidth: function () {
            return this._zr.getWidth();
        },
L
lang 已提交
154

L
lang 已提交
155 156 157
        /**
         * @return {number}
         */
L
lang 已提交
158 159 160 161 162
        getHeight: function () {
            return this._zr.getHeight();
        },

        update: function () {
L
lang 已提交
163
            this._needsUpdate = true;
L
lang 已提交
164 165
        },

P
pah100 已提交
166
        updateImmediately: function (event) {
L
lang 已提交
167
            console.time('update');
168

L
lang 已提交
169 170
            var ecModel = this._model;

P
pah100 已提交
171
            ecModel.restoreData();
L
lang 已提交
172

P
pah100 已提交
173 174 175 176
            // TODO
            // Save total ecModel here for undo/redo (after restoring data and before processing data).
            // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.

L
lang 已提交
177
            this._processData(ecModel);
L
lang 已提交
178

L
lang 已提交
179 180
            this._stackSeriesData(ecModel);

L
lang 已提交
181
            this._coordinateSystem.update(ecModel, this._extensionAPI);
L
lang 已提交
182

P
pah100 已提交
183
            this._doLayout(ecModel, event);
L
lang 已提交
184

185 186
            this._doVisualCoding(ecModel);

P
pah100 已提交
187
            this._doRender(ecModel, event);
L
lang 已提交
188 189

            this._needsUpdate = false;
190

L
lang 已提交
191 192 193
            // Set background
            this._dom.style.backgroundColor = ecModel.get('backgroundColor');

L
lang 已提交
194
            console.timeEnd('update');
L
lang 已提交
195 196
        },

L
lang 已提交
197
        resize: function () {
L
lang 已提交
198
            // var ecModel = this._model;
L
lang 已提交
199

L
lang 已提交
200
            // this._coordinateSystem.resize(ecModel, this._extensionAPI);
L
lang 已提交
201

L
lang 已提交
202
            // this._doVisualCoding(ecModel);
L
lang 已提交
203

L
lang 已提交
204 205 206 207 208
            // this._doLayout(ecModel);

            // this._doRender(ecModel);

            this.updateImmediately();
L
lang 已提交
209 210
        },

P
pah100 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223 224
        /**
         * @pubilc
         * @param {Object} event
         * @param {string} [event.type] Event type
         * @param {number} [event.from] From uid
         */
        dispatch: function (event) {
            var action = actions[event.type];
            if (action) {
                action(event, this._model);
                this.updateImmediately(event);
            }
        },

L
lang 已提交
225
        _prepareCharts: function (ecModel) {
L
lang 已提交
226

L
lang 已提交
227 228
            var chartsList = this._chartsList;
            var chartsMap = this._chartsMap;
L
lang 已提交
229
            var zr = this._zr;
L
lang 已提交
230 231 232 233 234 235

            for (var i = 0; i < chartsList.length; i++) {
                chartsList[i].__keepAlive = false;
            }

            ecModel.eachSeries(function (seriesModel, idx) {
P
pah100 已提交
236
                var id = seriesModel.uid;
L
lang 已提交
237 238

                var chart = chartsMap[id];
239 240
                if (!chart) {
                    var Clazz = ChartView.getClass(
241 242
                        ComponentModel.parseComponentType(seriesModel.type).sub
                    );
243 244
                    if (Clazz) {
                        chart = new Clazz();
L
lang 已提交
245
                        chart.init(ecModel, this._extensionAPI);
L
lang 已提交
246 247
                        chartsMap[id] = chart;
                        chartsList.push(chart);
L
lang 已提交
248
                        zr.add(chart.group);
L
lang 已提交
249 250 251 252 253 254
                    }
                    else {
                        // Error
                    }
                }

L
lang 已提交
255 256
                chart.__keepAlive = true;
                chart.__id = id;
L
lang 已提交
257 258
            }, this);

L
lang 已提交
259 260
            for (var i = 0; i < chartsList.length;) {
                var chart = chartsList[i];
L
lang 已提交
261
                if (!chart.__keepAlive) {
L
lang 已提交
262
                    zr.remove(chart.group);
L
lang 已提交
263
                    chart.dispose(this._extensionAPI);
L
lang 已提交
264 265
                    chartsList.splice(i, 1);
                    delete chartsMap[chart.__id];
L
lang 已提交
266 267 268 269
                }
                else {
                    i++;
                }
270
            }
L
lang 已提交
271 272
        },

L
lang 已提交
273
        _prepareComponents: function (ecModel) {
L
lang 已提交
274 275 276 277 278 279 280 281

            var componentsMap = this._componentsMap;
            var componentsList = this._componentsList;

            for (var i = 0; i < componentsList.length; i++) {
                componentsList[i].__keepAlive = true;
            }

P
pah100 已提交
282
            ecModel.eachComponent(function (componentType, componentModel) {
283 284 285 286
                if (componentType === 'series') {
                    return;
                }

P
pah100 已提交
287
                var id = componentModel.uid;
288 289 290
                var component = componentsMap[id];
                if (!component) {
                    // Create and add component
P
pah100 已提交
291 292 293
                    var Clazz = ComponentView.getClass(
                        componentType, componentModel.option.type
                    );
L
lang 已提交
294

295 296
                    if (Clazz) {
                        component = new Clazz();
L
lang 已提交
297
                        component.init(ecModel, this._extensionAPI);
L
lang 已提交
298
                        componentsMap[id] = component;
L
lang 已提交
299
                        componentsList.push(component);
L
lang 已提交
300 301

                        this._zr.add(component.group);
L
lang 已提交
302
                    }
303 304 305 306 307
                }
                component.__id = id;
                component.__keepAlive = true;
                // Used in rendering
                component.__model = componentModel;
L
lang 已提交
308 309 310 311 312 313
            }, this);

            for (var i = 0; i < componentsList.length;) {
                var component = componentsList[i];
                if (! component.__keepAlive) {
                    this._zr.remove(component.group);
L
lang 已提交
314
                    component.dispose(this._extensionAPI);
L
lang 已提交
315 316
                    componentsList.splice(i, 1);
                    delete componentsMap[component.__id];
L
lang 已提交
317 318
                }
                else {
L
lang 已提交
319
                    i++;
L
lang 已提交
320
                }
321
            }
L
lang 已提交
322 323
        },

L
lang 已提交
324 325 326 327 328 329
        /**
         * Processor data in each series
         *
         * @param {module:echarts/model/Global} ecModel
         * @private
         */
L
lang 已提交
330
        _processData: function (ecModel) {
L
lang 已提交
331 332 333 334
            zrUtil.each(PROCESSOR_STAGES, function (stage) {
                zrUtil.each(dataProcessorFuncs[stage] || [], function (process) {
                    process(ecModel);
                });
L
lang 已提交
335
            });
L
lang 已提交
336 337
        },

L
lang 已提交
338 339 340 341 342 343 344 345
        /**
         * @private
         */
        _stackSeriesData: function (ecModel) {
            var stackedDataMap = {};
            ecModel.eachSeries(function (series) {
                var stack = series.get('stack');
                var data = series.getData();
L
lang 已提交
346
                if (stack && data.type === 'list') {
L
lang 已提交
347 348
                    var previousStack = stackedDataMap[stack];
                    if (previousStack) {
L
lang 已提交
349
                        data.stackedOn = previousStack;
L
lang 已提交
350 351 352 353 354 355
                    }
                    stackedDataMap[stack] = data;
                }
            });
        },

L
lang 已提交
356 357 358 359 360 361
        /**
         * Layout before each chart render there series after visual coding and data processing
         *
         * @param {module:echarts/model/Global} ecModel
         * @private
         */
P
pah100 已提交
362
        _doLayout: function (ecModel, event) {
L
Add pie  
lang 已提交
363
            var api = this._extensionAPI;
L
lang 已提交
364
            zrUtil.each(this._layouts, function (layout) {
L
Add pie  
lang 已提交
365
                layout.update(ecModel, api, event);
366 367
            });
            zrUtil.each(layoutFuncs, function (layout) {
L
Add pie  
lang 已提交
368
                layout(ecModel, api, event);
L
lang 已提交
369 370 371 372 373 374 375 376 377 378
            });
        },

        /**
         * Code visual infomation from data after data processing
         *
         * @param {module:echarts/model/Global} ecModel
         * @private
         */
        _doVisualCoding: function (ecModel) {
379 380 381 382
            zrUtil.each(VISUAL_CODING_STAGES, function (stage) {
                zrUtil.each(visualCodingFuncs[stage] || [], function (visualCoding) {
                    visualCoding(ecModel);
                });
L
lang 已提交
383 384 385 386 387 388
            });
        },

        /**
         * Render each chart and component
         */
P
pah100 已提交
389
        _doRender: function (ecModel, event) {
L
lang 已提交
390
            var api = this._extensionAPI;
L
lang 已提交
391
            // Render all components
L
lang 已提交
392
            zrUtil.each(this._componentsList, function (component) {
L
lang 已提交
393 394 395 396 397 398 399
                var componentModel = component.__model;
                component.render(componentModel, ecModel, api, event);

                var z = componentModel.get('z');
                var zlevel = componentModel.get('zlevel');
                // Set z and zlevel
                component.group.traverse(function (el) {
L
lang 已提交
400 401
                    z != null && (el.z = z);
                    zlevel != null && (el.zlevel = zlevel);
L
lang 已提交
402
                });
L
lang 已提交
403
            }, this);
P
pah100 已提交
404

L
lang 已提交
405
            zrUtil.each(this._chartsList, function (chart) {
406
                chart.__keepAlive = false;
L
lang 已提交
407
            }, this);
P
pah100 已提交
408

L
lang 已提交
409
            // Render all charts
L
lang 已提交
410
            ecModel.eachSeries(function (seriesModel, idx) {
P
pah100 已提交
411
                var id = seriesModel.uid;
L
lang 已提交
412
                var chart = this._chartsMap[id];
413
                chart.__keepAlive = true;
P
pah100 已提交
414
                chart.render(seriesModel, ecModel, api, event);
L
lang 已提交
415 416 417 418 419

                var z = seriesModel.get('z');
                var zlevel = seriesModel.get('zlevel');
                // Set z and zlevel
                chart.group.traverse(function (el) {
L
lang 已提交
420 421
                    z != null && (el.z = z);
                    zlevel != null && (el.zlevel = zlevel);
L
lang 已提交
422
                });
L
lang 已提交
423
            }, this);
P
pah100 已提交
424

425 426 427 428 429 430
            // Remove groups of charts
            zrUtil.each(this._chartsList, function (chart) {
                if (!chart.__keepAlive) {
                    chart.remove();
                }
            }, this);
L
lang 已提交
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
        },

        dispose: function () {
            zrUtil.each(this._components, function (component) {
                component.dispose();
            });
            zrUtil.each(this._charts, function (chart) {
                chart.dispose();
            });

            this.zr.dispose();
        }
    };


L
lang 已提交
446 447 448 449
    /**
     * @type {Array.<Function>}
     * @inner
     */
P
pah100 已提交
450 451
    var actions = [];

L
lang 已提交
452 453 454 455
    /**
     * @type {Array.<Function>}
     * @inner
     */
L
lang 已提交
456 457
    var layoutClasses = [];

L
lang 已提交
458 459 460 461
    /**
     * @type {Array.<Function>}
     * @inner
     */
462 463
    var layoutFuncs = [];

L
lang 已提交
464 465 466 467 468 469 470 471 472 473 474 475
    /**
     * Data processor functions of each stage
     * @type {Array.<Object.<string, Function>>}
     * @inner
     */
    var dataProcessorFuncs = {};

    /**
     * Visual coding functions of each stage
     * @type {Array.<Object.<string, Function>>}
     * @inner
     */
476
    var visualCodingFuncs = {};
L
lang 已提交
477

L
lang 已提交
478 479 480 481 482
    /**
     * @module echarts
     */
    var echarts = {

L
lang 已提交
483 484 485 486 487
        /**
         * @param {HTMLDomElement} dom
         * @param {Object} [theme]
         * @param {Object} opts
         */
L
lang 已提交
488 489
        init: function (dom, theme, opts) {
            return new ECharts(dom, theme, opts);
L
lang 已提交
490 491
        },

L
lang 已提交
492
        /**
L
lang 已提交
493 494
         * @param {string} stage
         * @param {Function} processorFunc
L
lang 已提交
495
         */
L
lang 已提交
496 497 498
        registerProcessor: function (stage, processorFunc) {
            if (zrUtil.indexOf(PROCESSOR_STAGES, stage) < 0) {
                throw new Error('stage should be one of ' + PROCESSOR_STAGES);
L
lang 已提交
499
            }
L
lang 已提交
500 501
            var funcs = dataProcessorFuncs[stage] || (dataProcessorFuncs[stage] = []);
            funcs.push(processorFunc);
L
lang 已提交
502 503
        },

P
pah100 已提交
504
        /**
505 506 507 508 509 510 511
         * Usage:
         * registerAction('someAction', 'someEvent', function () { ... });
         * registerAction('someAction', function () { ... });
         *
         * @param {string} actionName
         * @param {string=} eventName Can be ignored
         * @param {Function=} action
P
pah100 已提交
512
         */
513 514 515 516 517
        registerAction: function (actionName, eventName, action) {
            if (typeof eventName === 'function') {
                action = eventName;
                eventName = null;
            }
P
pah100 已提交
518 519 520
            if (!actions[actionName]) {
                actions[actionName] = action;
            }
521 522
            // TODO
            // event
P
pah100 已提交
523 524
        },

L
lang 已提交
525 526 527 528
        /**
         * @param {string} type
         * @param {*} CoordinateSystem
         */
L
lang 已提交
529
        registerCoordinateSystem: function (type, CoordinateSystem) {
L
lang 已提交
530 531 532
            CoordinateSystemManager.register(type, CoordinateSystem);
        },

L
lang 已提交
533 534 535
        /**
         * @param {*} layout
         */
536
        registerLayout: function (layout, isFactory) {
L
tweak  
lang 已提交
537
            // PENDING All functions ?
538 539 540 541 542 543 544 545 546
            if (isFactory) {
                if (zrUtil.indexOf(layoutClasses, layout) < 0) {
                    layoutClasses.push(layout);
                }
            }
            else {
                if (zrUtil.indexOf(layoutFuncs, layout) < 0) {
                    layoutFuncs.push(layout);
                }
L
lang 已提交
547 548 549
            }
        },

L
lang 已提交
550 551 552 553
        /**
         * @param {string} stage
         * @param {Function} visualCodingFunc
         */
554 555 556 557 558 559
        registerVisualCoding: function (stage, visualCodingFunc) {
            if (zrUtil.indexOf(VISUAL_CODING_STAGES, stage) < 0) {
                throw new Error('stage should be one of ' + VISUAL_CODING_STAGES);
            }
            var funcs = visualCodingFuncs[stage] || (visualCodingFuncs[stage] = []);
            funcs.push(visualCodingFunc);
L
Update  
lang 已提交
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
        },

        /**
         * @param {Object} opts
         */
        extendChartView: function (opts) {
            return ChartView.extend(opts);
        },

        /**
         * @param {Object} opts
         */
        extendComponentModel: function (opts) {
            return ComponentModel.extend(opts);
        },

P
pah100 已提交
576 577 578 579 580 581 582
        /**
         * @param {Object} opts
         */
        extendSeriesModel: function (opts) {
            return SeriesModel.extend(opts);
        },

L
Update  
lang 已提交
583 584 585 586 587
        /**
         * @param {Object} opts
         */
        extendComponentView: function (opts) {
            return ComponentView.extend(opts);
L
lang 已提交
588
        }
L
lang 已提交
589 590
    };

591
    echarts.registerVisualCoding('echarts', require('./visual/defaultColor'));
L
lang 已提交
592

L
lang 已提交
593 594
    return echarts;
});