bar.js 44.4 KB
Newer Older
K
kener 已提交
1 2 3 4 5 6 7
/**
 * echarts图表类:柱形图
 *
 * @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 RectangleShape = require('zrender/shape/Rectangle');
K
kener 已提交
14 15 16 17
    // 组件依赖
    require('../component/axis');
    require('../component/grid');
    require('../component/dataZoom');
K
kener 已提交
18
    
K
kener 已提交
19 20 21 22 23
    var ecConfig = require('../config');
    var ecData = require('../util/ecData');
    var zrUtil = require('zrender/tool/util');
    var zrColor = require('zrender/tool/color');
    
K
kener 已提交
24 25 26 27 28 29 30
    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} series 数据
     * @param {Object} component 组件
     */
K
kener 已提交
31
    function Bar(ecTheme, messageCenter, zr, option, myChart){
K
kener 已提交
32
        // 基类
K
kener 已提交
33
        ComponentBase.call(this, ecTheme, messageCenter, zr, option, myChart);
K
kener 已提交
34
        // 可计算特性装饰
K
kener 已提交
35
        CalculableBase.call(this);
K
kener 已提交
36
        
K
kener 已提交
37
        this.refresh(option);
K
kener 已提交
38 39 40 41
    }
    
    Bar.prototype = {
        type : ecConfig.CHART_TYPE_BAR,
K
kener 已提交
42 43 44
        /**
         * 绘制图形
         */
K
kener 已提交
45 46 47 48 49 50 51
        _buildShape : function () {
            var series = this.series;
            this.selectedMap = {};
            
            // series默认颜色索引,seriesIndex索引到color
            this._sIndex2colorMap = {};
            
K
kener 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64
            // 水平垂直双向series索引 ,position索引到seriesIndex
            var _position2sIndexMap = {
                top : [],
                bottom : [],
                left : [],
                right : []
            };
            var xAxisIndex;
            var yAxisIndex;
            var xAxis;
            var yAxis;
            for (var i = 0, l = series.length; i < l; i++) {
                if (series[i].type == ecConfig.CHART_TYPE_BAR) {
K
kener 已提交
65
                    series[i] = this.reformOption(series[i]);
K
kener 已提交
66 67
                    xAxisIndex = series[i].xAxisIndex;
                    yAxisIndex = series[i].yAxisIndex;
K
kener 已提交
68 69
                    xAxis = this.component.xAxis.getAxis(xAxisIndex);
                    yAxis = this.component.yAxis.getAxis(yAxisIndex);
K
kener 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82
                    if (xAxis.type == ecConfig.COMPONENT_TYPE_AXIS_CATEGORY
                    ) {
                        _position2sIndexMap[xAxis.getPosition()].push(i);
                    }
                    else if (yAxis.type == ecConfig.COMPONENT_TYPE_AXIS_CATEGORY
                    ) {
                        _position2sIndexMap[yAxis.getPosition()].push(i);
                    }
                }
            }
            // console.log(_position2sIndexMap)
            for (var position in _position2sIndexMap) {
                if (_position2sIndexMap[position].length > 0) {
K
kener 已提交
83
                    this._buildSinglePosition(
K
kener 已提交
84 85 86 87 88
                        position, _position2sIndexMap[position]
                    );
                }
            }

K
kener 已提交
89
            this.addShapeList();
K
kener 已提交
90
        },
K
kener 已提交
91 92 93 94 95 96

        /**
         * 构建单个方向上的柱形图
         *
         * @param {number} seriesIndex 系列索引
         */
K
kener 已提交
97 98
        _buildSinglePosition : function (position, seriesArray) {
            var mapData = this._mapData(seriesArray);
K
kener 已提交
99 100 101 102 103 104 105 106 107 108
            var locationMap = mapData.locationMap;
            var maxDataLength = mapData.maxDataLength;

            if (maxDataLength === 0 || locationMap.length === 0) {
                return;
            }

            switch (position) {
                case 'bottom' :
                case 'top' :
K
kener 已提交
109
                    this._buildHorizontal(maxDataLength, locationMap, seriesArray);
K
kener 已提交
110 111 112
                    break;
                case 'left' :
                case 'right' :
K
kener 已提交
113
                    this._buildVertical(maxDataLength, locationMap, seriesArray);
K
kener 已提交
114 115
                    break;
            }
K
kener 已提交
116
        },
K
kener 已提交
117 118 119 120 121

        /**
         * 数据整形
         * 数组位置映射到系列索引
         */
K
kener 已提交
122 123
        _mapData : function (seriesArray) {
            var series = this.series;
K
kener 已提交
124
            var serie;                              // 临时映射变量
K
kener 已提交
125 126 127
            var dataIndex = 0;                      // 堆积数据所在位置映射
            var stackMap = {};                      // 堆积数据位置映射,堆积组在二维中的第几项
            var magicStackKey = '__kener__stack__'; // 堆积命名,非堆积数据安单一堆积处理
K
kener 已提交
128 129
            var stackKey;                           // 临时映射变量
            var serieName;                          // 临时映射变量
K
kener 已提交
130
            var legend = this.component.legend;
K
kener 已提交
131 132
            var locationMap = [];                   // 需要返回的东西:数组位置映射到系列索引
            var maxDataLength = 0;                  // 需要返回的东西:最大数据长度
K
kener 已提交
133
            var iconShape;
K
kener 已提交
134 135 136 137 138
            // 计算需要显示的个数和分配位置并记在下面这个结构里
            for (var i = 0, l = seriesArray.length; i < l; i++) {
                serie = series[seriesArray[i]];
                serieName = serie.name;
                if (legend){
K
kener 已提交
139 140
                    this.selectedMap[serieName] = legend.isSelected(serieName);
                    this._sIndex2colorMap[seriesArray[i]] =
K
kener 已提交
141
                        legend.getColor(serieName);
K
kener 已提交
142 143 144 145
                    
                    iconShape = legend.getItemShape(serieName);
                    if (iconShape) {
                        // 回调legend,换一个更形象的icon
146 147 148 149 150 151 152 153 154 155 156
                        if (serie.itemStyle.normal.borderWidth > 0) {
                            iconShape.style.x += 1;
                            iconShape.style.y += 1;
                            iconShape.style.width -= 2;
                            iconShape.style.height -= 2;
                            iconShape.style.strokeColor = 
                            iconShape.highlightStyle.strokeColor =
                                serie.itemStyle.normal.borderColor;
                            iconShape.highlightStyle.lineWidth = 3;
                            iconShape.style.brushType = 'both';
                        }
K
kener 已提交
157 158
                        legend.setItemShape(serieName, iconShape);
                    }
K
kener 已提交
159
                } else {
K
kener 已提交
160 161 162
                    this.selectedMap[serieName] = true;
                    this._sIndex2colorMap[seriesArray[i]] =
                        this.zr.getColor(seriesArray[i]);
K
kener 已提交
163 164
                }

K
kener 已提交
165
                if (this.selectedMap[serieName]) {
K
kener 已提交
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 199 200 201 202 203
                    stackKey = serie.stack || (magicStackKey + seriesArray[i]);
                    if (typeof stackMap[stackKey] == 'undefined') {
                        stackMap[stackKey] = dataIndex;
                        locationMap[dataIndex] = [seriesArray[i]];
                        dataIndex++;
                    }
                    else {
                        // 已经分配了位置就推进去就行
                        locationMap[stackMap[stackKey]].push(seriesArray[i]);
                    }
                }
                // 兼职帮算一下最大长度
                maxDataLength = Math.max(maxDataLength, serie.data.length);
            }

            /* 调试输出
            var s = '';
            for (var i = 0, l = maxDataLength; i < l; i++) {
                s = '[';
                for (var j = 0, k = locationMap.length; j < k; j++) {
                    s +='['
                    for (var m = 0, n = locationMap[j].length - 1; m < n; m++) {
                        s += series[locationMap[j][m]].data[i] + ','
                    }
                    s += series[locationMap[j][locationMap[j].length - 1]]
                         .data[i];
                    s += ']'
                }
                s += ']';
                console.log(s);
            }
            console.log(locationMap)
            */

            return {
                locationMap : locationMap,
                maxDataLength : maxDataLength
            };
K
kener 已提交
204
        },
K
kener 已提交
205 206 207 208

        /**
         * 构建类目轴为水平方向的柱形图系列
         */
K
kener 已提交
209 210
        _buildHorizontal : function (maxDataLength, locationMap, seriesArray) {
            var series = this.series;
K
kener 已提交
211 212 213 214
            // 确定类目轴和数值轴,同一方向随便找一个即可
            var seriesIndex = locationMap[0][0];
            var serie = series[seriesIndex];
            var xAxisIndex = serie.xAxisIndex;
K
kener 已提交
215
            var categoryAxis = this.component.xAxis.getAxis(xAxisIndex);
K
kener 已提交
216 217 218
            var yAxisIndex; // 数值轴各异
            var valueAxis;  // 数值轴各异

K
kener 已提交
219
            var size = this._mapSize(categoryAxis, locationMap);
K
kener 已提交
220 221 222 223 224 225
            var gap = size.gap;
            var barGap = size.barGap;
            var barWidthMap = size.barWidthMap;
            var barWidth = size.barWidth;                   // 自适应宽度
            var barMinHeightMap = size.barMinHeightMap;
            var barHeight;
K
kener 已提交
226
            var interval = size.interval;
K
kener 已提交
227

K
kener 已提交
228
            var xMarkMap = {}; // 为标注记录一些参数
K
kener 已提交
229 230
            var x;
            var y;
K
kener 已提交
231
            var lastYP; // 正向堆积处理
K
kener 已提交
232
            var baseYP;
K
kener 已提交
233
            var lastYN; // 负向堆积处理
K
kener 已提交
234 235 236 237 238 239 240 241 242 243 244
            var baseYN;
            var barShape;
            var data;
            var value;
            for (var i = 0, l = maxDataLength; i < l; i++) {
                if (typeof categoryAxis.getNameByIndex(i) == 'undefined') {
                    // 系列数据超出类目轴长度
                    break;
                }
                x = categoryAxis.getCoordByIndex(i) - gap / 2;
                for (var j = 0, k = locationMap.length; j < k; j++) {
K
kener 已提交
245
                    // 堆积数据用第一条valueAxis
K
kener 已提交
246
                    yAxisIndex = series[locationMap[j][0]].yAxisIndex || 0;
K
kener 已提交
247
                    valueAxis = this.component.yAxis.getAxis(yAxisIndex);
K
kener 已提交
248
                    baseYP = lastYP = baseYN = lastYN = valueAxis.getCoord(0);
K
kener 已提交
249 250 251 252 253 254 255 256 257
                    for (var m = 0, n = locationMap[j].length; m < n; m++) {
                        seriesIndex = locationMap[j][m];
                        serie = series[seriesIndex];
                        data = serie.data[i];
                        value = typeof data != 'undefined'
                                ? (typeof data.value != 'undefined'
                                  ? data.value
                                  : data)
                                : '-';
K
kener 已提交
258 259 260 261 262 263 264 265
                        xMarkMap[seriesIndex] = xMarkMap[seriesIndex] 
                                                || {
                                                    min : Number.POSITIVE_INFINITY,
                                                    max : Number.NEGATIVE_INFINITY,
                                                    sum : 0,
                                                    counter : 0,
                                                    average : 0
                                                };
K
kener 已提交
266 267 268 269
                        if (value == '-') {
                            // 空数据在做完后补充拖拽提示框
                            continue;
                        }
270
                        //y = valueAxis.getCoord(value);
K
kener 已提交
271
                        if (value > 0) {
K
kener 已提交
272
                            // 正向堆积
273 274 275 276
                            //barHeight = baseYP - y;
                            barHeight = m > 0 
                                        ? valueAxis.getCoordSize(value)
                                        : (baseYP - valueAxis.getCoord(value));
K
kener 已提交
277
                            // 非堆积数据最小高度有效
K
kener 已提交
278 279 280 281 282 283 284 285 286
                            if (n == 1
                                && barMinHeightMap[seriesIndex] > barHeight
                            ) {
                                barHeight = barMinHeightMap[seriesIndex];
                            }
                            lastYP -= barHeight;
                            y = lastYP;
                        }
                        else if (value < 0){
K
kener 已提交
287
                            // 负向堆积
288 289 290 291
                            //barHeight = y - baseYN;
                            barHeight = m > 0 
                                        ? valueAxis.getCoordSize(value)
                                        : (valueAxis.getCoord(value) - baseYN);
K
kener 已提交
292
                            // 非堆积数据最小高度有效
K
kener 已提交
293 294 295 296 297 298 299 300 301 302
                            if (n == 1
                                && barMinHeightMap[seriesIndex] > barHeight
                            ) {
                                barHeight = barMinHeightMap[seriesIndex];
                            }
                            y = lastYN;
                            lastYN += barHeight;
                        }
                        else {
                            // 0值
303
                            barHeight = 0;//baseYP - y;
K
kener 已提交
304 305 306 307
                            // 最小高度无效
                            lastYP -= barHeight;
                            y = lastYP;
                        }
K
kener 已提交
308 309
                        xMarkMap[seriesIndex][i] = 
                            x + (barWidthMap[seriesIndex] || barWidth) / 2;
310 311 312 313 314 315 316 317 318 319 320 321
                        if (xMarkMap[seriesIndex].min > value) {
                            xMarkMap[seriesIndex].min = value;
                            xMarkMap[seriesIndex].minY = y;
                            xMarkMap[seriesIndex].minX = xMarkMap[seriesIndex][i];
                        }
                        if (xMarkMap[seriesIndex].max < value) {
                            xMarkMap[seriesIndex].max = value;
                            xMarkMap[seriesIndex].maxY = y;
                            xMarkMap[seriesIndex].maxX = xMarkMap[seriesIndex][i];
                        }
                        xMarkMap[seriesIndex].sum += value;
                        xMarkMap[seriesIndex].counter++;
K
kener 已提交
322 323 324 325 326 327 328 329 330 331 332 333
                        
                        if (i % interval == 0) {
                            barShape = this._getBarItem(
                                seriesIndex, i,
                                categoryAxis.getNameByIndex(i),
                                x, y,
                                barWidthMap[seriesIndex] || barWidth,
                                barHeight,
                                'vertical'
                            );
                            this.shapeList.push(new RectangleShape(barShape));
                        }
K
kener 已提交
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
                    }

                    // 补充空数据的拖拽提示框
                    for (var m = 0, n = locationMap[j].length; m < n; m++) {
                        seriesIndex = locationMap[j][m];
                        serie = series[seriesIndex];
                        data = serie.data[i];
                        value = typeof data != 'undefined'
                                ? (typeof data.value != 'undefined'
                                  ? data.value
                                  : data)
                                : '-';
                        if (value != '-') {
                            // 只关心空数据
                            continue;
                        }

K
kener 已提交
351 352
                        if (this.deepQuery(
                                [data, serie, this.option], 'calculable'
K
kener 已提交
353 354
                            )
                        ) {
K
kener 已提交
355
                            lastYP -= this.ecTheme.island.r;
K
kener 已提交
356 357
                            y = lastYP;

K
kener 已提交
358
                            barShape = this._getBarItem(
K
kener 已提交
359 360
                                seriesIndex, i,
                                categoryAxis.getNameByIndex(i),
K
kener 已提交
361 362
                                x + 0.5, y + 0.5,
                                (barWidthMap[seriesIndex] || barWidth) - 1,
K
kener 已提交
363
                                this.ecTheme.island.r - 1,
K
kener 已提交
364
                                'vertical'
K
kener 已提交
365 366 367
                            );
                            barShape.hoverable = false;
                            barShape.draggable = false;
K
kener 已提交
368
                            barShape.style.lineWidth = 1;
K
kener 已提交
369 370 371
                            barShape.style.brushType = 'stroke';
                            barShape.style.strokeColor =
                                    serie.calculableHolderColor
K
kener 已提交
372
                                    || this.ecTheme.calculableHolderColor;
K
kener 已提交
373

K
kener 已提交
374
                            this.shapeList.push(new RectangleShape(barShape));
K
kener 已提交
375 376 377 378 379 380
                        }
                    }

                    x += ((barWidthMap[seriesIndex] || barWidth) + barGap);
                }
            }
381 382 383 384
            
            for (var j = 0, k = locationMap.length; j < k; j++) {
                for (var m = 0, n = locationMap[j].length; m < n; m++) {
                    seriesIndex = locationMap[j][m];
K
kener 已提交
385 386 387 388 389 390
                    if (xMarkMap[seriesIndex].counter > 0) {
                        xMarkMap[seriesIndex].average = 
                            (xMarkMap[seriesIndex].sum / xMarkMap[seriesIndex].counter).toFixed(2) 
                            - 0;
                    }
                    
K
kener 已提交
391
                    y = this.component.yAxis.getAxis(series[seriesIndex].yAxisIndex || 0)
392 393 394
                        .getCoord(xMarkMap[seriesIndex].average);
                        
                    xMarkMap[seriesIndex].averageLine = [
K
kener 已提交
395 396
                        [this.component.grid.getX(), y],
                        [this.component.grid.getXend(), y]
397 398
                    ];
                    xMarkMap[seriesIndex].minLine = [
K
kener 已提交
399 400
                        [this.component.grid.getX(), xMarkMap[seriesIndex].minY],
                        [this.component.grid.getXend(), xMarkMap[seriesIndex].minY]
401 402
                    ];
                    xMarkMap[seriesIndex].maxLine = [
K
kener 已提交
403 404
                        [this.component.grid.getX(), xMarkMap[seriesIndex].maxY],
                        [this.component.grid.getXend(), xMarkMap[seriesIndex].maxY]
405 406 407
                    ];
                }
            }
K
kener 已提交
408 409 410
            
            this._buildMark(seriesArray, xMarkMap, true);
        },
K
kener 已提交
411 412 413 414

        /**
         * 构建类目轴为垂直方向的柱形图系列
         */
K
kener 已提交
415 416
        _buildVertical : function (maxDataLength, locationMap, seriesArray) {
            var series = this.series;
K
kener 已提交
417 418 419 420
            // 确定类目轴和数值轴,同一方向随便找一个即可
            var seriesIndex = locationMap[0][0];
            var serie = series[seriesIndex];
            var yAxisIndex = serie.yAxisIndex;
K
kener 已提交
421
            var categoryAxis = this.component.yAxis.getAxis(yAxisIndex);
K
kener 已提交
422 423 424
            var xAxisIndex; // 数值轴各异
            var valueAxis;  // 数值轴各异

K
kener 已提交
425
            var size = this._mapSize(categoryAxis, locationMap);
K
kener 已提交
426 427 428 429 430 431
            var gap = size.gap;
            var barGap = size.barGap;
            var barWidthMap = size.barWidthMap;
            var barWidth = size.barWidth;                   // 自适应宽度
            var barMinHeightMap = size.barMinHeightMap;
            var barHeight;
K
kener 已提交
432
            var interval = size.interval;
K
kener 已提交
433

K
kener 已提交
434
            var xMarkMap = {}; // 为标注记录一个横向偏移
K
kener 已提交
435 436
            var x;
            var y;
K
kener 已提交
437
            var lastXP; // 正向堆积处理
K
kener 已提交
438
            var baseXP;
K
kener 已提交
439
            var lastXN; // 负向堆积处理
K
kener 已提交
440 441 442 443 444 445 446 447 448 449 450
            var baseXN;
            var barShape;
            var data;
            var value;
            for (var i = 0, l = maxDataLength; i < l; i++) {
                if (typeof categoryAxis.getNameByIndex(i) == 'undefined') {
                    // 系列数据超出类目轴长度
                    break;
                }
                y = categoryAxis.getCoordByIndex(i) + gap / 2;
                for (var j = 0, k = locationMap.length; j < k; j++) {
K
kener 已提交
451
                    // 堆积数据用第一条valueAxis
K
kener 已提交
452
                    xAxisIndex = series[locationMap[j][0]].xAxisIndex || 0;
K
kener 已提交
453
                    valueAxis = this.component.xAxis.getAxis(xAxisIndex);
K
kener 已提交
454
                    baseXP = lastXP = baseXN = lastXN = valueAxis.getCoord(0);
K
kener 已提交
455 456 457 458 459 460 461 462 463
                    for (var m = 0, n = locationMap[j].length; m < n; m++) {
                        seriesIndex = locationMap[j][m];
                        serie = series[seriesIndex];
                        data = serie.data[i];
                        value = typeof data != 'undefined'
                                ? (typeof data.value != 'undefined'
                                  ? data.value
                                  : data)
                                : '-';
K
kener 已提交
464 465 466 467 468 469 470 471
                        xMarkMap[seriesIndex] = xMarkMap[seriesIndex] 
                                                || {
                                                    min : Number.POSITIVE_INFINITY,
                                                    max : Number.NEGATIVE_INFINITY,
                                                    sum : 0,
                                                    counter : 0,
                                                    average : 0
                                                };
K
kener 已提交
472 473 474 475
                        if (value == '-') {
                            // 空数据在做完后补充拖拽提示框
                            continue;
                        }
476
                        //x = valueAxis.getCoord(value);
K
kener 已提交
477
                        if (value > 0) {
K
kener 已提交
478
                            // 正向堆积
479 480 481 482
                            //barHeight = x - baseXP;
                            barHeight = m > 0 
                                        ? valueAxis.getCoordSize(value)
                                        : (valueAxis.getCoord(value) - baseXP);
K
kener 已提交
483
                            // 非堆积数据最小高度有效
K
kener 已提交
484 485 486 487 488 489 490 491 492
                            if (n == 1
                                && barMinHeightMap[seriesIndex] > barHeight
                            ) {
                                barHeight = barMinHeightMap[seriesIndex];
                            }
                            x = lastXP;
                            lastXP += barHeight;
                        }
                        else if (value < 0){
K
kener 已提交
493
                            // 负向堆积
494 495 496 497
                            //barHeight = baseXN - x;
                            barHeight = m > 0 
                                        ? valueAxis.getCoordSize(value)
                                        : (baseXN - valueAxis.getCoord(value));
K
kener 已提交
498
                            // 非堆积数据最小高度有效
K
kener 已提交
499 500 501 502 503 504 505 506 507 508
                            if (n == 1
                                && barMinHeightMap[seriesIndex] > barHeight
                            ) {
                                barHeight = barMinHeightMap[seriesIndex];
                            }
                            lastXN -= barHeight;
                            x = lastXN;
                        }
                        else {
                            // 0值
509
                            barHeight = 0;//x - baseXP;
K
kener 已提交
510 511 512 513 514
                            // 最小高度无效
                            x = lastXP;
                            lastXP += barHeight;
                        }

K
kener 已提交
515 516
                        xMarkMap[seriesIndex][i] = 
                            y - (barWidthMap[seriesIndex] || barWidth) / 2;
517 518 519 520 521 522 523 524 525 526 527 528
                        if (xMarkMap[seriesIndex].min > value) {
                            xMarkMap[seriesIndex].min = value;
                            xMarkMap[seriesIndex].minX = x + barHeight;
                            xMarkMap[seriesIndex].minY = xMarkMap[seriesIndex][i];
                        }
                        if (xMarkMap[seriesIndex].max < value) {
                            xMarkMap[seriesIndex].max = value;
                            xMarkMap[seriesIndex].maxX = x + barHeight;
                            xMarkMap[seriesIndex].maxY = xMarkMap[seriesIndex][i];
                        }
                        xMarkMap[seriesIndex].sum += value;
                        xMarkMap[seriesIndex].counter++;
K
kener 已提交
529 530 531 532 533 534 535 536 537 538 539 540
                        
                        if (i % interval == 0) {
                            barShape = this._getBarItem(
                                seriesIndex, i,
                                categoryAxis.getNameByIndex(i),
                                x, y - (barWidthMap[seriesIndex] || barWidth),
                                barHeight,
                                barWidthMap[seriesIndex] || barWidth,
                                'horizontal'
                            );
                            this.shapeList.push(new RectangleShape(barShape));
                        }
K
kener 已提交
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
                    }

                    // 补充空数据的拖拽提示框
                    for (var m = 0, n = locationMap[j].length; m < n; m++) {
                        seriesIndex = locationMap[j][m];
                        serie = series[seriesIndex];
                        data = serie.data[i];
                        value = typeof data != 'undefined'
                                ? (typeof data.value != 'undefined'
                                  ? data.value
                                  : data)
                                : '-';
                        if (value != '-') {
                            // 只关心空数据
                            continue;
                        }

K
kener 已提交
558 559
                        if (this.deepQuery(
                                [data, serie, this.option], 'calculable'
K
kener 已提交
560 561 562
                            )
                        ) {
                            x = lastXP;
K
kener 已提交
563
                            lastXP += this.ecTheme.island.r;
K
kener 已提交
564

K
kener 已提交
565
                            barShape = this._getBarItem(
K
kener 已提交
566 567 568
                                seriesIndex,
                                i,
                                categoryAxis.getNameByIndex(i),
K
kener 已提交
569
                                x + 0.5, y + 0.5 - (barWidthMap[seriesIndex] || barWidth),
K
kener 已提交
570
                                this.ecTheme.island.r - 1,
K
kener 已提交
571
                                (barWidthMap[seriesIndex] || barWidth) - 1,
K
kener 已提交
572
                                'horizontal'
K
kener 已提交
573 574 575
                            );
                            barShape.hoverable = false;
                            barShape.draggable = false;
K
kener 已提交
576
                            barShape.style.lineWidth = 1;
K
kener 已提交
577 578 579
                            barShape.style.brushType = 'stroke';
                            barShape.style.strokeColor =
                                    serie.calculableHolderColor
K
kener 已提交
580
                                    || this.ecTheme.calculableHolderColor;
K
kener 已提交
581

K
kener 已提交
582
                            this.shapeList.push(new RectangleShape(barShape));
K
kener 已提交
583 584 585 586 587 588
                        }
                    }

                    y -= ((barWidthMap[seriesIndex] || barWidth) + barGap);
                }
            }
589 590 591 592
            
            for (var j = 0, k = locationMap.length; j < k; j++) {
                for (var m = 0, n = locationMap[j].length; m < n; m++) {
                    seriesIndex = locationMap[j][m];
K
kener 已提交
593 594 595 596 597 598
                    if (xMarkMap[seriesIndex].counter > 0) {
                        xMarkMap[seriesIndex].average = 
                            (xMarkMap[seriesIndex].sum / xMarkMap[seriesIndex].counter).toFixed(2)
                            - 0;
                    }
                    
K
kener 已提交
599
                    x = this.component.xAxis.getAxis(series[seriesIndex].xAxisIndex || 0)
600 601 602
                        .getCoord(xMarkMap[seriesIndex].average);
                        
                    xMarkMap[seriesIndex].averageLine = [
K
kener 已提交
603 604
                        [x, this.component.grid.getYend()],
                        [x, this.component.grid.getY()]
605 606
                    ];
                    xMarkMap[seriesIndex].minLine = [
K
kener 已提交
607 608
                        [xMarkMap[seriesIndex].minX, this.component.grid.getYend()],
                        [xMarkMap[seriesIndex].minX, this.component.grid.getY()]
609 610
                    ];
                    xMarkMap[seriesIndex].maxLine = [
K
kener 已提交
611 612
                        [xMarkMap[seriesIndex].maxX, this.component.grid.getYend()],
                        [xMarkMap[seriesIndex].maxX, this.component.grid.getY()]
613 614 615 616
                    ];
                }
            }
            
K
kener 已提交
617 618
            this._buildMark(seriesArray, xMarkMap, false);
        },
K
kener 已提交
619
        
K
kener 已提交
620 621 622 623 624
        /**
         * 我真是自找麻烦啊,为啥要允许系列级个性化最小宽度和高度啊!!!
         * @param {CategoryAxis} categoryAxis 类目坐标轴,需要知道类目间隔大小
         * @param {Array} locationMap 整形数据的系列索引
         */
K
kener 已提交
625 626
        _mapSize : function (categoryAxis, locationMap, ignoreUserDefined) {
            var series = this.series;
K
kener 已提交
627 628 629 630 631
            var barWidthMap = {};
            var barMinHeightMap = {};
            var sBarWidth;
            var sBarWidthCounter = 0;
            var sBarWidthTotal = 0;
K
kener 已提交
632 633
            var barGap;
            var barCategoryGap;
K
kener 已提交
634
            var hasFound;
K
kener 已提交
635
            var queryTarget;
K
kener 已提交
636
            var interval = 1;
K
kener 已提交
637 638

            for (var j = 0, k = locationMap.length; j < k; j++) {
K
kener 已提交
639
                hasFound = false;   // 同一堆积第一个barWidth生效
K
kener 已提交
640 641
                for (var m = 0, n = locationMap[j].length; m < n; m++) {
                    seriesIndex = locationMap[j][m];
K
kener 已提交
642
                    queryTarget = series[seriesIndex];
K
kener 已提交
643 644
                    if (!ignoreUserDefined) {
                        if (!hasFound) {
K
kener 已提交
645
                            sBarWidth = this.query(
K
kener 已提交
646
                                queryTarget,
K
kener 已提交
647 648 649
                                'barWidth'
                            );
                            if (typeof sBarWidth != 'undefined') {
K
kener 已提交
650
                                // 同一堆积第一个生效barWidth
K
kener 已提交
651 652 653 654
                                barWidthMap[seriesIndex] = sBarWidth;
                                sBarWidthTotal += sBarWidth;
                                sBarWidthCounter++;
                                hasFound = true;
K
kener 已提交
655
                                // 复位前面同一堆积但没被定义的
K
kener 已提交
656 657 658 659
                                for (var ii = 0, ll = m; ii < ll; ii++) {
                                    var pSeriesIndex = locationMap[j][ii];
                                    barWidthMap[pSeriesIndex] = sBarWidth;
                                }
K
kener 已提交
660 661 662 663 664 665
                            }
                        } else {
                            barWidthMap[seriesIndex] = sBarWidth;   // 用找到的一个
                        }
                    }

K
kener 已提交
666
                    barMinHeightMap[seriesIndex] = this.query(
K
kener 已提交
667
                        queryTarget,
K
kener 已提交
668 669
                        'barMinHeight'
                    );
K
kener 已提交
670 671
                    barGap = typeof barGap != 'undefined' 
                             ? barGap
K
kener 已提交
672
                             : this.query(
K
kener 已提交
673 674 675 676 677
                                   queryTarget,
                                   'barGap'
                               );
                    barCategoryGap = typeof barCategoryGap != 'undefined' 
                                     ? barCategoryGap
K
kener 已提交
678
                                     : this.query(
K
kener 已提交
679 680 681
                                           queryTarget,
                                           'barCategoryGap'
                                       );
K
kener 已提交
682 683 684 685 686 687 688
                }
            }

            var gap;
            var barWidth;
            if (locationMap.length != sBarWidthCounter) {
                // 至少存在一个自适应宽度的柱形图
K
kener 已提交
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
                if (!ignoreUserDefined) {
                    gap = typeof barCategoryGap == 'string' 
                          && barCategoryGap.match(/%$/)
                              // 百分比
                              ? Math.floor(
                                  categoryAxis.getGap() 
                                  * (100 - parseFloat(barCategoryGap)) 
                                  / 100
                                )
                              // 数值
                              : (categoryAxis.getGap() - barCategoryGap);
                    if (typeof barGap == 'string' && barGap.match(/%$/)) {
                        barGap = parseFloat(barGap) / 100;
                        barWidth = Math.floor(
                            (gap - sBarWidthTotal)
                            / ((locationMap.length - 1) * barGap 
                               + locationMap.length - sBarWidthCounter)
                        );
                        barGap = Math.floor(barWidth * barGap);
                    }
                    else {
                        barGap = parseFloat(barGap);
                        barWidth = Math.floor(
                            (gap - sBarWidthTotal 
                                 - barGap * (locationMap.length - 1)
                            )
                            / (locationMap.length - sBarWidthCounter)
                        );
                    }
K
kener 已提交
718
                    // 无法满足用户定义的宽度设计,忽略用户宽度,打回重做
K
kener 已提交
719
                    if (barWidth <= 0) {
K
kener 已提交
720
                        return this._mapSize(categoryAxis, locationMap, true);
K
kener 已提交
721 722 723 724 725 726 727 728
                    }
                }
                else {
                    // 忽略用户定义的宽度设定
                    gap = categoryAxis.getGap();
                    barGap = 0;
                    barWidth = Math.floor(gap / locationMap.length);
                    // 已经忽略用户定义的宽度设定依然还无法满足显示,只能硬来了;
K
kener 已提交
729
                    if (barWidth <= 0) {
K
kener 已提交
730
                        interval = Math.floor(locationMap.length / gap);
K
kener 已提交
731 732
                        barWidth = 1;
                    }
K
kener 已提交
733 734 735
                }
            }
            else {
K
kener 已提交
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
                // 全是自定义宽度,barGap无效,系列间隔决定barGap
                gap = sBarWidthCounter > 1
                      ? (typeof barCategoryGap == 'string' 
                         && barCategoryGap.match(/%$/)
                        )
                          // 百分比
                          ? Math.floor(
                              categoryAxis.getGap() 
                              * (100 - parseFloat(barCategoryGap)) 
                              / 100
                            )
                          // 数值
                          : (categoryAxis.getGap() - barCategoryGap)
                      // 只有一个
                      : sBarWidthTotal;
K
kener 已提交
751
                barWidth = 0;
K
kener 已提交
752 753 754 755 756 757
                barGap = sBarWidthCounter > 1 
                         ? Math.floor(
                               (gap - sBarWidthTotal) / (sBarWidthCounter - 1)
                           )
                         : 0;
                if (barGap < 0) {
K
kener 已提交
758
                    // 无法满足用户定义的宽度设计,忽略用户宽度,打回重做
K
kener 已提交
759
                    return this._mapSize(categoryAxis, locationMap, true);
K
kener 已提交
760 761 762 763 764 765 766 767
                }
            }

            return {
                barWidthMap : barWidthMap,
                barMinHeightMap : barMinHeightMap ,
                gap : gap,
                barWidth : barWidth,
K
kener 已提交
768 769
                barGap : barGap,
                interval : interval
K
kener 已提交
770
            };
K
kener 已提交
771
        },
K
kener 已提交
772 773 774 775

        /**
         * 生成最终图形数据
         */
K
kener 已提交
776 777
        _getBarItem : function (seriesIndex, dataIndex, name, x, y, width, height, orient) {
            var series = this.series;
K
kener 已提交
778 779 780 781
            var barShape;
            var serie = series[seriesIndex];
            var data = serie.data[dataIndex];
            // 多级控制
K
kener 已提交
782
            var defaultColor = this._sIndex2colorMap[seriesIndex];
K
kener 已提交
783
            var queryTarget = [data, serie];
K
kener 已提交
784
            var normalColor = this.deepQuery(
K
kener 已提交
785
                queryTarget,
K
kener 已提交
786
                'itemStyle.normal.color'
K
kener 已提交
787
            ) || defaultColor;
K
kener 已提交
788
            var emphasisColor = this.deepQuery(
K
kener 已提交
789
                queryTarget,
K
kener 已提交
790 791
                'itemStyle.emphasis.color'
            );
K
kener 已提交
792
            var normal = this.deepMerge(
K
kener 已提交
793
                queryTarget,
794
                'itemStyle.normal'
K
kener 已提交
795
            );
796
            var normalBorderWidth = normal.borderWidth;
K
kener 已提交
797
            var emphasis = this.deepMerge(
K
kener 已提交
798
                queryTarget,
799
                'itemStyle.emphasis'
K
kener 已提交
800
            );
K
kener 已提交
801
            barShape = {
K
kener 已提交
802
                zlevel : this._zlevelBase,
K
kener 已提交
803 804 805 806 807 808 809
                clickable: true,
                style : {
                    x : x,
                    y : y,
                    width : width,
                    height : height,
                    brushType : 'both',
K
kener 已提交
810
                    color : this.getItemStyleColor(normalColor, seriesIndex, dataIndex, data),
811 812 813
                    radius : normal.borderRadius,
                    lineWidth : normalBorderWidth,
                    strokeColor : normal.borderColor
K
kener 已提交
814 815
                },
                highlightStyle : {
K
kener 已提交
816
                    color : this.getItemStyleColor(emphasisColor, seriesIndex, dataIndex, data),
817 818 819
                    radius : emphasis.borderRadius,
                    lineWidth : emphasis.borderWidth,
                    strokeColor : emphasis.borderColor
K
kener 已提交
820 821
                },
                _orient : orient
K
kener 已提交
822
            };
K
kener 已提交
823 824 825 826 827
            barShape.highlightStyle.color = barShape.highlightStyle.color
                            || (typeof barShape.style.color == 'string'
                                ? zrColor.lift(barShape.style.color, -0.3)
                                : barShape.style.color
                               );
K
kener 已提交
828
            // 考虑线宽的显示优化
K
kener 已提交
829 830
            if (normalBorderWidth > 0
                && barShape.style.height > normalBorderWidth
831
                && barShape.style.width > normalBorderWidth
K
kener 已提交
832
            ) {
833 834 835 836
                barShape.style.y += normalBorderWidth / 2;
                barShape.style.height -= normalBorderWidth;
                barShape.style.x += normalBorderWidth / 2;
                barShape.style.width -= normalBorderWidth;
K
kener 已提交
837 838
            }
            else {
K
kener 已提交
839
                // 太小了或者线宽小于0,废了边线
K
kener 已提交
840 841 842
                barShape.style.brushType = 'fill';
            }
            
K
kener 已提交
843
            barShape.highlightStyle.textColor = barShape.highlightStyle.color;
K
kener 已提交
844
            
K
kener 已提交
845
            barShape = this.addLabel(barShape, serie, data, name, orient);
K
kener 已提交
846

K
kener 已提交
847 848
            if (this.deepQuery([data, serie, this.option],'calculable')) {
                this.setCalculable(barShape);
K
kener 已提交
849 850 851 852 853 854
                barShape.draggable = true;
            }

            ecData.pack(
                barShape,
                series[seriesIndex], seriesIndex,
K
kener 已提交
855
                series[seriesIndex].data[dataIndex], dataIndex,
K
kener 已提交
856 857 858 859
                name
            );

            return barShape;
K
kener 已提交
860
        },
K
kener 已提交
861

K
kener 已提交
862
        // 添加标注
K
kener 已提交
863 864
        _buildMark : function (seriesArray, xMarkMap ,isHorizontal) {
            var series = this.series;
K
kener 已提交
865
            for (var i = 0, l = seriesArray.length; i < l; i++) {
K
kener 已提交
866
                this.buildMark(
K
kener 已提交
867 868
                    series[seriesArray[i]],
                    seriesArray[i],
K
kener 已提交
869
                    this.component,
K
kener 已提交
870 871 872
                    {
                        isHorizontal : isHorizontal,
                        xMarkMap : xMarkMap
K
kener 已提交
873
                    }
K
kener 已提交
874
                );
K
kener 已提交
875
            }
K
kener 已提交
876
        },
K
kener 已提交
877
        
K
kener 已提交
878
        // 位置转换
K
kener 已提交
879 880 881
        getMarkCoord : function (serie, seriesIndex, mpData, markCoordParams) {
            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
K
kener 已提交
882 883
            var dataIndex;
            var pos;
884
            if (mpData.type
K
kener 已提交
885
                && (mpData.type == 'max' || mpData.type == 'min' || mpData.type == 'average')
886 887 888 889 890 891 892 893 894 895
            ) {
                // 特殊值内置支持
                pos = [
                    markCoordParams.xMarkMap[seriesIndex][mpData.type + 'X'],
                    markCoordParams.xMarkMap[seriesIndex][mpData.type + 'Y'],
                    markCoordParams.xMarkMap[seriesIndex][mpData.type + 'Line'],
                    markCoordParams.xMarkMap[seriesIndex][mpData.type]
                ];
            }
            else if (markCoordParams.isHorizontal) {
K
kener 已提交
896 897 898 899 900 901 902 903 904 905 906 907
                // 横向
                dataIndex = typeof mpData.xAxis == 'string'
                            && xAxis.getIndexByName
                            ? xAxis.getIndexByName(mpData.xAxis)
                            : (mpData.xAxis || 0);
                pos = [
                    markCoordParams.xMarkMap[seriesIndex][dataIndex],
                    yAxis.getCoord(mpData.yAxis || 0)
                ];
            }
            else {
                // 纵向
K
kener 已提交
908 909 910 911
                dataIndex = typeof mpData.yAxis == 'string'
                            && yAxis.getIndexByName
                            ? yAxis.getIndexByName(mpData.yAxis)
                            : (mpData.yAxis || 0);
K
kener 已提交
912 913 914 915 916 917
                pos = [
                    xAxis.getCoord(mpData.xAxis || 0),
                    markCoordParams.xMarkMap[seriesIndex][dataIndex]
                ];
            }
            return pos;
K
kener 已提交
918
        },
K
kener 已提交
919
        
K
kener 已提交
920 921 922
        /**
         * 刷新
         */
K
kener 已提交
923
        refresh : function (newOption) {
K
kener 已提交
924
            if (newOption) {
K
kener 已提交
925 926
                this.option = newOption;
                this.series = newOption.series;
K
kener 已提交
927
            }
K
kener 已提交
928 929
            
            this.backupShapeList();
K
kener 已提交
930 931
            this._buildShape();
        },
932 933 934 935
        
        /**
         * 动态数据增加动画 
         */
K
kener 已提交
936 937
        addDataAnimation : function (params) {
            var series = this.series;
938 939 940 941 942 943 944 945 946 947 948
            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 dy;
            var serie;
            var seriesIndex;
            var dataIndex;
K
kener 已提交
949 950
            for (var i = this.shapeList.length - 1; i >= 0; i--) {
                seriesIndex = ecData.get(this.shapeList[i], 'seriesIndex');
951 952
                if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
                    // 有数据删除才有移动的动画
K
kener 已提交
953
                    if (this.shapeList[i].type == 'rectangle') {
954
                        // 主动画
K
kener 已提交
955
                        dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
956 957 958 959 960
                        serie = series[seriesIndex];
                        if (aniMap[seriesIndex][2] 
                            && dataIndex == serie.data.length - 1
                        ) {
                            // 队头加入删除末尾
K
kener 已提交
961
                            this.zr.delShape(this.shapeList[i].id);
962 963
                            continue;
                        }
K
kener 已提交
964
                        else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
965
                            // 队尾加入删除头部
K
kener 已提交
966
                            this.zr.delShape(this.shapeList[i].id);
967 968
                            continue;
                        }
K
kener 已提交
969
                        if (this.shapeList[i]._orient == 'horizontal') {
970
                            // 条形图
K
kener 已提交
971
                            dy = this.component.yAxis.getAxis(
972 973 974 975 976 977 978
                                    serie.yAxisIndex || 0
                                 ).getGap();
                            y = aniMap[seriesIndex][2] ? -dy : dy;
                            x = 0;
                        }
                        else {
                            // 柱形图
K
kener 已提交
979
                            dx = this.component.xAxis.getAxis(
980 981 982 983 984
                                    serie.xAxisIndex || 0
                                 ).getGap();
                            x = aniMap[seriesIndex][2] ? dx : -dx;
                            y = 0;
                        }
K
kener 已提交
985
                        this.shapeList[i].position = [0, 0];
K
kener 已提交
986
                        this.zr.animate(this.shapeList[i].id, '')
987 988 989 990 991 992 993 994
                            .when(
                                500,
                                {position : [x, y]}
                            )
                            .start();
                    }
                }
            }
K
kener 已提交
995 996
        }
    }
K
kener 已提交
997 998 999 1000
    
    zrUtil.inherits(Bar, CalculableBase);
    zrUtil.inherits(Bar, ComponentBase);
    
1001 1002 1003
    // 图表注册
    require('../chart').define('bar', Bar);
    
K
kener 已提交
1004 1005
    return Bar;
});