radar.js 18.6 KB
Newer Older
K
kener 已提交
1 2 3 4 5 6 7 8
/**
 * echarts图表类:雷达图
 *
 * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
 * @author Neil (杨骥, yangji01@baidu.com)
 *
 */

K
kener 已提交
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 define(function(require) {
    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} series 数据
     * @param {Object} component 组件
     */
    function Radar(messageCenter, zr, option, component) {
        // 基类装饰
        var ComponentBase = require('../component/base');
        ComponentBase.call(this, zr);
        // 可计算特性装饰
        var CalculableBase = require('./calculableBase');
        CalculableBase.call(this, zr, option);

        var ecConfig = require('../config');
        var ecData = require('../util/ecData');

        var zrColor = require('zrender/tool/color');

        var self = this;
        self.type = ecConfig.CHART_TYPE_RADAR;

        var series;                 // 共享数据源,不要修改跟自己无关的项
        var serie;

        var _zlevelBase = self.getZlevelBase();

        var _queryTarget;

        var _dropBoxList;
K
kener 已提交
41 42 43 44 45 46

        var _symbol = [
              'circle', 'rectangle', 'triangle', 'diamond',
              'emptyCircle', 'emptyRectangle', 'emptyTriangle', 'emptyDiamond'
            ];
        var _radarDataCounter;
K
kener 已提交
47
        
K
kener 已提交
48
        /**
K
kener 已提交
49
         * 绘制图形
K
kener 已提交
50
         */
K
kener 已提交
51 52 53
        function _buildShape() {  
            self.selectedMap = {};
            _dropBoxList = [];
K
kener 已提交
54
            _radarDataCounter = 0;
K
kener 已提交
55 56 57 58 59 60 61 62 63
            for (var i = 0, l = series.length; i < l ; i ++) {
                if (series[i].type == ecConfig.CHART_TYPE_RADAR) {
                    serie = self.reformOption(series[i]);
                    _queryTarget = [serie, option];

                    // 添加可拖拽提示框,多系列共用一个极坐标,第一个优先
                    if (self.deepQuery(_queryTarget, 'calculable')) {
                        _addDropBox(i);
                    }
K
kener 已提交
64
                    _buildSingleRadar(i);
K
kener 已提交
65 66
                }
            }
K
kener 已提交
67

K
kener 已提交
68 69 70 71 72
            for (var i = 0, l = self.shapeList.length; i < l; i++) {
                self.shapeList[i].id = zr.newShapeId(self.type);
                zr.addShape(self.shapeList[i]);
            }
        }
K
kener 已提交
73

K
kener 已提交
74 75 76 77
        /**
         * 构建数据图形
         * @param {number} 序列的index
         */
K
kener 已提交
78 79
        function _buildSingleRadar(index) {
            var legend = component.legend;
K
kener 已提交
80
            var iconShape;
K
kener 已提交
81
            var data = serie.data;
K
kener 已提交
82 83 84
            var defaultColor;
            var name;
            var pointList;
K
kener 已提交
85 86 87
            var calculable = self.deepQuery(_queryTarget, 'calculable');
           
            for (var i = 0; i < data.length; i ++) {
K
kener 已提交
88 89 90 91 92 93 94
                name = data[i].name || '';
                
                // 图例开关
                self.selectedMap[name] = legend 
                                         ? legend.isSelected(name) 
                                         : true;
                if (!self.selectedMap[name]) {
K
kener 已提交
95
                    continue;
K
kener 已提交
96
                }
K
kener 已提交
97 98 99 100 101
                
                 // 默认颜色策略
                if (legend) {
                    // 有图例则从图例中获取颜色定义
                    defaultColor = legend.getColor(name);
K
kener 已提交
102 103 104 105 106 107 108 109
                    iconShape = legend.getItemShape(name);
                    if (iconShape) {
                        // 回调legend,换一个更形象的icon
                        iconShape.style.brushType = self.deepQuery(
                            [data[i], serie], 'itemStyle.normal.areaStyle'
                        ) ? 'both' : 'stroke';
                        legend.setItemShape(name, iconShape);
                    }
K
kener 已提交
110 111 112 113 114
                }
                else {
                    // 全局颜色定义
                    defaultColor = zr.getColor(i);
                }
K
kener 已提交
115

K
kener 已提交
116 117 118 119
                pointList = _getPointList(serie.polarIndex, data[i]);
                // 添加拐点形状
                _addSymbol(pointList, defaultColor, data[i], index);
                // 添加数据形状
K
kener 已提交
120
                _addDataShape(
K
kener 已提交
121 122
                    pointList, defaultColor, data[i],
                    index, i, calculable
K
kener 已提交
123
                );
K
kener 已提交
124
                _radarDataCounter++;
K
kener 已提交
125 126 127
            }
            
        }
K
kener 已提交
128

K
kener 已提交
129
        /**
K
kener 已提交
130 131 132 133
         * 获取数据的点集
         * @param {number} polarIndex
         * @param {Array<Object>} 处理的数据
         * @return {Array<Array<number>>} 点集
K
kener 已提交
134
         */
K
kener 已提交
135 136 137 138
        function _getPointList(polarIndex, dataArr) {
            var pointList = [];
            var vector;
            var polar = component.polar;
K
kener 已提交
139

K
kener 已提交
140 141 142 143 144
            for (var i = 0, l = dataArr.value.length; i < l; i++) {
                vector = polar.getVector(polarIndex, i, dataArr.value[i]);
                if (vector) {
                    pointList.push(vector);
                } 
K
kener 已提交
145
            }
K
kener 已提交
146
            return pointList;
K
kener 已提交
147
        }
K
kener 已提交
148
        
K
kener 已提交
149
        /**
K
kener 已提交
150 151 152 153
         * 生成折线图上的拐点图形
         */
        function _getSymbol(
            x, y, symbol, symbolSize, normalColor, emphasisColor, lineWidth
K
kener 已提交
154
        ) {
K
kener 已提交
155 156 157
            var itemShape = {
                shape : 'icon',
                zlevel : _zlevelBase + 1,
K
kener 已提交
158
                style : {
K
kener 已提交
159 160 161 162 163 164 165 166 167
                    iconType : symbol.replace('empty', '').toLowerCase(),
                    x : x - symbolSize,
                    y : y - symbolSize,
                    width : symbolSize * 2,
                    height : symbolSize * 2,
                    brushType : 'both',
                    color : symbol.match('empty') ? '#fff' : normalColor,
                    strokeColor : normalColor,
                    lineWidth: lineWidth * 2
K
kener 已提交
168
                },
K
kener 已提交
169
                hoverable: false
K
kener 已提交
170
            };
K
kener 已提交
171 172 173 174 175 176 177 178 179
            
            if (symbol.match('star')) {
                itemShape.style.iconType = 'star';
                itemShape.style.n = 
                    (symbol.replace('empty', '').replace('star','') - 0) || 5;
            }
            
            itemShape._x = x;
            itemShape._y = y;
K
kener 已提交
180

K
kener 已提交
181 182 183
            return itemShape;
        }
        
K
kener 已提交
184
        /**
K
kener 已提交
185
         * 添加拐点
K
kener 已提交
186
         * @param {Array<Array<number>>} pointList 点集
K
kener 已提交
187 188 189 190
         * @param {string} defaultColor 默认填充颜色
         * @param {object} data 数据
         * @param {number} serieIndex
         */
K
kener 已提交
191
        function _addSymbol(pointList, defaultColor, data) {
K
kener 已提交
192 193 194 195 196
            // 多级控制
            var queryTarget = [data, serie];
            var symbol = self.deepQuery(queryTarget,'symbol')
                         || _symbol[_radarDataCounter % _symbol.length]
                         || 'cricle';
K
kener 已提交
197
            
K
kener 已提交
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
            if (symbol != 'none') {
                var symbolSize = self.deepQuery(queryTarget,'symbolSize');
                var nColor = self.deepQuery(
                    queryTarget, 'itemStyle.normal.color'
                );
                var eColor = self.deepQuery(
                    queryTarget, 'itemStyle.emphasis.color'
                );
                var lineWidth = self.deepQuery(
                    queryTarget, 'itemStyle.normal.lineStyle.width'
                );
                
                for (var i = 0, l = pointList.length; i < l; i++) {
                    self.shapeList.push(_getSymbol(
                        pointList[i][0],    // x
                        pointList[i][1],    // y
                        symbol,
                        symbolSize,
                        nColor || defaultColor,
                        eColor || nColor || defaultColor,
                        lineWidth
                    ));
                }
            }
K
kener 已提交
222
        }
K
kener 已提交
223
        
K
kener 已提交
224 225
        /**
         * 添加数据图形
K
kener 已提交
226 227 228 229
         * @param {Array<Array<number>>} pointList 点集
         * @param {string} defaultColor 默认填充颜色
         * @param {object} data 数据
         * @param {number} serieIndex
K
kener 已提交
230 231
         * @param {number} dataIndex
         * @param {boolean} calcalable
K
kener 已提交
232
         */ 
K
kener 已提交
233
        function _addDataShape(
K
kener 已提交
234 235
            pointList, defaultColor, data,
            seriesIndex, dataIndex, calculable
K
kener 已提交
236
        ) {
K
kener 已提交
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
            // 多级控制
            var queryTarget = [data, serie];
            var nColor = self.deepQuery(
                queryTarget, 'itemStyle.normal.color'
            );
            var nLineWidth = self.deepQuery(
                queryTarget, 'itemStyle.normal.lineStyle.width'
            );
            var nLineType = self.deepQuery(
                queryTarget, 'itemStyle.normal.lineStyle.type'
            );
            var nAreaColor = self.deepQuery(
                queryTarget, 'itemStyle.normal.areaStyle.color'
            );
            var nIsAreaFill = self.deepQuery(
                queryTarget, 'itemStyle.normal.areaStyle'
            );
            var shape = {
                shape : 'polygon',
                zlevel : _zlevelBase,
                style : {
                    pointList   : pointList,
                    brushType   : nIsAreaFill ? 'both' : 'stroke',
K
kener 已提交
260 261 262
                    color       : nAreaColor 
                                  || nColor 
                                  || zrColor.alpha(defaultColor,0.5),
K
kener 已提交
263 264
                    strokeColor : nColor || defaultColor,
                    lineWidth   : nLineWidth,
K
kener 已提交
265
                    lineType    : nLineType
K
kener 已提交
266 267 268 269 270 271 272 273 274 275
                },
                highlightStyle : {
                    brushType   : self.deepQuery(
                                      queryTarget,
                                      'itemStyle.emphasis.areaStyle'
                                  ) || nIsAreaFill 
                                  ? 'both' : 'stroke',
                    color       : self.deepQuery(
                                      queryTarget,
                                      'itemStyle.emphasis.areaStyle.color'
K
kener 已提交
276 277 278 279
                                  ) 
                                  || nAreaColor 
                                  || nColor 
                                  || zrColor.alpha(defaultColor,0.5),
K
kener 已提交
280 281 282 283 284 285 286
                    strokeColor : self.deepQuery(
                                      queryTarget, 'itemStyle.emphasis.color'
                                  ) || nColor || defaultColor,
                    lineWidth   : self.deepQuery(
                                      queryTarget,
                                      'itemStyle.emphasis.lineStyle.width'
                                  ) || nLineWidth,
K
kener 已提交
287 288 289 290
                    lineType    : self.deepQuery(
                                      queryTarget,
                                      'itemStyle.emphasis.lineStyle.type'
                                  ) || nLineType
K
kener 已提交
291 292
                }
            };
K
kener 已提交
293
            ecData.pack(
K
kener 已提交
294 295 296 297 298 299 300 301
                shape,
                series[seriesIndex],    // 系列
                seriesIndex,            // 系列索引
                data,                   // 数据
                dataIndex,              // 数据索引
                data.name,              // 数据名称
                // 附加指标信息 
                component.polar.getIndicator(series[seriesIndex].polarIndex)
K
kener 已提交
302 303
            );
            if (calculable) {
K
kener 已提交
304
                shape.draggable = true;
K
kener 已提交
305
                self.setCalculable(shape);
K
kener 已提交
306
            }
K
kener 已提交
307 308
            self.shapeList.push(shape);
        }
K
kener 已提交
309

K
kener 已提交
310 311 312 313 314 315 316 317 318 319
        /**
         * 增加外围接受框
         * @param {number} serie的序列
         */
        function _addDropBox(index) {
            var polarIndex = self.deepQuery(
                _queryTarget, 'polarIndex'
            );
            if (!_dropBoxList[polarIndex]) {
                var shape = component.polar.getDropBox(polarIndex);
K
kener 已提交
320
                shape.zlevel = _zlevelBase;
K
kener 已提交
321 322 323 324
                self.setCalculable(shape);
                ecData.pack(shape, series, index, undefined, -1);
                self.shapeList.push(shape);
                _dropBoxList[polarIndex] = true;
K
kener 已提交
325
            }
K
kener 已提交
326
        }
K
kener 已提交
327 328


K
kener 已提交
329 330 331 332 333 334 335
        /**
         * 数据项被拖拽出去,重载基类方法
         */
        function ondragend(param, status) {
            if (!self.isDragend || !param.target) {
                // 没有在当前实例上发生拖拽行为则直接返回
                return;
K
kener 已提交
336 337
            }

K
kener 已提交
338
            var target = param.target;      // 被拖拽图形元素
K
kener 已提交
339

K
kener 已提交
340 341
            var seriesIndex = ecData.get(target, 'seriesIndex');
            var dataIndex = ecData.get(target, 'dataIndex');
K
kener 已提交
342

K
kener 已提交
343 344 345 346
            // 被拖拽的图形是饼图sector,删除被拖拽走的数据
            component.legend && component.legend.del(
                series[seriesIndex].data[dataIndex].name
            );
K
kener 已提交
347

K
kener 已提交
348
            series[seriesIndex].data.splice(dataIndex, 1);
K
kener 已提交
349

K
kener 已提交
350 351 352
            // 别status = {}赋值啊!!
            status.dragOut = true;
            status.needRefresh = true;
K
kener 已提交
353

K
kener 已提交
354 355
            // 处理完拖拽事件后复位
            self.isDragend = false;
K
kener 已提交
356

K
kener 已提交
357 358
            return;
        }
K
kener 已提交
359

K
kener 已提交
360 361 362 363 364 365
         /**
         * 数据项被拖拽进来, 重载基类方法
         */
        function ondrop(param, status) {
            if (!self.isDrop || !param.target) {
                // 没有在当前实例上发生拖拽行为则直接返回
K
kener 已提交
366 367 368
                return;
            }

K
kener 已提交
369 370
            var target = param.target;      // 拖拽安放目标
            var dragged = param.dragged;    // 当前被拖拽的图形对象
K
kener 已提交
371

K
kener 已提交
372 373
            var seriesIndex = ecData.get(target, 'seriesIndex');
            var dataIndex = ecData.get(target, 'dataIndex');
K
kener 已提交
374

K
kener 已提交
375 376 377
            var data;
            var legend = component.legend;
            var value;
K
kener 已提交
378

K
kener 已提交
379 380 381 382 383
            if (dataIndex == -1) {
                data = {
                    value : ecData.get(dragged, 'value'),
                    name : ecData.get(dragged, 'name')
                };
K
kener 已提交
384

K
kener 已提交
385
                series[seriesIndex].data.push(data);
K
kener 已提交
386

K
kener 已提交
387 388 389 390 391 392
                legend && legend.add(
                    data.name,
                    dragged.style.color || dragged.style.strokeColor
                );
            }
            else {
K
kener 已提交
393 394
                // 数据被拖拽到某个数据项上,数据修改
                var accMath = require('../util/accMath');
K
kener 已提交
395 396 397 398 399 400
                data = series[seriesIndex].data[dataIndex];
                legend && legend.del(data.name);
                data.name += option.nameConnector
                             + ecData.get(dragged, 'name');
                value = ecData.get(dragged, 'value');
                for (var i = 0 ; i < value.length; i ++) {
K
kener 已提交
401
                    data.value[i] = accMath.accAdd(data.value[i], value[i]);
K
kener 已提交
402
                }
K
kener 已提交
403 404 405 406 407 408
                
                legend && legend.add(
                    data.name,
                    dragged.style.color || dragged.style.strokeColor
                );
            }
K
kener 已提交
409

K
kener 已提交
410 411
            // 别status = {}赋值啊!!
            status.dragIn = status.dragIn || true;
K
kener 已提交
412

K
kener 已提交
413 414
            // 处理完拖拽事件后复位
            self.isDrop = false;
K
kener 已提交
415

K
kener 已提交
416 417 418 419 420 421 422 423 424 425 426 427 428
            return;
        }

        /**
         * 构造函数默认执行的初始化方法,也用于创建实例后动态修改
         * @param {Object} newZr
         * @param {Object} newSeries
         * @param {Object} newComponent
         */
        function init(newOption, newComponent) {
            component = newComponent;
            refresh(newOption);
        }
K
kener 已提交
429

K
kener 已提交
430 431 432 433 434 435 436
        /**
         * 刷新
         */
        function refresh(newOption) {
            if (newOption) {
                option = newOption;
                series = option.series;
K
kener 已提交
437
            }
K
kener 已提交
438 439 440
            self.clear();
            _buildShape();
        }
K
kener 已提交
441

K
kener 已提交
442 443 444 445 446 447 448 449 450 451 452
        function animation() {
            var duration = self.deepQuery([option], 'animationDuration');
            var easing = self.deepQuery([option], 'animationEasing');
            var dataIndex;
            var seriesIndex;
            var data;
            var serie;
            var polarIndex;
            var polar = component.polar;
            var center;
            var item;
K
kener 已提交
453 454
            var x;
            var y;
K
kener 已提交
455 456 457 458 459 460 461 462 463 464 465 466 467

            for (var i = 0, l = self.shapeList.length; i < l; i++) {
                if (self.shapeList[i].shape == 'polygon') {
                    item = self.shapeList[i];
                    seriesIndex = ecData.get(item, 'seriesIndex');
                    dataIndex = ecData.get(item, 'dataIndex');

                    serie = series[seriesIndex];
                    data = serie.data[dataIndex];

                    polarIndex = self.deepQuery(
                        [data, serie, option], 'polarIndex');
                    center = polar.getCenter(polarIndex);
K
kener 已提交
468 469
                    x = center[0];
                    y = center[1];
K
kener 已提交
470 471 472 473 474 475 476
                    zr.modShape(
                        self.shapeList[i].id, 
                        {
                            scale : [0.1, 0.1, x, y]
                        },
                        true
                    );
K
kener 已提交
477 478 479 480 481 482
                    
                    zr.animate(item.id, '')
                        .when(
                            (self.deepQuery([serie],'animationDuration')
                            || duration)
                            + dataIndex * 100,
K
kener 已提交
483
                            {scale : [1, 1, x, y]}
K
kener 已提交
484
                        )
K
kener 已提交
485 486 487
                        .start(
                            self.deepQuery([serie], 'animationEasing') || easing
                        );
K
kener 已提交
488
                }
K
kener 已提交
489 490 491
                else {
                    x = self.shapeList[i]._x || 0;
                    y = self.shapeList[i]._y || 0;
K
kener 已提交
492 493 494 495 496 497 498
                    zr.modShape(
                        self.shapeList[i].id, 
                        {
                            scale : [0, 0, x, y]
                        },
                        true
                    );
K
kener 已提交
499 500 501
                    zr.animate(self.shapeList[i].id, '')
                        .when(
                            duration,
K
kener 已提交
502
                            {scale : [1, 1, x, y]}
K
kener 已提交
503
                        )
K
kener 已提交
504
                        .start('QuinticOut');
K
kener 已提交
505
                }
K
kener 已提交
506 507 508 509
            }

        }

K
kener 已提交
510 511 512 513 514 515 516
        self.init = init;
        self.refresh = refresh;
        self.animation = animation;
        self.ondrop = ondrop;
        self.ondragend = ondragend;

        init(option, component);
K
kener 已提交
517
    }
K
kener 已提交
518 519 520 521 522 523

    // 图表注册
    require('../chart').define('radar', Radar);
    
    return Radar;
});