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

        var ecData = require('../util/ecData');
K
kener 已提交
34
        
K
kener 已提交
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
        var self = this;
        self.type = ecConfig.CHART_TYPE_K;

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

        var _zlevelBase = self.getZlevelBase();

        function _buildShape() {
            self.selectedMap = {};

            // 水平垂直双向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) {
                    series[i] = self.reformOption(series[i]);
K
jshint~  
kener 已提交
54
                    xAxis = component.xAxis.getAxis(series[i].xAxisIndex);
K
kener 已提交
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
                    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) {
                    _buildSinglePosition(
                        position, _position2sIndexMap[position]
                    );
                }
            }

            for (var i = 0, l = self.shapeList.length; i < l; i++) {
                zr.addShape(self.shapeList[i]);
            }
        }

        /**
         * 构建单个方向上的K线图
         *
         * @param {number} seriesIndex 系列索引
         */
        function _buildSinglePosition(position, seriesArray) {
            var mapData = _mapData(seriesArray);
            var locationMap = mapData.locationMap;
            var maxDataLength = mapData.maxDataLength;

            if (maxDataLength === 0 || locationMap.length === 0) {
                return;
            }
            _buildHorizontal(maxDataLength, locationMap);
K
kener 已提交
89 90 91 92 93 94 95 96

            for (var i = 0, l = seriesArray.length; i < l; i++) {
                self.buildMark(
                    series[seriesArray[i]],
                    seriesArray[i],
                    component
                );
            }
K
kener 已提交
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
        }

        /**
         * 数据整形
         * 数组位置映射到系列索引
         */
        function _mapData(seriesArray) {
            var serie;                              // 临时映射变量
            var serieName;                          // 临时映射变量
            var legend = component.legend;
            var locationMap = [];                   // 需要返回的东西:数组位置映射到系列索引
            var maxDataLength = 0;                  // 需要返回的东西:最大数据长度
            // 计算需要显示的个数和分配位置并记在下面这个结构里
            for (var i = 0, l = seriesArray.length; i < l; i++) {
                serie = series[seriesArray[i]];
                serieName = serie.name;
                if (legend){
                    self.selectedMap[serieName] = legend.isSelected(serieName);
                } else {
                    self.selectedMap[serieName] = true;
                }

                if (self.selectedMap[serieName]) {
                    locationMap.push(seriesArray[i]);
                }
                // 兼职帮算一下最大长度
                maxDataLength = Math.max(maxDataLength, serie.data.length);
            }
            return {
                locationMap : locationMap,
                maxDataLength : maxDataLength
            };
        }

        /**
         * 构建类目轴为水平方向的K线图系列
         */
        function _buildHorizontal(maxDataLength, locationMap) {
            // 确定类目轴和数值轴,同一方向随便找一个即可
            var seriesIndex;
            var serie;
            var xAxisIndex;
            var categoryAxis;
            var yAxisIndex; // 数值轴各异
            var valueAxis;  // 数值轴各异

            var pointList = {};
            var candleWidth;
            var data;
            var value;
K
kener 已提交
147
            var barMaxWidth;
K
kener 已提交
148 149 150 151 152 153
            for (var j = 0, k = locationMap.length; j < k; j++) {
                seriesIndex = locationMap[j];
                serie = series[seriesIndex];
                
                xAxisIndex = serie.xAxisIndex || 0;
                categoryAxis = component.xAxis.getAxis(xAxisIndex);
K
kener 已提交
154 155 156 157 158 159
                candleWidth = serie.barWidth 
                              || Math.floor(categoryAxis.getGap() / 2);
                barMaxWidth = serie.barMaxWidth;
                if (barMaxWidth && barMaxWidth < candleWidth) {
                    candleWidth = barMaxWidth;
                }
K
kener 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
                yAxisIndex = serie.yAxisIndex || 0;
                valueAxis = component.yAxis.getAxis(yAxisIndex);
                
                pointList[seriesIndex] = [];
                for (var i = 0, l = maxDataLength; i < l; i++) {
                    if (typeof categoryAxis.getNameByIndex(i) 
                        == 'undefined'
                    ) {
                        // 系列数据超出类目轴长度
                        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)
            _buildKLine(pointList);
        }

        /**
K
kener 已提交
199
         * 生成K线
K
kener 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
         */
        function _buildKLine(pointList) {
            // 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;

            for (var seriesIndex = 0, len = series.length;
                seriesIndex < len;
                seriesIndex++
            ) {
                serie = series[seriesIndex];
                seriesPL = pointList[seriesIndex];
                if (serie.type == ecConfig.CHART_TYPE_K
                    && typeof seriesPL != 'undefined'
                ) {
                    // 多级控制
233 234
                    queryTarget = serie;
                    nLineWidth = self.query(
K
kener 已提交
235 236
                        queryTarget, 'itemStyle.normal.lineStyle.width'
                    );
237
                    nLineColor = self.query(
K
kener 已提交
238 239
                        queryTarget, 'itemStyle.normal.lineStyle.color'
                    );
240
                    nLineColor0 = self.query(
K
kener 已提交
241 242
                        queryTarget, 'itemStyle.normal.lineStyle.color0'
                    );
243
                    nColor = self.query(
K
kener 已提交
244 245
                        queryTarget, 'itemStyle.normal.color'
                    );
246
                    nColor0 = self.query(
K
kener 已提交
247 248 249
                        queryTarget, 'itemStyle.normal.color0'
                    );
                    
250
                    eLineWidth = self.query(
K
kener 已提交
251 252
                        queryTarget, 'itemStyle.emphasis.lineStyle.width'
                    );
253
                    eLineColor = self.query(
K
kener 已提交
254 255
                        queryTarget, 'itemStyle.emphasis.lineStyle.color'
                    );
256
                    eLineColor0 = self.query(
K
kener 已提交
257 258
                        queryTarget, 'itemStyle.emphasis.lineStyle.color0'
                    );
259
                    eColor = self.query(
K
kener 已提交
260 261
                        queryTarget, 'itemStyle.emphasis.color'
                    );
262
                    eColor0 = self.query(
K
kener 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
                        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]];
281
                        queryTarget = data;
K
kener 已提交
282
                        candleType = singlePoint[3] < singlePoint[2];
K
kener 已提交
283 284 285 286 287 288 289 290 291 292 293 294 295 296
                        self.shapeList.push(_getCandle(
                            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
297
                            ? (self.query(          // 阳
K
kener 已提交
298 299
                                   queryTarget, 'itemStyle.normal.color'
                               ) || nColor)
300
                            : (self.query(          // 阴
K
kener 已提交
301 302 303 304
                                   queryTarget, 'itemStyle.normal.color0'
                               ) || nColor0),
                            
                            // 线宽
305
                            self.query(
K
kener 已提交
306 307 308 309 310
                               queryTarget, 'itemStyle.normal.lineStyle.width'
                            ) || nLineWidth,
                            
                            // 线色
                            candleType
311
                            ? (self.query(          // 阳
K
kener 已提交
312 313 314
                                   queryTarget,
                                   'itemStyle.normal.lineStyle.color'
                               ) || nLineColor)
315
                            : (self.query(          // 阴
K
kener 已提交
316 317 318 319 320 321 322 323
                                   queryTarget,
                                   'itemStyle.normal.lineStyle.color0'
                               ) || nLineColor0),
                            
                            //------------高亮
                            
                            // 填充颜色
                            candleType
324
                            ? (self.query(          // 阳
K
kener 已提交
325 326
                                   queryTarget, 'itemStyle.emphasis.color'
                               ) || eColor || nColor)
327
                            : (self.query(          // 阴
K
kener 已提交
328 329 330 331
                                   queryTarget, 'itemStyle.emphasis.color0'
                               ) || eColor0 || nColor0),
                            
                            // 线宽
332
                            self.query(
K
kener 已提交
333 334 335 336 337
                               queryTarget, 'itemStyle.emphasis.lineStyle.width'
                            ) || eLineWidth || nLineWidth,
                            
                            // 线色
                            candleType
338
                            ? (self.query(          // 阳
K
kener 已提交
339 340 341
                                   queryTarget,
                                   'itemStyle.emphasis.lineStyle.color'
                               ) || eLineColor || nLineColor)
342
                            : (self.query(          // 阴
K
kener 已提交
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
                                   queryTarget,
                                   'itemStyle.emphasis.lineStyle.color0'
                               ) || eLineColor0 || nLineColor0)
                        ));
                    }
                }
            }
            // console.log(self.shapeList)
        }

        /**
         * 生成K线图上的图形
         */
        function _getCandle(
            seriesIndex, dataIndex, name, 
            x, width, y0, y1, y2, y3, 
            nColor, nLinewidth, nLineColor, 
            eColor, eLinewidth, eLineColor
        ) {
            var itemShape = {
                zlevel : _zlevelBase,
K
kener 已提交
364
                clickable: true,
K
kener 已提交
365 366 367 368 369 370 371 372 373 374 375 376 377 378
                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 已提交
379
                _seriesIndex: seriesIndex
K
kener 已提交
380 381 382 383
            };
            ecData.pack(
                itemShape,
                series[seriesIndex], seriesIndex,
K
kener 已提交
384
                series[seriesIndex].data[dataIndex], dataIndex,
K
kener 已提交
385 386
                name
            );
K
kener 已提交
387 388
            
            itemShape = new CandleShape(itemShape);
K
kener 已提交
389 390 391
            return itemShape;
        }

K
kener 已提交
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
        // 位置转换
        function getMarkCoord(serie, seriesIndex, mpData) {
            var xAxis = component.xAxis.getAxis(serie.xAxisIndex);
            var yAxis = component.yAxis.getAxis(serie.yAxisIndex);
            
            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 已提交
408 409
        }
        
K
kener 已提交
410 411 412 413 414 415 416
        /**
         * 构造函数默认执行的初始化方法,也用于创建实例后动态修改
         * @param {Object} newSeries
         * @param {Object} newComponent
         */
        function init(newOption, newComponent) {
            component = newComponent;
K
kener 已提交
417
            refresh(newOption);
K
kener 已提交
418 419 420 421 422
        }

        /**
         * 刷新
         */
K
kener 已提交
423 424 425 426 427
        function refresh(newOption) {
            if (newOption) {
                option = newOption;
                series = option.series;
            }
K
kener 已提交
428 429 430 431
            self.clear();
            _buildShape();
        }

K
kener 已提交
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
        /**
         * 动画设定
         */
        function addDataAnimation(params) {
            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;
             for (var i = 0, l = self.shapeList.length; i < l; i++) {
                seriesIndex = self.shapeList[i]._seriesIndex;
                if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
                    // 有数据删除才有移动的动画
K
kener 已提交
450
                    if (self.shapeList[i].type == 'candle') {
K
kener 已提交
451 452 453 454 455 456 457 458 459
                        dataIndex = ecData.get(self.shapeList[i], 'dataIndex');
                        serie = series[seriesIndex];
                        if (aniMap[seriesIndex][2] 
                            && dataIndex == serie.data.length - 1
                        ) {
                            // 队头加入删除末尾
                            zr.delShape(self.shapeList[i].id);
                            continue;
                        }
K
kener 已提交
460
                        else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
K
kener 已提交
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
                            // 队尾加入删除头部
                            zr.delShape(self.shapeList[i].id);
                            continue;
                        }
                        dx = component.xAxis.getAxis(
                                serie.xAxisIndex || 0
                             ).getGap();
                        x = aniMap[seriesIndex][2] ? dx : -dx;
                        y = 0;
                        zr.animate(self.shapeList[i].id, '')
                            .when(
                                500,
                                {position : [x, y]}
                            )
                            .start();
                    }
                }
            }
        }
        
K
kener 已提交
481 482 483 484
        /**
         * 动画设定
         */
        function animation() {
485 486
            var duration = self.query(option, 'animationDuration');
            var easing = self.query(option, 'animationEasing');
K
kener 已提交
487 488 489 490 491
            var x;
            var y;
            var serie;

            for (var i = 0, l = self.shapeList.length; i < l; i++) {
K
kener 已提交
492
                if (self.shapeList[i].type == 'candle') {
K
kener 已提交
493
                    serie = series[self.shapeList[i]._seriesIndex];
K
kener 已提交
494 495
                    x = self.shapeList[i].style.x;
                    y = self.shapeList[i].style.y[0];
K
kener 已提交
496 497 498 499 500
                    zr.modShape(
                        self.shapeList[i].id,
                        { scale : [1, 0, x, y] },
                        true
                    );
K
kener 已提交
501 502
                    zr.animate(self.shapeList[i].id, '')
                        .when(
503
                            (self.query(serie,'animationDuration')
K
kener 已提交
504
                            || duration),
K
kener 已提交
505
                            {scale : [1, 1, x, y]}
K
kener 已提交
506
                        )
K
kener 已提交
507
                        .start(
508
                            self.query(serie, 'animationEasing') || easing
K
kener 已提交
509
                        );
K
kener 已提交
510 511
                }
            }
K
kener 已提交
512 513
            
            self.animationMark(duration, easing);
K
kener 已提交
514 515
        }

K
kener 已提交
516 517
        // 重载基类方法
        self.getMarkCoord = getMarkCoord;
K
kener 已提交
518
        self.animation = animation;
K
kener 已提交
519
        
K
kener 已提交
520 521
        self.init = init;
        self.refresh = refresh;
K
kener 已提交
522
        self.addDataAnimation = addDataAnimation;
K
kener 已提交
523 524 525 526

        init(option, component);
    }
    
527 528 529
    // 图表注册
    require('../chart').define('k', K);
    
K
kener 已提交
530 531
    return K;
});