bar.js 43.5 KB
Newer Older
K
kener 已提交
1 2 3 4
/**
 * echarts图表类:柱形图
 *
 * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
K
kener 已提交
5
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
K
kener 已提交
6 7
 *
 */
K
kener 已提交
8
define(function (require) {
K
kener 已提交
9
    var ChartBase = require('./base');
K
kener 已提交
10
    
K
kener 已提交
11
    // 图形依赖
K
kener 已提交
12
    var RectangleShape = require('zrender/shape/Rectangle');
K
kener 已提交
13 14 15 16
    // 组件依赖
    require('../component/axis');
    require('../component/grid');
    require('../component/dataZoom');
K
kener 已提交
17
    
K
kener 已提交
18
    var ecConfig = require('../config');
K
Kener 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
    // 柱形图默认参数
    ecConfig.bar = {
        zlevel: 0,                  // 一级层叠
        z: 2,                       // 二级层叠
        clickable: true,
        legendHoverLink: true,
        // stack: null
        xAxisIndex: 0,
        yAxisIndex: 0,
        barMinHeight: 0,          // 最小高度改为0
        // barWidth: null,        // 默认自适应
        barGap: '30%',            // 柱间距离,默认为柱形宽度的30%,可设固定值
        barCategoryGap: '20%',    // 类目间柱形距离,默认为类目间距的20%,可设固定值
        itemStyle: {
            normal: {
                // color: '各异',
                barBorderColor: '#fff',       // 柱条边线
                barBorderRadius: 0,           // 柱条边线圆角,单位px,默认为0
                barBorderWidth: 0,            // 柱条边线线宽,单位px,默认为1
                label: {
                    show: false
                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
                    // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
                    //           'inside'|'left'|'right'|'top'|'bottom'
                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
                }
            },
            emphasis: {
                // color: '各异',
                barBorderColor: '#fff',            // 柱条边线
                barBorderRadius: 0,                // 柱条边线圆角,单位px,默认为0
                barBorderWidth: 0,                 // 柱条边线线宽,单位px,默认为1
                label: {
                    show: false
                    // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调
                    // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为
                    //           'inside'|'left'|'right'|'top'|'bottom'
                    // textStyle: null      // 默认使用全局文本样式,详见TEXTSTYLE
                }
            }
        }
    };

K
kener 已提交
62 63 64 65
    var ecData = require('../util/ecData');
    var zrUtil = require('zrender/tool/util');
    var zrColor = require('zrender/tool/color');
    
K
kener 已提交
66 67 68 69 70 71 72
    /**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} series 数据
     * @param {Object} component 组件
     */
K
kener 已提交
73
    function Bar(ecTheme, messageCenter, zr, option, myChart){
K
kener 已提交
74
        // 图表基类
L
lang 已提交
75
        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
K
kener 已提交
76
        
K
kener 已提交
77
        this.refresh(option);
K
kener 已提交
78 79 80
    }
    
    Bar.prototype = {
81
        type: ecConfig.CHART_TYPE_BAR,
K
kener 已提交
82 83 84
        /**
         * 绘制图形
         */
85
        _buildShape: function () {
L
lang 已提交
86
            this._buildPosition();
K
kener 已提交
87
        },
K
kener 已提交
88 89
        
        _buildNormal: function(seriesArray, maxDataLength, locationMap, xMarkMap, orient) {
K
kener 已提交
90
            var series = this.series;
K
kener 已提交
91 92 93
            // 确定类目轴和数值轴,同一方向随便找一个即可
            var seriesIndex = locationMap[0][0];
            var serie = series[seriesIndex];
K
Kener 已提交
94 95 96 97 98 99
            var isHorizontal = orient == 'horizontal';
            var xAxis = this.component.xAxis;
            var yAxis = this.component.yAxis;
            var categoryAxis = isHorizontal 
                               ? xAxis.getAxis(serie.xAxisIndex)
                               : yAxis.getAxis(serie.yAxisIndex);
K
kener 已提交
100 101
            var valueAxis;  // 数值轴各异

K
kener 已提交
102
            var size = this._mapSize(categoryAxis, locationMap);
K
kener 已提交
103 104 105
            var gap = size.gap;
            var barGap = size.barGap;
            var barWidthMap = size.barWidthMap;
K
kener 已提交
106
            var barMaxWidthMap = size.barMaxWidthMap;
K
kener 已提交
107 108 109
            var barWidth = size.barWidth;                   // 自适应宽度
            var barMinHeightMap = size.barMinHeightMap;
            var barHeight;
K
kener 已提交
110
            var curBarWidth;
K
kener 已提交
111
            var interval = size.interval;
K
kener 已提交
112 113 114

            var x;
            var y;
K
kener 已提交
115 116 117 118
            var lastP; // 正向堆积处理
            var baseP;
            var lastN; // 负向堆积处理
            var baseN;
K
kener 已提交
119 120 121
            var barShape;
            var data;
            var value;
K
Kener 已提交
122
            var islandR = this.deepQuery([this.ecTheme, ecConfig], 'island.r');
K
kener 已提交
123
            for (var i = 0, l = maxDataLength; i < l; i++) {
124
                if (categoryAxis.getNameByIndex(i) == null) {
K
kener 已提交
125 126 127
                    // 系列数据超出类目轴长度
                    break;
                }
K
Kener 已提交
128
                isHorizontal
K
kener 已提交
129 130
                    ? (x = categoryAxis.getCoordByIndex(i) - gap / 2)
                    : (y = categoryAxis.getCoordByIndex(i) + gap / 2);
K
Kener 已提交
131

K
kener 已提交
132
                for (var j = 0, k = locationMap.length; j < k; j++) {
K
kener 已提交
133
                    // 堆积数据用第一条valueAxis
K
Kener 已提交
134 135 136 137 138
                    var yAxisIndex = series[locationMap[j][0]].yAxisIndex || 0;
                    var xAxisIndex = series[locationMap[j][0]].xAxisIndex || 0;
                    valueAxis = isHorizontal 
                                ? yAxis.getAxis(yAxisIndex)
                                : xAxis.getAxis(xAxisIndex);
K
kener 已提交
139
                    baseP = lastP = baseN = lastN = valueAxis.getCoord(0);
K
kener 已提交
140 141 142 143
                    for (var m = 0, n = locationMap[j].length; m < n; m++) {
                        seriesIndex = locationMap[j][m];
                        serie = series[seriesIndex];
                        data = serie.data[i];
K
Kener 已提交
144
                        value = this.getDataFromOption(data, '-');
K
kener 已提交
145 146
                        xMarkMap[seriesIndex] = xMarkMap[seriesIndex] 
                                                || {
147 148 149 150 151
                                                    min: Number.POSITIVE_INFINITY,
                                                    max: Number.NEGATIVE_INFINITY,
                                                    sum: 0,
                                                    counter: 0,
                                                    average: 0
K
kener 已提交
152
                                                };
K
Kener 已提交
153 154 155 156
                        curBarWidth = Math.min(
                            barMaxWidthMap[seriesIndex] || Number.MAX_VALUE,
                            barWidthMap[seriesIndex] || barWidth
                        );
157
                        if (value === '-') {
K
kener 已提交
158 159 160 161
                            // 空数据在做完后补充拖拽提示框
                            continue;
                        }
                        if (value > 0) {
K
kener 已提交
162
                            // 正向堆积
163 164
                            barHeight = m > 0 
                                        ? valueAxis.getCoordSize(value)
K
kener 已提交
165
                                        : (
K
Kener 已提交
166
                                            isHorizontal
K
kener 已提交
167 168 169
                                            ? (baseP - valueAxis.getCoord(value))
                                            : (valueAxis.getCoord(value) - baseP)
                                        );
K
kener 已提交
170
                            // 非堆积数据最小高度有效
K
kener 已提交
171
                            if (n === 1 && barMinHeightMap[seriesIndex] > barHeight) {
K
kener 已提交
172 173
                                barHeight = barMinHeightMap[seriesIndex];
                            }
K
Kener 已提交
174
                            if (isHorizontal) {
K
kener 已提交
175 176 177 178 179 180 181
                                lastP -= barHeight;
                                y = lastP;
                            }
                            else {
                                x = lastP;
                                lastP += barHeight;
                            }
K
kener 已提交
182 183
                        }
                        else if (value < 0){
K
kener 已提交
184
                            // 负向堆积
185 186
                            barHeight = m > 0 
                                        ? valueAxis.getCoordSize(value)
K
kener 已提交
187
                                        : (
K
Kener 已提交
188
                                            isHorizontal
K
kener 已提交
189 190 191
                                            ? (valueAxis.getCoord(value) - baseN)
                                            : (baseN - valueAxis.getCoord(value))
                                        );
K
kener 已提交
192
                            // 非堆积数据最小高度有效
K
kener 已提交
193
                            if (n === 1 && barMinHeightMap[seriesIndex] > barHeight) {
K
kener 已提交
194 195
                                barHeight = barMinHeightMap[seriesIndex];
                            }
K
Kener 已提交
196
                            if (isHorizontal) {
K
kener 已提交
197 198 199 200 201 202 203
                                y = lastN;
                                lastN += barHeight;
                            }
                            else {
                                lastN -= barHeight;
                                x = lastN;
                            }
K
kener 已提交
204 205 206
                        }
                        else {
                            // 0值
K
kener 已提交
207
                            barHeight = 0;
K
kener 已提交
208
                            // 最小高度无效
K
Kener 已提交
209
                            if (isHorizontal) {
K
kener 已提交
210 211 212 213 214 215 216
                                lastP -= barHeight;
                                y = lastP;
                            }
                            else {
                                x = lastP;
                                lastP += barHeight;
                            }
K
kener 已提交
217
                        }
K
Kener 已提交
218
                        xMarkMap[seriesIndex][i] = isHorizontal
L
jshint  
lang 已提交
219 220
                                                   ? (x + curBarWidth / 2) 
                                                   : (y - curBarWidth / 2);
221 222
                        if (xMarkMap[seriesIndex].min > value) {
                            xMarkMap[seriesIndex].min = value;
K
Kener 已提交
223
                            if (isHorizontal) {
K
kener 已提交
224 225 226 227 228 229 230
                                xMarkMap[seriesIndex].minY = y;
                                xMarkMap[seriesIndex].minX = xMarkMap[seriesIndex][i];
                            }
                            else {
                                xMarkMap[seriesIndex].minX = x + barHeight;
                                xMarkMap[seriesIndex].minY = xMarkMap[seriesIndex][i];
                            }
231 232 233
                        }
                        if (xMarkMap[seriesIndex].max < value) {
                            xMarkMap[seriesIndex].max = value;
K
Kener 已提交
234
                            if (isHorizontal) {
K
kener 已提交
235 236 237 238 239 240 241 242
                                xMarkMap[seriesIndex].maxY = y;
                                xMarkMap[seriesIndex].maxX = xMarkMap[seriesIndex][i];
                            }
                            else {
                                xMarkMap[seriesIndex].maxX = x + barHeight;
                                xMarkMap[seriesIndex].maxY = xMarkMap[seriesIndex][i];
                            }
                            
243 244 245
                        }
                        xMarkMap[seriesIndex].sum += value;
                        xMarkMap[seriesIndex].counter++;
K
kener 已提交
246
                        
K
kener 已提交
247
                        if (i % interval === 0) {
K
kener 已提交
248 249 250
                            barShape = this._getBarItem(
                                seriesIndex, i,
                                categoryAxis.getNameByIndex(i),
K
kener 已提交
251
                                x,
K
Kener 已提交
252 253 254 255
                                y - (isHorizontal ? 0 : curBarWidth),
                                isHorizontal ? curBarWidth : barHeight,
                                isHorizontal ? barHeight : curBarWidth,
                                isHorizontal ? 'vertical' : 'horizontal'
K
kener 已提交
256 257 258
                            );
                            this.shapeList.push(new RectangleShape(barShape));
                        }
K
kener 已提交
259 260 261 262 263 264 265
                    }

                    // 补充空数据的拖拽提示框
                    for (var m = 0, n = locationMap[j].length; m < n; m++) {
                        seriesIndex = locationMap[j][m];
                        serie = series[seriesIndex];
                        data = serie.data[i];
K
Kener 已提交
266
                        value = this.getDataFromOption(data, '-');
K
Kener 已提交
267 268 269 270
                        curBarWidth = Math.min(
                            barMaxWidthMap[seriesIndex] || Number.MAX_VALUE,
                            barWidthMap[seriesIndex] || barWidth
                        );
K
kener 已提交
271 272 273 274 275
                        if (value != '-') {
                            // 只关心空数据
                            continue;
                        }

K
kener 已提交
276
                        if (this.deepQuery([data, serie, this.option], 'calculable')) {
K
Kener 已提交
277
                            if (isHorizontal) {
K
Kener 已提交
278
                                lastP -= islandR;
K
kener 已提交
279 280 281 282
                                y = lastP;
                            }
                            else {
                                x = lastP;
K
Kener 已提交
283
                                lastP += islandR;
K
kener 已提交
284 285
                            }
                            
K
kener 已提交
286
                            barShape = this._getBarItem(
K
kener 已提交
287 288
                                seriesIndex, i,
                                categoryAxis.getNameByIndex(i),
K
Kener 已提交
289 290
                                x,
                                y - (isHorizontal ? 0 : curBarWidth),
K
Kener 已提交
291 292
                                isHorizontal ? curBarWidth : islandR,
                                isHorizontal ? islandR : curBarWidth,
K
Kener 已提交
293
                                isHorizontal ? 'vertical' : 'horizontal'
K
kener 已提交
294 295 296
                            );
                            barShape.hoverable = false;
                            barShape.draggable = false;
K
kener 已提交
297
                            barShape.style.lineWidth = 1;
K
kener 已提交
298
                            barShape.style.brushType = 'stroke';
K
kener 已提交
299
                            barShape.style.strokeColor = serie.calculableHolderColor
K
Kener 已提交
300 301
                                                         || this.ecTheme.calculableHolderColor
                                                         || ecConfig.calculableHolderColor;
K
kener 已提交
302

K
kener 已提交
303
                            this.shapeList.push(new RectangleShape(barShape));
K
kener 已提交
304 305
                        }
                    }
K
Kener 已提交
306 307 308
                    isHorizontal
                        ? (x += (curBarWidth + barGap))
                        : (y -= (curBarWidth + barGap));
K
kener 已提交
309 310
                }
            }
311
            
K
Kener 已提交
312
            this._calculMarkMapXY(xMarkMap, locationMap, isHorizontal ? 'y' : 'x');
K
kener 已提交
313 314 315 316 317 318 319 320
        },
        /**
         * 构建类目轴为水平方向的柱形图系列
         */
        _buildHorizontal: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
            return this._buildNormal(
                seriesArray, maxDataLength, locationMap, xMarkMap, 'horizontal'
            );
K
kener 已提交
321
        },
K
kener 已提交
322 323 324 325

        /**
         * 构建类目轴为垂直方向的柱形图系列
         */
K
kener 已提交
326
        _buildVertical: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
K
kener 已提交
327 328 329
            return this._buildNormal(
                seriesArray, maxDataLength, locationMap, xMarkMap, 'vertical'
            );
K
kener 已提交
330 331 332 333 334 335 336 337
        },
        
        /**
         * 构建双数值轴柱形图
         */
        _buildOther: function (seriesArray, maxDataLength, locationMap, xMarkMap) {
            var series = this.series;
            
338 339
            for (var j = 0, k = locationMap.length; j < k; j++) {
                for (var m = 0, n = locationMap[j].length; m < n; m++) {
K
kener 已提交
340 341 342 343 344 345 346 347
                    var seriesIndex = locationMap[j][m];
                    var serie = series[seriesIndex];
                    var xAxisIndex = serie.xAxisIndex || 0;
                    var xAxis = this.component.xAxis.getAxis(xAxisIndex);
                    var baseX = xAxis.getCoord(0);
                    var yAxisIndex = serie.yAxisIndex || 0;
                    var yAxis = this.component.yAxis.getAxis(yAxisIndex);
                    var baseY = yAxis.getCoord(0);
K
kener 已提交
348
                    
K
kener 已提交
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
                    xMarkMap[seriesIndex] = xMarkMap[seriesIndex] 
                                            || {
                                                min0: Number.POSITIVE_INFINITY,
                                                min1: Number.POSITIVE_INFINITY,
                                                max0: Number.NEGATIVE_INFINITY,
                                                max1: Number.NEGATIVE_INFINITY,
                                                sum0: 0,
                                                sum1: 0,
                                                counter0: 0,
                                                counter1: 0,
                                                average0: 0,
                                                average1: 0
                                            };

                    for (var i = 0, l = serie.data.length; i < l; i++) {
                        var data = serie.data[i];
K
Kener 已提交
365
                        var value = this.getDataFromOption(data, '-');
K
kener 已提交
366 367 368
                        if (!(value instanceof Array)) {
                            continue;
                        }
369
                        
K
kener 已提交
370 371 372 373 374 375 376
                        var x = xAxis.getCoord(value[0]);
                        var y = yAxis.getCoord(value[1]);
                        
                        var queryTarget = [data, serie];
                        var barWidth = this.deepQuery(queryTarget, 'barWidth') || 10; // 默认柱形
                        var barHeight = this.deepQuery(queryTarget, 'barHeight');
                        var orient;
K
jshint  
kener 已提交
377
                        var barShape;
K
kener 已提交
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
                        
                        if (barHeight != null) {
                            // 条形图
                            orient = 'horizontal';
                            
                            if (value[0] > 0) {
                                // 正向
                                barWidth = x - baseX;
                                x -= barWidth;
                            }
                            else if (value[0] < 0){
                                // 负向
                                barWidth = baseX - x;
                            }
                            else {
                                // 0值
                                barWidth = 0;
                            }
                            
                            barShape = this._getBarItem(
                                seriesIndex, i,
                                value[0],
                                x, 
                                y - barHeight / 2,
                                barWidth,
                                barHeight,
                                orient
                            );
                        }
                        else {
                            // 柱形
                            orient = 'vertical';
                            
                            if (value[1] > 0) {
                            // 正向
                                barHeight = baseY - y;
                            }
                            else if (value[1] < 0){
                                // 负向
                                barHeight = y - baseY;
                                y -= barHeight;
                            }
                            else {
                                // 0值
                                barHeight = 0;
                            }
                            barShape = this._getBarItem(
                                seriesIndex, i,
                                value[0],
                                x - barWidth / 2, 
                                y,
                                barWidth,
                                barHeight,
                                orient
                            );
                        }
                        this.shapeList.push(new RectangleShape(barShape));
                        
                        
                        x = xAxis.getCoord(value[0]);
                        y = yAxis.getCoord(value[1]);
                        if (xMarkMap[seriesIndex].min0 > value[0]) {
                            xMarkMap[seriesIndex].min0 = value[0];
                            xMarkMap[seriesIndex].minY0 = y;
                            xMarkMap[seriesIndex].minX0 = x;
                        }
                        if (xMarkMap[seriesIndex].max0 < value[0]) {
                            xMarkMap[seriesIndex].max0 = value[0];
                            xMarkMap[seriesIndex].maxY0 = y;
                            xMarkMap[seriesIndex].maxX0 = x;
                        }
                        xMarkMap[seriesIndex].sum0 += value[0];
                        xMarkMap[seriesIndex].counter0++;
                        
                        if (xMarkMap[seriesIndex].min1 > value[1]) {
                            xMarkMap[seriesIndex].min1 = value[1];
                            xMarkMap[seriesIndex].minY1 = y;
                            xMarkMap[seriesIndex].minX1 = x;
                        }
                        if (xMarkMap[seriesIndex].max1 < value[1]) {
                            xMarkMap[seriesIndex].max1 = value[1];
                            xMarkMap[seriesIndex].maxY1 = y;
                            xMarkMap[seriesIndex].maxX1 = x;
                        }
                        xMarkMap[seriesIndex].sum1 += value[1];
                        xMarkMap[seriesIndex].counter1++;
                    }
465 466
                }
            }
K
kener 已提交
467 468
            
            this._calculMarkMapXY(xMarkMap, locationMap, 'xy');
K
kener 已提交
469
        },
K
kener 已提交
470
        
K
kener 已提交
471 472 473 474 475
        /**
         * 我真是自找麻烦啊,为啥要允许系列级个性化最小宽度和高度啊!!!
         * @param {CategoryAxis} categoryAxis 类目坐标轴,需要知道类目间隔大小
         * @param {Array} locationMap 整形数据的系列索引
         */
476
        _mapSize: function (categoryAxis, locationMap, ignoreUserDefined) {
K
kener 已提交
477 478 479 480 481 482 483 484 485
            var res = this._findSpecialBarSzie(locationMap, ignoreUserDefined);
            var barWidthMap = res.barWidthMap;
            var barMaxWidthMap = res.barMaxWidthMap;
            var barMinHeightMap = res.barMinHeightMap;
            var sBarWidthCounter = res.sBarWidthCounter;    // 用户指定
            var sBarWidthTotal = res.sBarWidthTotal;        // 用户指定
            var barGap = res.barGap;
            var barCategoryGap = res.barCategoryGap;
            
K
kener 已提交
486 487
            var gap;
            var barWidth;
K
kener 已提交
488
            var interval = 1;
K
kener 已提交
489 490
            if (locationMap.length != sBarWidthCounter) {
                // 至少存在一个自适应宽度的柱形图
K
kener 已提交
491
                if (!ignoreUserDefined) {
K
kener 已提交
492 493
                    gap = typeof barCategoryGap === 'string' && barCategoryGap.match(/%$/)
                          // 百分比
K
kener 已提交
494
                          ? ((categoryAxis.getGap() * (100 - parseFloat(barCategoryGap)) / 100).toFixed(2) - 0)
K
kener 已提交
495 496
                          // 数值
                          : (categoryAxis.getGap() - barCategoryGap);
497
                    if (typeof barGap === 'string' && barGap.match(/%$/)) {
K
kener 已提交
498
                        barGap = parseFloat(barGap) / 100;
K
Kener 已提交
499 500 501 502 503
                        barWidth = +(
                            (gap - sBarWidthTotal) / (
                                (locationMap.length - 1) * barGap + locationMap.length - sBarWidthCounter
                            )
                        ).toFixed(2);
K
kener 已提交
504
                        barGap = barWidth * barGap;
K
kener 已提交
505 506 507
                    }
                    else {
                        barGap = parseFloat(barGap);
K
Kener 已提交
508 509 510 511 512
                        barWidth = +(
                            (gap - sBarWidthTotal - barGap * (locationMap.length - 1)) / (
                                locationMap.length - sBarWidthCounter
                            )
                        ).toFixed(2);
K
kener 已提交
513
                    }
K
kener 已提交
514
                    // 无法满足用户定义的宽度设计,忽略用户宽度,打回重做
K
kener 已提交
515
                    if (barWidth <= 0) {
K
kener 已提交
516
                        return this._mapSize(categoryAxis, locationMap, true);
K
kener 已提交
517 518 519 520 521 522
                    }
                }
                else {
                    // 忽略用户定义的宽度设定
                    gap = categoryAxis.getGap();
                    barGap = 0;
K
Kener 已提交
523
                    barWidth = +(gap / locationMap.length).toFixed(2);
K
kener 已提交
524
                    // 已经忽略用户定义的宽度设定依然还无法满足显示,只能硬来了;
K
kener 已提交
525
                    if (barWidth <= 0) {
K
kener 已提交
526
                        interval = Math.floor(locationMap.length / gap);
K
kener 已提交
527 528
                        barWidth = 1;
                    }
K
kener 已提交
529 530 531
                }
            }
            else {
K
kener 已提交
532 533
                // 全是自定义宽度,barGap无效,系列间隔决定barGap
                gap = sBarWidthCounter > 1
K
kener 已提交
534
                      ? (typeof barCategoryGap === 'string' && barCategoryGap.match(/%$/))
K
kener 已提交
535
                          // 百分比
K
Kener 已提交
536
                          ? +(categoryAxis.getGap() * (100 - parseFloat(barCategoryGap)) / 100).toFixed(2)
K
kener 已提交
537 538 539 540
                          // 数值
                          : (categoryAxis.getGap() - barCategoryGap)
                      // 只有一个
                      : sBarWidthTotal;
K
kener 已提交
541
                barWidth = 0;
K
kener 已提交
542
                barGap = sBarWidthCounter > 1 
K
Kener 已提交
543
                         ? +((gap - sBarWidthTotal) / (sBarWidthCounter - 1)).toFixed(2)
K
kener 已提交
544 545
                         : 0;
                if (barGap < 0) {
K
kener 已提交
546
                    // 无法满足用户定义的宽度设计,忽略用户宽度,打回重做
K
kener 已提交
547
                    return this._mapSize(categoryAxis, locationMap, true);
K
kener 已提交
548 549
                }
            }
K
kener 已提交
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
            
            // 检查是否满足barMaxWidthMap
            
            return this._recheckBarMaxWidth(
                locationMap,
                barWidthMap, barMaxWidthMap, barMinHeightMap,
                gap,   // 总宽度
                barWidth, barGap, interval
            );
        },
        
        /**
         * 计算堆积下用户特殊指定的各种size 
         */
        _findSpecialBarSzie: function(locationMap, ignoreUserDefined) {
            var series = this.series;
            var barWidthMap = {};
            var barMaxWidthMap = {};
            var barMinHeightMap = {};
            var sBarWidth;              // 用户指定
            var sBarMaxWidth;           // 用户指定
            var sBarWidthCounter = 0;   // 用户指定
            var sBarWidthTotal = 0;     // 用户指定
            var barGap;
            var barCategoryGap;
            for (var j = 0, k = locationMap.length; j < k; j++) {
                var hasFound = {
                    barWidth: false,
                    barMaxWidth: false
                };
                for (var m = 0, n = locationMap[j].length; m < n; m++) {
                    var seriesIndex = locationMap[j][m];
                    var queryTarget = series[seriesIndex];
                    if (!ignoreUserDefined) {
                        if (!hasFound.barWidth) {
                            sBarWidth = this.query(queryTarget, 'barWidth');
                            if (sBarWidth != null) {
                                // 同一堆积第一个生效barWidth
                                barWidthMap[seriesIndex] = sBarWidth;
                                sBarWidthTotal += sBarWidth;
                                sBarWidthCounter++;
                                hasFound.barWidth = true;
                                // 复位前面同一堆积但没被定义的
                                for (var ii = 0, ll = m; ii < ll; ii++) {
                                    var pSeriesIndex = locationMap[j][ii];
                                    barWidthMap[pSeriesIndex] = sBarWidth;
                                }
                            }
                        }
                        else {
                            barWidthMap[seriesIndex] = sBarWidth;   // 用找到的一个
                        }
                        
                        if (!hasFound.barMaxWidth) {
                            sBarMaxWidth = this.query(queryTarget, 'barMaxWidth');
                            if (sBarMaxWidth != null) {
                                // 同一堆积第一个生效barMaxWidth
                                barMaxWidthMap[seriesIndex] = sBarMaxWidth;
                                hasFound.barMaxWidth = true;
                                // 复位前面同一堆积但没被定义的
                                for (var ii = 0, ll = m; ii < ll; ii++) {
                                    var pSeriesIndex = locationMap[j][ii];
                                    barMaxWidthMap[pSeriesIndex] = sBarMaxWidth;
                                }
                            }
                        }
                        else {
                            barMaxWidthMap[seriesIndex] = sBarMaxWidth;   // 用找到的一个
                        }
                    }
K
kener 已提交
620

K
kener 已提交
621 622 623 624 625 626 627
                    barMinHeightMap[seriesIndex] = this.query(queryTarget, 'barMinHeight');
                    barGap = barGap != null ? barGap : this.query(queryTarget, 'barGap');
                    barCategoryGap = barCategoryGap != null 
                                     ? barCategoryGap : this.query(queryTarget, 'barCategoryGap');
                }
            }
            
K
kener 已提交
628
            return {
629
                barWidthMap: barWidthMap,
K
kener 已提交
630 631 632 633 634 635 636 637
                barMaxWidthMap: barMaxWidthMap,
                barMinHeightMap: barMinHeightMap,
                sBarWidth: sBarWidth,
                sBarMaxWidth: sBarMaxWidth,
                sBarWidthCounter: sBarWidthCounter,
                sBarWidthTotal: sBarWidthTotal,
                barGap: barGap,
                barCategoryGap: barCategoryGap
L
jshint  
lang 已提交
638
            };
K
kener 已提交
639 640 641 642 643 644 645 646 647 648 649 650
        },
        
        /**
         * 检查是否满足barMaxWidthMap 
         */
        _recheckBarMaxWidth: function(
                locationMap,
                barWidthMap, barMaxWidthMap, barMinHeightMap,
                gap,   // 总宽度
                barWidth, barGap, interval
        ) {
            for (var j = 0, k = locationMap.length; j < k; j++) {
K
kener 已提交
651 652 653 654
                var seriesIndex = locationMap[j][0];
                if (barMaxWidthMap[seriesIndex] && barMaxWidthMap[seriesIndex] < barWidth) {
                    // 不满足最大宽度
                    gap -= barWidth - barMaxWidthMap[seriesIndex]; // 总宽度减少
K
kener 已提交
655 656
                }
            }
K
kener 已提交
657
            
K
kener 已提交
658 659 660
            return {
                barWidthMap: barWidthMap,
                barMaxWidthMap: barMaxWidthMap,
661
                barMinHeightMap: barMinHeightMap ,
K
kener 已提交
662
                gap: gap,   // 总宽度
663 664 665
                barWidth: barWidth,
                barGap: barGap,
                interval: interval
L
jshint  
lang 已提交
666
            };
K
kener 已提交
667
        },
K
kener 已提交
668
        
K
kener 已提交
669 670 671
        /**
         * 生成最终图形数据
         */
672
        _getBarItem: function (seriesIndex, dataIndex, name, x, y, width, height, orient) {
K
kener 已提交
673
            var series = this.series;
K
kener 已提交
674 675 676 677
            var barShape;
            var serie = series[seriesIndex];
            var data = serie.data[dataIndex];
            // 多级控制
K
kener 已提交
678
            var defaultColor = this._sIndex2ColorMap[seriesIndex];
K
kener 已提交
679
            var queryTarget = [data, serie];
K
kener 已提交
680 681 682
            
            var normal = this.deepMerge(queryTarget, 'itemStyle.normal');
            var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis');
K
Kener 已提交
683
            var normalBorderWidth = normal.barBorderWidth;
K
kener 已提交
684
            
K
kener 已提交
685
            barShape = {
686 687
                zlevel: this.getZlevelBase(),
                z: this.getZBase(),
K
kener 已提交
688
                clickable: this.deepQuery(queryTarget, 'clickable'),
689 690 691 692 693 694
                style: {
                    x: x,
                    y: y,
                    width: width,
                    height: height,
                    brushType: 'both',
K
Kener 已提交
695 696 697 698
                    color: this.getItemStyleColor(
                        this.deepQuery(queryTarget, 'itemStyle.normal.color') || defaultColor,
                        seriesIndex, dataIndex, data
                    ),
699 700 701
                    radius: normal.barBorderRadius,
                    lineWidth: normalBorderWidth,
                    strokeColor: normal.barBorderColor
K
kener 已提交
702
                },
703
                highlightStyle: {
K
Kener 已提交
704 705 706 707
                    color: this.getItemStyleColor(
                        this.deepQuery(queryTarget, 'itemStyle.emphasis.color'),
                        seriesIndex, dataIndex, data
                    ),
708 709 710
                    radius: emphasis.barBorderRadius,
                    lineWidth: emphasis.barBorderWidth,
                    strokeColor: emphasis.barBorderColor
K
kener 已提交
711
                },
712
                _orient: orient
K
kener 已提交
713
            };
K
Kener 已提交
714
            var barShapeStyle = barShape.style;
K
kener 已提交
715
            barShape.highlightStyle.color = barShape.highlightStyle.color
K
Kener 已提交
716 717 718
                            || (typeof barShapeStyle.color === 'string'
                                ? zrColor.lift(barShapeStyle.color, -0.3)
                                : barShapeStyle.color
K
kener 已提交
719
                               );
K
kener 已提交
720
            //亚像素优化
K
Kener 已提交
721 722 723 724
            barShapeStyle.x = Math.floor(barShapeStyle.x);
            barShapeStyle.y = Math.floor(barShapeStyle.y);
            barShapeStyle.height = Math.ceil(barShapeStyle.height);
            barShapeStyle.width = Math.ceil(barShapeStyle.width);
K
kener 已提交
725
            // 考虑线宽的显示优化
K
kener 已提交
726
            if (normalBorderWidth > 0
K
Kener 已提交
727 728
                && barShapeStyle.height > normalBorderWidth
                && barShapeStyle.width > normalBorderWidth
K
kener 已提交
729
            ) {
K
Kener 已提交
730 731 732 733
                barShapeStyle.y += normalBorderWidth / 2;
                barShapeStyle.height -= normalBorderWidth;
                barShapeStyle.x += normalBorderWidth / 2;
                barShapeStyle.width -= normalBorderWidth;
K
kener 已提交
734 735
            }
            else {
K
kener 已提交
736
                // 太小了或者线宽小于0,废了边线
K
Kener 已提交
737
                barShapeStyle.brushType = 'fill';
K
kener 已提交
738 739
            }
            
K
kener 已提交
740
            barShape.highlightStyle.textColor = barShape.highlightStyle.color;
K
kener 已提交
741
            
K
kener 已提交
742
            barShape = this.addLabel(barShape, serie, data, name, orient);
K
Kener 已提交
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
            var barShapeStyleList = [                    // normal emphasis都需要检查
                barShapeStyle,
                barShape.highlightStyle
            ];
            for (var i = 0, l = barShapeStyleList.length; i < l; i++) {
                var textPosition = barShapeStyleList[i].textPosition;
                if (textPosition === 'insideLeft'
                    || textPosition === 'insideRight'
                    || textPosition === 'insideTop'
                    || textPosition === 'insideBottom'
                ) {
                    var gap = 5;
                    switch (textPosition) {
                        case 'insideLeft':
                            barShapeStyleList[i].textX = barShapeStyle.x + gap;
                            barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height / 2;
                            barShapeStyleList[i].textAlign = 'left';
                            barShapeStyleList[i].textBaseline = 'middle';
                            break;
                        case 'insideRight':
                            barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width - gap;
                            barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height / 2;
                            barShapeStyleList[i].textAlign = 'right';
                            barShapeStyleList[i].textBaseline = 'middle';
                            break;
                        case 'insideTop':
                            barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width / 2;
                            barShapeStyleList[i].textY = barShapeStyle.y + gap / 2;
                            barShapeStyleList[i].textAlign = 'center';
                            barShapeStyleList[i].textBaseline = 'top';
                            break;
                        case 'insideBottom':
                            barShapeStyleList[i].textX = barShapeStyle.x + barShapeStyle.width / 2;
                            barShapeStyleList[i].textY = barShapeStyle.y + barShapeStyle.height - gap / 2;
                            barShapeStyleList[i].textAlign = 'center';
                            barShapeStyleList[i].textBaseline = 'bottom';
                            break;
                    }
                    barShapeStyleList[i].textPosition = 'specific';
                    barShapeStyleList[i].textColor = barShapeStyleList[i].textColor || '#fff';
K
kener 已提交
783 784
                }
            }
K
Kener 已提交
785
            
K
kener 已提交
786

K
kener 已提交
787 788
            if (this.deepQuery([data, serie, this.option],'calculable')) {
                this.setCalculable(barShape);
K
kener 已提交
789 790 791 792 793 794
                barShape.draggable = true;
            }

            ecData.pack(
                barShape,
                series[seriesIndex], seriesIndex,
K
kener 已提交
795
                series[seriesIndex].data[dataIndex], dataIndex,
K
kener 已提交
796 797 798 799
                name
            );

            return barShape;
K
kener 已提交
800
        },
K
kener 已提交
801

K
kener 已提交
802
        // 位置转换
803
        getMarkCoord: function (seriesIndex, mpData) {
K
kener 已提交
804 805
            var serie = this.series[seriesIndex];
            var xMarkMap = this.xMarkMap[seriesIndex];
K
kener 已提交
806 807
            var xAxis = this.component.xAxis.getAxis(serie.xAxisIndex);
            var yAxis = this.component.yAxis.getAxis(serie.yAxisIndex);
K
kener 已提交
808 809
            var dataIndex;
            var pos;
810
            if (mpData.type
811
                && (mpData.type === 'max' || mpData.type === 'min' || mpData.type === 'average')
812 813
            ) {
                // 特殊值内置支持
K
kener 已提交
814 815 816 817
                var valueIndex = mpData.valueIndex != null 
                                 ? mpData.valueIndex 
                                 : xMarkMap.maxX0 != null 
                                   ? '1' : '';
818
                pos = [
K
kener 已提交
819 820 821 822
                    xMarkMap[mpData.type + 'X' + valueIndex],
                    xMarkMap[mpData.type + 'Y' + valueIndex],
                    xMarkMap[mpData.type + 'Line' + valueIndex],
                    xMarkMap[mpData.type + valueIndex]
823 824
                ];
            }
K
kener 已提交
825
            else if (xMarkMap.isHorizontal) {
K
kener 已提交
826
                // 横向
827
                dataIndex = typeof mpData.xAxis === 'string' && xAxis.getIndexByName
K
kener 已提交
828 829
                            ? xAxis.getIndexByName(mpData.xAxis)
                            : (mpData.xAxis || 0);
K
kener 已提交
830
                
K
kener 已提交
831
                var x = xMarkMap[dataIndex];
832
                x = x != null
K
kener 已提交
833 834 835 836 837 838
                    ? 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 已提交
839 840 841
            }
            else {
                // 纵向
842
                dataIndex = typeof mpData.yAxis === 'string' && yAxis.getIndexByName
K
kener 已提交
843 844
                            ? yAxis.getIndexByName(mpData.yAxis)
                            : (mpData.yAxis || 0);
K
kener 已提交
845
                
K
kener 已提交
846
                var y = xMarkMap[dataIndex];
847
                y = y != null
K
kener 已提交
848 849 850 851 852 853
                    ? 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 已提交
854
            }
K
kener 已提交
855
            
K
kener 已提交
856
            return pos;
K
kener 已提交
857
        },
K
kener 已提交
858
        
K
kener 已提交
859 860 861
        /**
         * 刷新
         */
862
        refresh: function (newOption) {
K
kener 已提交
863
            if (newOption) {
K
kener 已提交
864 865
                this.option = newOption;
                this.series = newOption.series;
K
kener 已提交
866
            }
K
kener 已提交
867 868
            
            this.backupShapeList();
K
kener 已提交
869 870
            this._buildShape();
        },
871 872 873 874
        
        /**
         * 动态数据增加动画 
         */
L
lang 已提交
875
        addDataAnimation: function (params, done) {
K
kener 已提交
876
            var series = this.series;
877 878 879 880 881 882 883 884 885 886 887
            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;
L
lang 已提交
888 889 890 891 892 893 894 895

            var aniCount = 0;
            function animationDone() {
                aniCount--;
                if (aniCount === 0) {
                    done && done();
                }
            }
K
kener 已提交
896 897
            for (var i = this.shapeList.length - 1; i >= 0; i--) {
                seriesIndex = ecData.get(this.shapeList[i], 'seriesIndex');
898 899
                if (aniMap[seriesIndex] && !aniMap[seriesIndex][3]) {
                    // 有数据删除才有移动的动画
900
                    if (this.shapeList[i].type === 'rectangle') {
901
                        // 主动画
K
kener 已提交
902
                        dataIndex = ecData.get(this.shapeList[i], 'dataIndex');
903
                        serie = series[seriesIndex];
K
kener 已提交
904
                        if (aniMap[seriesIndex][2] && dataIndex === serie.data.length - 1) {
905
                            // 队头加入删除末尾
K
kener 已提交
906
                            this.zr.delShape(this.shapeList[i].id);
907 908
                            continue;
                        }
K
kener 已提交
909
                        else if (!aniMap[seriesIndex][2] && dataIndex === 0) {
910
                            // 队尾加入删除头部
K
kener 已提交
911
                            this.zr.delShape(this.shapeList[i].id);
912 913
                            continue;
                        }
914
                        if (this.shapeList[i]._orient === 'horizontal') {
915
                            // 条形图
K
Kener 已提交
916
                            dy = this.component.yAxis.getAxis(serie.yAxisIndex || 0).getGap();
917 918 919 920 921
                            y = aniMap[seriesIndex][2] ? -dy : dy;
                            x = 0;
                        }
                        else {
                            // 柱形图
K
Kener 已提交
922
                            dx = this.component.xAxis.getAxis(serie.xAxisIndex || 0).getGap();
923 924 925
                            x = aniMap[seriesIndex][2] ? dx : -dx;
                            y = 0;
                        }
K
kener 已提交
926
                        this.shapeList[i].position = [0, 0];
L
lang 已提交
927 928

                        aniCount++;
K
kener 已提交
929
                        this.zr.animate(this.shapeList[i].id, '')
930
                            .when(
K
Kener 已提交
931
                                this.query(this.option, 'animationDurationUpdate'),
932
                                { position: [x, y] }
933
                            )
L
lang 已提交
934
                            .done(animationDone)
935 936 937 938
                            .start();
                    }
                }
            }
L
lang 已提交
939 940 941 942 943
            
            // 没有动画
            if (!aniCount) {
                animationDone();
            }
K
kener 已提交
944
        }
K
kener 已提交
945
    };
K
kener 已提交
946
    
K
kener 已提交
947
    zrUtil.inherits(Bar, ChartBase);
K
kener 已提交
948
    
949 950 951
    // 图表注册
    require('../chart').define('bar', Bar);
    
K
kener 已提交
952 953
    return Bar;
});