bar.js 46.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
define(function (require) {
    var ComponentBase = require('../component/base');
K
kener 已提交
10
    var ChartBase = require('./base');
K
kener 已提交
11
    
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 35
        // 图表基类
        ChartBase.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
        _buildShape : function () {
            var series = this.series;
            this.selectedMap = {};
K
kener 已提交
48
            this.xMarkMap = {};
K
kener 已提交
49 50 51 52
            
            // series默认颜色索引,seriesIndex索引到color
            this._sIndex2colorMap = {};
            
K
kener 已提交
53 54 55 56 57 58 59 60 61 62 63 64 65
            // 水平垂直双向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 已提交
66
                    series[i] = this.reformOption(series[i]);
K
kener 已提交
67 68
                    xAxisIndex = series[i].xAxisIndex;
                    yAxisIndex = series[i].yAxisIndex;
K
kener 已提交
69 70
                    xAxis = this.component.xAxis.getAxis(xAxisIndex);
                    yAxis = this.component.yAxis.getAxis(yAxisIndex);
K
kener 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83
                    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 已提交
84
                    this._buildSinglePosition(
K
kener 已提交
85
                        position, _position2sIndexMap[position], this.xMarkMap
K
kener 已提交
86 87 88 89
                    );
                }
            }

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

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

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

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

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

K
kener 已提交
166
                if (this.selectedMap[serieName]) {
K
kener 已提交
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 204
                    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 已提交
205
        },
K
kener 已提交
206 207 208 209

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

K
kener 已提交
220
            var size = this._mapSize(categoryAxis, locationMap);
K
kener 已提交
221 222 223 224 225 226
            var gap = size.gap;
            var barGap = size.barGap;
            var barWidthMap = size.barWidthMap;
            var barWidth = size.barWidth;                   // 自适应宽度
            var barMinHeightMap = size.barMinHeightMap;
            var barHeight;
K
kener 已提交
227
            var interval = size.interval;
K
kener 已提交
228 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
                        
K
kener 已提交
323
                        if (i % interval === 0) {
K
kener 已提交
324 325 326 327 328 329 330 331 332 333
                            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
                    ];
K
kener 已提交
406 407 408
                    
                    xMarkMap[seriesIndex].isHorizontal = true;
                    this.buildMark(seriesIndex);
409 410
                }
            }
K
kener 已提交
411
        },
K
kener 已提交
412 413 414 415

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

K
kener 已提交
426
            var size = this._mapSize(categoryAxis, locationMap);
K
kener 已提交
427 428 429 430 431 432
            var gap = size.gap;
            var barGap = size.barGap;
            var barWidthMap = size.barWidthMap;
            var barWidth = size.barWidth;                   // 自适应宽度
            var barMinHeightMap = size.barMinHeightMap;
            var barHeight;
K
kener 已提交
433
            var interval = size.interval;
K
kener 已提交
434 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
                        
K
kener 已提交
530
                        if (i % interval === 0) {
K
kener 已提交
531 532 533 534 535 536 537 538 539 540
                            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
                    ];
K
kener 已提交
614 615 616
                    
                    xMarkMap[seriesIndex].isHorizontal = false;
                    this.buildMark(seriesIndex);
617 618
                }
            }
K
kener 已提交
619
        },
K
kener 已提交
620
        
K
kener 已提交
621 622 623 624 625
        /**
         * 我真是自找麻烦啊,为啥要允许系列级个性化最小宽度和高度啊!!!
         * @param {CategoryAxis} categoryAxis 类目坐标轴,需要知道类目间隔大小
         * @param {Array} locationMap 整形数据的系列索引
         */
K
kener 已提交
626 627
        _mapSize : function (categoryAxis, locationMap, ignoreUserDefined) {
            var series = this.series;
K
kener 已提交
628
            var seriesIndex;
K
kener 已提交
629 630 631 632 633
            var barWidthMap = {};
            var barMinHeightMap = {};
            var sBarWidth;
            var sBarWidthCounter = 0;
            var sBarWidthTotal = 0;
K
kener 已提交
634 635
            var barGap;
            var barCategoryGap;
K
kener 已提交
636
            var hasFound;
K
kener 已提交
637
            var queryTarget;
K
kener 已提交
638
            var interval = 1;
K
kener 已提交
639 640

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

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

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

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

        /**
         * 生成最终图形数据
         */
K
kener 已提交
778 779
        _getBarItem : function (seriesIndex, dataIndex, name, x, y, width, height, orient) {
            var series = this.series;
K
kener 已提交
780 781 782 783
            var barShape;
            var serie = series[seriesIndex];
            var data = serie.data[dataIndex];
            // 多级控制
K
kener 已提交
784
            var defaultColor = this._sIndex2colorMap[seriesIndex];
K
kener 已提交
785
            var queryTarget = [data, serie];
K
kener 已提交
786
            var normalColor = this.deepQuery(
K
kener 已提交
787
                queryTarget,
K
kener 已提交
788
                'itemStyle.normal.color'
K
kener 已提交
789
            ) || defaultColor;
K
kener 已提交
790
            var emphasisColor = this.deepQuery(
K
kener 已提交
791
                queryTarget,
K
kener 已提交
792 793
                'itemStyle.emphasis.color'
            );
K
kener 已提交
794
            var normal = this.deepMerge(
K
kener 已提交
795
                queryTarget,
796
                'itemStyle.normal'
K
kener 已提交
797
            );
798
            var normalBorderWidth = normal.barBorderWidth;
K
kener 已提交
799
            var emphasis = this.deepMerge(
K
kener 已提交
800
                queryTarget,
801
                'itemStyle.emphasis'
K
kener 已提交
802
            );
K
kener 已提交
803
            barShape = {
K
kener 已提交
804
                zlevel : this._zlevelBase,
K
kener 已提交
805
                clickable: this.deepQuery(queryTarget, 'clickable'),
K
kener 已提交
806 807 808 809 810 811
                style : {
                    x : x,
                    y : y,
                    width : width,
                    height : height,
                    brushType : 'both',
K
kener 已提交
812
                    color : this.getItemStyleColor(normalColor, seriesIndex, dataIndex, data),
813
                    radius : normal.barBorderRadius,
814
                    lineWidth : normalBorderWidth,
815
                    strokeColor : normal.barBorderColor
K
kener 已提交
816 817
                },
                highlightStyle : {
K
kener 已提交
818
                    color : this.getItemStyleColor(emphasisColor, seriesIndex, dataIndex, data),
819 820 821
                    radius : emphasis.barBorderRadius,
                    lineWidth : emphasis.barBorderWidth,
                    strokeColor : emphasis.barBorderColor
K
kener 已提交
822 823
                },
                _orient : orient
K
kener 已提交
824
            };
K
kener 已提交
825 826 827 828 829
            barShape.highlightStyle.color = barShape.highlightStyle.color
                            || (typeof barShape.style.color == 'string'
                                ? zrColor.lift(barShape.style.color, -0.3)
                                : barShape.style.color
                               );
K
kener 已提交
830
            // 考虑线宽的显示优化
K
kener 已提交
831 832
            if (normalBorderWidth > 0
                && barShape.style.height > normalBorderWidth
833
                && barShape.style.width > normalBorderWidth
K
kener 已提交
834
            ) {
835 836 837 838
                barShape.style.y += normalBorderWidth / 2;
                barShape.style.height -= normalBorderWidth;
                barShape.style.x += normalBorderWidth / 2;
                barShape.style.width -= normalBorderWidth;
K
kener 已提交
839 840
            }
            else {
K
kener 已提交
841
                // 太小了或者线宽小于0,废了边线
K
kener 已提交
842 843 844
                barShape.style.brushType = 'fill';
            }
            
K
kener 已提交
845
            barShape.highlightStyle.textColor = barShape.highlightStyle.color;
K
kener 已提交
846
            
K
kener 已提交
847
            barShape = this.addLabel(barShape, serie, data, name, orient);
K
kener 已提交
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
            if (barShape.style.textPosition == 'insideLeft'
                || barShape.style.textPosition == 'insideRight'
                || barShape.style.textPosition == 'insideTop'
                || barShape.style.textPosition == 'insideBottom'
            ) {
                var gap = 5;
                switch (barShape.style.textPosition) {
                    case 'insideLeft' : 
                        barShape.style.textX = barShape.style.x + gap;
                        barShape.style.textY = barShape.style.y + barShape.style.height / 2;
                        barShape.style.textAlign = 'left';
                        barShape.style.textBaseline = 'middle';
                        break;
                    case 'insideRight' : 
                        barShape.style.textX = barShape.style.x + barShape.style.width - gap;
                        barShape.style.textY = barShape.style.y + barShape.style.height / 2;
                        barShape.style.textAlign = 'right';
                        barShape.style.textBaseline = 'middle';
                        break;
                    case 'insideTop' : 
                        barShape.style.textX = barShape.style.x + barShape.style.width / 2;
                        barShape.style.textY = barShape.style.y + gap / 2;
                        barShape.style.textAlign = 'center';
                        barShape.style.textBaseline = 'top';
                        break;
                    case 'insideBottom' : 
                        barShape.style.textX = barShape.style.x + barShape.style.width / 2;
                        barShape.style.textY = barShape.style.y + barShape.style.height - gap / 2;
                        barShape.style.textAlign = 'center';
                        barShape.style.textBaseline = 'bottom';
                        break;
                }
                barShape.style.textPosition = 'specific';
                barShape.style.textColor = barShape.style.textColor || '#fff';
            }
            
            
K
kener 已提交
885

K
kener 已提交
886 887
            if (this.deepQuery([data, serie, this.option],'calculable')) {
                this.setCalculable(barShape);
K
kener 已提交
888 889 890 891 892 893
                barShape.draggable = true;
            }

            ecData.pack(
                barShape,
                series[seriesIndex], seriesIndex,
K
kener 已提交
894
                series[seriesIndex].data[dataIndex], dataIndex,
K
kener 已提交
895 896 897 898
                name
            );

            return barShape;
K
kener 已提交
899
        },
K
kener 已提交
900

K
kener 已提交
901
        // 位置转换
K
kener 已提交
902 903 904
        getMarkCoord : function (seriesIndex, mpData) {
            var serie = this.series[seriesIndex];
            var xMarkMap = this.xMarkMap[seriesIndex];
K
kener 已提交
905 906
            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
K
kener 已提交
907 908
            var dataIndex;
            var pos;
909
            if (mpData.type
K
kener 已提交
910
                && (mpData.type == 'max' || mpData.type == 'min' || mpData.type == 'average')
911 912 913
            ) {
                // 特殊值内置支持
                pos = [
K
kener 已提交
914 915 916 917
                    xMarkMap[mpData.type + 'X'],
                    xMarkMap[mpData.type + 'Y'],
                    xMarkMap[mpData.type + 'Line'],
                    xMarkMap[mpData.type]
918 919
                ];
            }
K
kener 已提交
920
            else if (xMarkMap.isHorizontal) {
K
kener 已提交
921
                // 横向
K
kener 已提交
922
                dataIndex = typeof mpData.xAxis == 'string' && xAxis.getIndexByName
K
kener 已提交
923 924
                            ? xAxis.getIndexByName(mpData.xAxis)
                            : (mpData.xAxis || 0);
K
kener 已提交
925
                
K
kener 已提交
926
                var x = xMarkMap[dataIndex];
K
kener 已提交
927 928 929 930 931 932 933
                x = typeof x != 'undefined'
                    ? x 
                    : typeof mpData.xAxis != 'string' && xAxis.getCoordByIndex
                      ? xAxis.getCoordByIndex(mpData.xAxis || 0)
                      : xAxis.getCoord(mpData.xAxis || 0);
                
                pos = [x, yAxis.getCoord(mpData.yAxis || 0)];
K
kener 已提交
934 935 936
            }
            else {
                // 纵向
K
kener 已提交
937
                dataIndex = typeof mpData.yAxis == 'string' && yAxis.getIndexByName
K
kener 已提交
938 939
                            ? yAxis.getIndexByName(mpData.yAxis)
                            : (mpData.yAxis || 0);
K
kener 已提交
940
                
K
kener 已提交
941
                var y = xMarkMap[dataIndex];
K
kener 已提交
942 943 944 945 946 947 948
                y = typeof y != 'undefined'
                    ? y
                    : typeof mpData.yAxis != 'string' && yAxis.getCoordByIndex
                      ? yAxis.getCoordByIndex(mpData.yAxis || 0)
                      : yAxis.getCoord(mpData.yAxis || 0);
                
                pos = [xAxis.getCoord(mpData.xAxis || 0), y];
K
kener 已提交
949
            }
K
kener 已提交
950
            
K
kener 已提交
951
            return pos;
K
kener 已提交
952
        },
K
kener 已提交
953
        
K
kener 已提交
954 955 956
        /**
         * 刷新
         */
K
kener 已提交
957
        refresh : function (newOption) {
K
kener 已提交
958
            if (newOption) {
K
kener 已提交
959 960
                this.option = newOption;
                this.series = newOption.series;
K
kener 已提交
961
            }
K
kener 已提交
962 963
            
            this.backupShapeList();
K
kener 已提交
964 965
            this._buildShape();
        },
966 967 968 969
        
        /**
         * 动态数据增加动画 
         */
K
kener 已提交
970 971
        addDataAnimation : function (params) {
            var series = this.series;
972 973 974 975 976 977 978 979 980 981 982
            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 已提交
983 984
            for (var i = this.shapeList.length - 1; i >= 0; i--) {
                seriesIndex = ecData.get(this.shapeList[i], 'seriesIndex');
985 986
                if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
                    // 有数据删除才有移动的动画
K
kener 已提交
987
                    if (this.shapeList[i].type == 'rectangle') {
988
                        // 主动画
K
kener 已提交
989
                        dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
990 991 992 993 994
                        serie = series[seriesIndex];
                        if (aniMap[seriesIndex][2] 
                            && dataIndex == serie.data.length - 1
                        ) {
                            // 队头加入删除末尾
K
kener 已提交
995
                            this.zr.delShape(this.shapeList[i].id);
996 997
                            continue;
                        }
K
kener 已提交
998
                        else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
999
                            // 队尾加入删除头部
K
kener 已提交
1000
                            this.zr.delShape(this.shapeList[i].id);
1001 1002
                            continue;
                        }
K
kener 已提交
1003
                        if (this.shapeList[i]._orient == 'horizontal') {
1004
                            // 条形图
K
kener 已提交
1005
                            dy = this.component.yAxis.getAxis(
1006 1007 1008 1009 1010 1011 1012
                                    serie.yAxisIndex || 0
                                 ).getGap();
                            y = aniMap[seriesIndex][2] ? -dy : dy;
                            x = 0;
                        }
                        else {
                            // 柱形图
K
kener 已提交
1013
                            dx = this.component.xAxis.getAxis(
1014 1015 1016 1017 1018
                                    serie.xAxisIndex || 0
                                 ).getGap();
                            x = aniMap[seriesIndex][2] ? dx : -dx;
                            y = 0;
                        }
K
kener 已提交
1019
                        this.shapeList[i].position = [0, 0];
K
kener 已提交
1020
                        this.zr.animate(this.shapeList[i].id, '')
1021 1022 1023 1024 1025 1026 1027 1028
                            .when(
                                500,
                                {position : [x, y]}
                            )
                            .start();
                    }
                }
            }
K
kener 已提交
1029
        }
K
kener 已提交
1030
    };
K
kener 已提交
1031
    
K
kener 已提交
1032
    zrUtil.inherits(Bar, ChartBase);
K
kener 已提交
1033 1034
    zrUtil.inherits(Bar, ComponentBase);
    
1035 1036 1037
    // 图表注册
    require('../chart').define('bar', Bar);
    
K
kener 已提交
1038 1039
    return Bar;
});