k.js 19.7 KB
Newer Older
K
kener 已提交
1 2 3 4 5 6 7
/**
 * echarts图表类:K线图
 *
 * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, linzhifeng@baidu.com)
 *
 */
K
kener 已提交
8 9 10 11
define(function (require) {
    var ComponentBase = require('../component/base');
    var CalculableBase = require('./calculableBase');
    
K
kener 已提交
12
    // 图形依赖
K
kener 已提交
13
    var CandleShape = require('../util/shape/Candle');
K
kener 已提交
14 15 16 17
    // 组件依赖
    require('../component/axis');
    require('../component/grid');
    require('../component/dataZoom');
K
kener 已提交
18
    
K
kener 已提交
19 20 21 22
    var ecConfig = require('../config');
    var ecData = require('../util/ecData');
    var zrUtil = require('zrender/tool/util');
    
K
kener 已提交
23 24 25 26 27 28 29
    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} series 数据
     * @param {Object} component 组件
     */
K
kener 已提交
30
    function K(ecTheme, messageCenter, zr, option, myChart){
K
kener 已提交
31
        // 基类
K
kener 已提交
32
        ComponentBase.call(this, ecTheme, messageCenter, zr, option, myChart);
K
kener 已提交
33
        // 可计算特性装饰
K
kener 已提交
34
        CalculableBase.call(this);
K
kener 已提交
35

K
kener 已提交
36
        this.refresh(option);
K
kener 已提交
37 38 39 40 41 42 43 44 45 46
    }
    
    K.prototype = {
        type : ecConfig.CHART_TYPE_K,
        /**
         * 绘制图形
         */
        _buildShape : function () {
            var series = this.series;
            this.selectedMap = {};
K
kener 已提交
47 48 49 50 51 52 53 54 55

            // 水平垂直双向series索引 ,position索引到seriesIndex
            var _position2sIndexMap = {
                top : [],
                bottom : []
            };
            var xAxis;
            for (var i = 0, l = series.length; i < l; i++) {
                if (series[i].type == ecConfig.CHART_TYPE_K) {
K
kener 已提交
56 57
                    series[i] = this.reformOption(series[i]);
                    xAxis = this.component.xAxis.getAxis(series[i].xAxisIndex);
K
kener 已提交
58 59 60 61 62 63 64 65 66
                    if (xAxis.type == ecConfig.COMPONENT_TYPE_AXIS_CATEGORY
                    ) {
                        _position2sIndexMap[xAxis.getPosition()].push(i);
                    }
                }
            }
            //console.log(_position2sIndexMap)
            for (var position in _position2sIndexMap) {
                if (_position2sIndexMap[position].length > 0) {
K
kener 已提交
67
                    this._buildSinglePosition(
K
kener 已提交
68 69 70 71 72
                        position, _position2sIndexMap[position]
                    );
                }
            }

73 74
            this.addShapeList();
            /*
K
kener 已提交
75 76
            for (var i = 0, l = this.shapeList.length; i < l; i++) {
                this.zr.addShape(this.shapeList[i]);
K
kener 已提交
77
            }
78
            */
K
kener 已提交
79
        },
K
kener 已提交
80 81 82 83 84 85

        /**
         * 构建单个方向上的K线图
         *
         * @param {number} seriesIndex 系列索引
         */
K
kener 已提交
86 87 88
        _buildSinglePosition : function (position, seriesArray) {
            var series = this.series;
            var mapData = this._mapData(seriesArray);
K
kener 已提交
89 90 91 92 93 94
            var locationMap = mapData.locationMap;
            var maxDataLength = mapData.maxDataLength;

            if (maxDataLength === 0 || locationMap.length === 0) {
                return;
            }
K
kener 已提交
95
            this._buildHorizontal(seriesArray, maxDataLength, locationMap);
K
kener 已提交
96 97

            for (var i = 0, l = seriesArray.length; i < l; i++) {
K
kener 已提交
98
                this.buildMark(
K
kener 已提交
99 100
                    series[seriesArray[i]],
                    seriesArray[i],
K
kener 已提交
101
                    this.component
K
kener 已提交
102 103
                );
            }
K
kener 已提交
104
        },
K
kener 已提交
105 106 107 108 109

        /**
         * 数据整形
         * 数组位置映射到系列索引
         */
K
kener 已提交
110 111
        _mapData : function (seriesArray) {
            var series = this.series;
K
kener 已提交
112 113
            var serie;                              // 临时映射变量
            var serieName;                          // 临时映射变量
K
kener 已提交
114
            var legend = this.component.legend;
K
kener 已提交
115 116 117 118 119 120 121
            var locationMap = [];                   // 需要返回的东西:数组位置映射到系列索引
            var maxDataLength = 0;                  // 需要返回的东西:最大数据长度
            // 计算需要显示的个数和分配位置并记在下面这个结构里
            for (var i = 0, l = seriesArray.length; i < l; i++) {
                serie = series[seriesArray[i]];
                serieName = serie.name;
                if (legend){
K
kener 已提交
122
                    this.selectedMap[serieName] = legend.isSelected(serieName);
K
kener 已提交
123
                } else {
K
kener 已提交
124
                    this.selectedMap[serieName] = true;
K
kener 已提交
125 126
                }

K
kener 已提交
127
                if (this.selectedMap[serieName]) {
K
kener 已提交
128 129 130 131 132 133 134 135 136
                    locationMap.push(seriesArray[i]);
                }
                // 兼职帮算一下最大长度
                maxDataLength = Math.max(maxDataLength, serie.data.length);
            }
            return {
                locationMap : locationMap,
                maxDataLength : maxDataLength
            };
K
kener 已提交
137
        },
K
kener 已提交
138 139 140 141

        /**
         * 构建类目轴为水平方向的K线图系列
         */
K
kener 已提交
142
        _buildHorizontal : function (seriesArray, maxDataLength, locationMap) {
K
kener 已提交
143
            var series = this.series;
K
kener 已提交
144 145 146 147 148 149 150 151 152 153 154 155
            // 确定类目轴和数值轴,同一方向随便找一个即可
            var seriesIndex;
            var serie;
            var xAxisIndex;
            var categoryAxis;
            var yAxisIndex; // 数值轴各异
            var valueAxis;  // 数值轴各异

            var pointList = {};
            var candleWidth;
            var data;
            var value;
K
kener 已提交
156
            var barMaxWidth;
K
kener 已提交
157 158 159 160 161
            for (var j = 0, k = locationMap.length; j < k; j++) {
                seriesIndex = locationMap[j];
                serie = series[seriesIndex];
                
                xAxisIndex = serie.xAxisIndex || 0;
K
kener 已提交
162
                categoryAxis = this.component.xAxis.getAxis(xAxisIndex);
K
kener 已提交
163 164 165 166 167 168
                candleWidth = serie.barWidth 
                              || Math.floor(categoryAxis.getGap() / 2);
                barMaxWidth = serie.barMaxWidth;
                if (barMaxWidth && barMaxWidth < candleWidth) {
                    candleWidth = barMaxWidth;
                }
K
kener 已提交
169
                yAxisIndex = serie.yAxisIndex || 0;
K
kener 已提交
170
                valueAxis = this.component.yAxis.getAxis(yAxisIndex);
K
kener 已提交
171 172 173
                
                pointList[seriesIndex] = [];
                for (var i = 0, l = maxDataLength; i < l; i++) {
K
kener 已提交
174
                    if (typeof categoryAxis.getNameByIndex(i) == 'undefined') {
K
kener 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
                        // 系列数据超出类目轴长度
                        break;
                    }
                    
                    data = serie.data[i];
                    value = typeof data != 'undefined'
                            ? (typeof data.value != 'undefined'
                              ? data.value
                              : data)
                            : '-';
                    if (value == '-' || value.length != 4) {
                        // 数据格式不符
                        continue;
                    }
                    pointList[seriesIndex].push([
                        categoryAxis.getCoordByIndex(i),    // 横坐标
                        candleWidth,
                        valueAxis.getCoord(value[0]),       // 纵坐标:开盘
                        valueAxis.getCoord(value[1]),       // 纵坐标:收盘
                        valueAxis.getCoord(value[2]),       // 纵坐标:最低
                        valueAxis.getCoord(value[3]),       // 纵坐标:最高
                        i,                                  // 数据index
                        categoryAxis.getNameByIndex(i)      // 类目名称
                    ]);
                }
            }
            // console.log(pointList)
K
kener 已提交
202
            this._buildKLine(seriesArray, pointList);
K
kener 已提交
203
        },
K
kener 已提交
204 205

        /**
K
kener 已提交
206
         * 生成K线
K
kener 已提交
207
         */
K
kener 已提交
208
        _buildKLine : function (seriesArray, pointList) {
K
kener 已提交
209
            var series = this.series;
K
kener 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
            // normal:
            var nLineWidth;
            var nLineColor;
            var nLineColor0;    // 阴线
            var nColor;
            var nColor0;        // 阴线
            
            // emphasis:
            var eLineWidth;
            var eLineColor;
            var eLineColor0;
            var eColor;
            var eColor0;

            var serie;
            var queryTarget;
            var data;
            var seriesPL;
            var singlePoint;
            var candleType;

K
kener 已提交
231 232 233
            var seriesIndex;
            for (var sIdx = 0, len = seriesArray.length; sIdx < len; sIdx++) {
                seriesIndex = seriesArray[sIdx]
K
kener 已提交
234 235
                serie = series[seriesIndex];
                seriesPL = pointList[seriesIndex];
K
kener 已提交
236 237 238 239 240
                
                if (this._isLarge(seriesPL)) {
                    seriesPL = this._getLargePointList(seriesPL);
                }
                
K
kener 已提交
241 242 243 244
                if (serie.type == ecConfig.CHART_TYPE_K
                    && typeof seriesPL != 'undefined'
                ) {
                    // 多级控制
245
                    queryTarget = serie;
K
kener 已提交
246
                    nLineWidth = this.query(
K
kener 已提交
247 248
                        queryTarget, 'itemStyle.normal.lineStyle.width'
                    );
K
kener 已提交
249
                    nLineColor = this.query(
K
kener 已提交
250 251
                        queryTarget, 'itemStyle.normal.lineStyle.color'
                    );
K
kener 已提交
252
                    nLineColor0 = this.query(
K
kener 已提交
253 254
                        queryTarget, 'itemStyle.normal.lineStyle.color0'
                    );
K
kener 已提交
255
                    nColor = this.query(
K
kener 已提交
256 257
                        queryTarget, 'itemStyle.normal.color'
                    );
K
kener 已提交
258
                    nColor0 = this.query(
K
kener 已提交
259 260 261
                        queryTarget, 'itemStyle.normal.color0'
                    );
                    
K
kener 已提交
262
                    eLineWidth = this.query(
K
kener 已提交
263 264
                        queryTarget, 'itemStyle.emphasis.lineStyle.width'
                    );
K
kener 已提交
265
                    eLineColor = this.query(
K
kener 已提交
266 267
                        queryTarget, 'itemStyle.emphasis.lineStyle.color'
                    );
K
kener 已提交
268
                    eLineColor0 = this.query(
K
kener 已提交
269 270
                        queryTarget, 'itemStyle.emphasis.lineStyle.color0'
                    );
K
kener 已提交
271
                    eColor = this.query(
K
kener 已提交
272 273
                        queryTarget, 'itemStyle.emphasis.color'
                    );
K
kener 已提交
274
                    eColor0 = this.query(
K
kener 已提交
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
                        queryTarget, 'itemStyle.emphasis.color0'
                    );

                    /*
                     * pointlist=[
                     *      0  x,
                     *      1  width, 
                     *      2  y0,
                     *      3  y1,
                     *      4  y2,
                     *      5  y3,
                     *      6  dataIndex,
                     *      7  categoryName
                     * ]
                     */
                    for (var i = 0, l = seriesPL.length; i < l; i++) {
                        singlePoint = seriesPL[i];
                        data = serie.data[singlePoint[6]];
293
                        queryTarget = data;
K
kener 已提交
294
                        candleType = singlePoint[3] < singlePoint[2];
K
kener 已提交
295
                        this.shapeList.push(this._getCandle(
K
kener 已提交
296 297 298 299 300 301 302 303 304 305 306 307 308
                            seriesIndex,    // seriesIndex
                            singlePoint[6], // dataIndex
                            singlePoint[7], // name
                            
                            singlePoint[0], // x
                            singlePoint[1], // width
                            singlePoint[2], // y开盘
                            singlePoint[3], // y收盘
                            singlePoint[4], // y最低
                            singlePoint[5], // y最高
                            
                            // 填充颜色
                            candleType
K
kener 已提交
309
                            ? (this.query(          // 阳
K
kener 已提交
310 311
                                   queryTarget, 'itemStyle.normal.color'
                               ) || nColor)
K
kener 已提交
312
                            : (this.query(          // 阴
K
kener 已提交
313 314 315 316
                                   queryTarget, 'itemStyle.normal.color0'
                               ) || nColor0),
                            
                            // 线宽
K
kener 已提交
317
                            this.query(
K
kener 已提交
318 319 320 321 322
                               queryTarget, 'itemStyle.normal.lineStyle.width'
                            ) || nLineWidth,
                            
                            // 线色
                            candleType
K
kener 已提交
323
                            ? (this.query(          // 阳
K
kener 已提交
324 325 326
                                   queryTarget,
                                   'itemStyle.normal.lineStyle.color'
                               ) || nLineColor)
K
kener 已提交
327
                            : (this.query(          // 阴
K
kener 已提交
328 329 330 331 332 333 334 335
                                   queryTarget,
                                   'itemStyle.normal.lineStyle.color0'
                               ) || nLineColor0),
                            
                            //------------高亮
                            
                            // 填充颜色
                            candleType
K
kener 已提交
336
                            ? (this.query(          // 阳
K
kener 已提交
337 338
                                   queryTarget, 'itemStyle.emphasis.color'
                               ) || eColor || nColor)
K
kener 已提交
339
                            : (this.query(          // 阴
K
kener 已提交
340 341 342 343
                                   queryTarget, 'itemStyle.emphasis.color0'
                               ) || eColor0 || nColor0),
                            
                            // 线宽
K
kener 已提交
344
                            this.query(
K
kener 已提交
345 346 347 348 349
                               queryTarget, 'itemStyle.emphasis.lineStyle.width'
                            ) || eLineWidth || nLineWidth,
                            
                            // 线色
                            candleType
K
kener 已提交
350
                            ? (this.query(          // 阳
K
kener 已提交
351 352 353
                                   queryTarget,
                                   'itemStyle.emphasis.lineStyle.color'
                               ) || eLineColor || nLineColor)
K
kener 已提交
354
                            : (this.query(          // 阴
K
kener 已提交
355 356 357 358 359 360 361
                                   queryTarget,
                                   'itemStyle.emphasis.lineStyle.color0'
                               ) || eLineColor0 || nLineColor0)
                        ));
                    }
                }
            }
K
kener 已提交
362 363
            // console.log(this.shapeList)
        },
K
kener 已提交
364

K
kener 已提交
365
        _isLarge : function(singlePL) {
K
kener 已提交
366
            return singlePL[0][1] < 0.5;
K
kener 已提交
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
        },
        
        /**
         * 大规模pointList优化 
         */
        _getLargePointList : function(singlePL) {
            var total = this.component.grid.getWidth();
            var len = singlePL.length;
            var newList = [];
            for (var i = 0; i < total; i++) {
                newList[i] = singlePL[Math.floor(len / total * i)];
            }
            return newList;
        },
        
K
kener 已提交
382 383 384
        /**
         * 生成K线图上的图形
         */
K
kener 已提交
385
        _getCandle : function (
K
kener 已提交
386 387 388 389 390
            seriesIndex, dataIndex, name, 
            x, width, y0, y1, y2, y3, 
            nColor, nLinewidth, nLineColor, 
            eColor, eLinewidth, eLineColor
        ) {
K
kener 已提交
391
            var series = this.series;
K
kener 已提交
392
            var itemShape = {
K
kener 已提交
393
                zlevel : this._zlevelBase,
K
kener 已提交
394
                clickable: true,
K
kener 已提交
395 396 397 398 399 400 401 402 403 404 405 406 407 408
                style : {
                    x : x,
                    y : [y0, y1, y2, y3],
                    width : width,
                    color : nColor,
                    strokeColor : nLineColor,
                    lineWidth : nLinewidth,
                    brushType : 'both'
                },
                highlightStyle : {
                    color : eColor,
                    strokeColor : eLineColor,
                    lineWidth : eLinewidth
                },
K
kener 已提交
409
                _seriesIndex: seriesIndex
K
kener 已提交
410 411 412 413
            };
            ecData.pack(
                itemShape,
                series[seriesIndex], seriesIndex,
K
kener 已提交
414
                series[seriesIndex].data[dataIndex], dataIndex,
K
kener 已提交
415 416
                name
            );
K
kener 已提交
417 418
            
            itemShape = new CandleShape(itemShape);
K
kener 已提交
419
            return itemShape;
K
kener 已提交
420
        },
K
kener 已提交
421

K
kener 已提交
422
        // 位置转换
K
kener 已提交
423 424 425
        getMarkCoord : function (serie, seriesIndex, mpData) {
            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
K
kener 已提交
426 427 428 429 430 431 432 433 434 435 436 437
            
            return [
                typeof mpData.xAxis != 'string' 
                && xAxis.getCoordByIndex
                ? xAxis.getCoordByIndex(mpData.xAxis || 0)
                : xAxis.getCoord(mpData.xAxis || 0),
                
                typeof mpData.yAxis != 'string' 
                && yAxis.getCoordByIndex
                ? yAxis.getCoordByIndex(mpData.yAxis || 0)
                : yAxis.getCoord(mpData.yAxis || 0)
            ];
K
kener 已提交
438
        },
K
kener 已提交
439
        
K
kener 已提交
440 441 442
        /**
         * 刷新
         */
K
kener 已提交
443
        refresh : function (newOption) {
K
kener 已提交
444
            if (newOption) {
K
kener 已提交
445 446
                this.option = newOption;
                this.series = newOption.series;
K
kener 已提交
447
            }
K
kener 已提交
448
            
449
            this.backupShapeList();
K
kener 已提交
450 451
            this._buildShape();
        },
K
kener 已提交
452

K
kener 已提交
453 454 455
        /**
         * 动画设定
         */
K
kener 已提交
456 457
        addDataAnimation : function (params) {
            var series = this.series;
K
kener 已提交
458 459 460 461 462 463 464 465 466 467
            var aniMap = {}; // seriesIndex索引参数
            for (var i = 0, l = params.length; i < l; i++) {
                aniMap[params[i][0]] = params[i];
            }
            var x;
            var dx;
            var y;
            var serie;
            var seriesIndex;
            var dataIndex;
K
kener 已提交
468 469
             for (var i = 0, l = this.shapeList.length; i < l; i++) {
                seriesIndex = this.shapeList[i]._seriesIndex;
K
kener 已提交
470 471
                if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
                    // 有数据删除才有移动的动画
K
kener 已提交
472 473
                    if (this.shapeList[i].type == 'candle') {
                        dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
K
kener 已提交
474 475 476 477 478
                        serie = series[seriesIndex];
                        if (aniMap[seriesIndex][2] 
                            && dataIndex == serie.data.length - 1
                        ) {
                            // 队头加入删除末尾
K
kener 已提交
479
                            this.zr.delShape(this.shapeList[i].id);
K
kener 已提交
480 481
                            continue;
                        }
K
kener 已提交
482
                        else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
K
kener 已提交
483
                            // 队尾加入删除头部
K
kener 已提交
484
                            this.zr.delShape(this.shapeList[i].id);
K
kener 已提交
485 486
                            continue;
                        }
K
kener 已提交
487
                        dx = this.component.xAxis.getAxis(
K
kener 已提交
488 489 490 491
                                serie.xAxisIndex || 0
                             ).getGap();
                        x = aniMap[seriesIndex][2] ? dx : -dx;
                        y = 0;
K
kener 已提交
492
                        this.zr.animate(this.shapeList[i].id, '')
K
kener 已提交
493 494 495 496 497 498 499 500
                            .when(
                                500,
                                {position : [x, y]}
                            )
                            .start();
                    }
                }
            }
K
kener 已提交
501
        }
K
kener 已提交
502
    };
K
kener 已提交
503
    
K
kener 已提交
504 505 506
    zrUtil.inherits(K, CalculableBase);
    zrUtil.inherits(K, ComponentBase);
    
507 508 509
    // 图表注册
    require('../chart').define('k', K);
    
K
kener 已提交
510 511
    return K;
});