| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 | define('echarts/chart/funnel', [    'require',    './base',    'zrender/shape/Text',    'zrender/shape/Line',    'zrender/shape/Polygon',    '../config',    '../util/ecData',    '../util/number',    'zrender/tool/util',    'zrender/tool/color',    'zrender/tool/area',    '../chart'], function (require) {    var ChartBase = require('./base');    var TextShape = require('zrender/shape/Text');    var LineShape = require('zrender/shape/Line');    var PolygonShape = require('zrender/shape/Polygon');    var ecConfig = require('../config');    ecConfig.funnel = {        zlevel: 0,        z: 2,        clickable: true,        legendHoverLink: true,        x: 80,        y: 60,        x2: 80,        y2: 60,        min: 0,        max: 100,        minSize: '0%',        maxSize: '100%',        sort: 'descending',        gap: 0,        funnelAlign: 'center',        itemStyle: {            normal: {                borderColor: '#fff',                borderWidth: 1,                label: {                    show: true,                    position: 'outer'                },                labelLine: {                    show: true,                    length: 10,                    lineStyle: {                        width: 1,                        type: 'solid'                    }                }            },            emphasis: {                borderColor: 'rgba(0,0,0,0)',                borderWidth: 1,                label: { show: true },                labelLine: { show: true }            }        }    };    var ecData = require('../util/ecData');    var number = require('../util/number');    var zrUtil = require('zrender/tool/util');    var zrColor = require('zrender/tool/color');    var zrArea = require('zrender/tool/area');    function Funnel(ecTheme, messageCenter, zr, option, myChart) {        ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);        this.refresh(option);    }    Funnel.prototype = {        type: ecConfig.CHART_TYPE_FUNNEL,        _buildShape: function () {            var series = this.series;            var legend = this.component.legend;            this._paramsMap = {};            this._selected = {};            this.selectedMap = {};            var serieName;            for (var i = 0, l = series.length; i < l; i++) {                if (series[i].type === ecConfig.CHART_TYPE_FUNNEL) {                    series[i] = this.reformOption(series[i]);                    this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;                    serieName = series[i].name || '';                    this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;                    if (!this.selectedMap[serieName]) {                        continue;                    }                    this._buildSingleFunnel(i);                    this.buildMark(i);                }            }            this.addShapeList();        },        _buildSingleFunnel: function (seriesIndex) {            var legend = this.component.legend;            var serie = this.series[seriesIndex];            var data = this._mapData(seriesIndex);            var location = this._getLocation(seriesIndex);            this._paramsMap[seriesIndex] = {                location: location,                data: data            };            var itemName;            var total = 0;            var selectedData = [];            for (var i = 0, l = data.length; i < l; i++) {                itemName = data[i].name;                this.selectedMap[itemName] = legend ? legend.isSelected(itemName) : true;                if (this.selectedMap[itemName] && !isNaN(data[i].value)) {                    selectedData.push(data[i]);                    total++;                }            }            if (total === 0) {                return;            }            var funnelCase = this._buildFunnelCase(seriesIndex);            var align = serie.funnelAlign;            var gap = serie.gap;            var height = total > 1 ? (location.height - (total - 1) * gap) / total : location.height;            var width;            var lastY = location.y;            var lastWidth = serie.sort === 'descending' ? this._getItemWidth(seriesIndex, selectedData[0].value) : number.parsePercent(serie.minSize, location.width);            var next = serie.sort === 'descending' ? 1 : 0;            var centerX = location.centerX;            var pointList = [];            var x;            var polygon;            var lastPolygon;            for (var i = 0, l = selectedData.length; i < l; i++) {                itemName = selectedData[i].name;                if (this.selectedMap[itemName] && !isNaN(selectedData[i].value)) {                    width = i <= l - 2 ? this._getItemWidth(seriesIndex, selectedData[i + next].value) : serie.sort === 'descending' ? number.parsePercent(serie.minSize, location.width) : number.parsePercent(serie.maxSize, location.width);                    switch (align) {                    case 'left':                        x = location.x;                        break;                    case 'right':                        x = location.x + location.width - lastWidth;                        break;                    default:                        x = centerX - lastWidth / 2;                    }                    polygon = this._buildItem(seriesIndex, selectedData[i]._index, legend ? legend.getColor(itemName) : this.zr.getColor(selectedData[i]._index), x, lastY, lastWidth, width, height, align);                    lastY += height + gap;                    lastPolygon = polygon.style.pointList;                    pointList.unshift([                        lastPolygon[0][0] - 10,                        lastPolygon[0][1]                    ]);                    pointList.push([                        lastPolygon[1][0] + 10,                        lastPolygon[1][1]                    ]);                    if (i === 0) {                        if (lastWidth === 0) {                            lastPolygon = pointList.pop();                            align == 'center' && (pointList[0][0] += 10);                            align == 'right' && (pointList[0][0] = lastPolygon[0]);                            pointList[0][1] -= align == 'center' ? 10 : 15;                            if (l == 1) {                                lastPolygon = polygon.style.pointList;                            }                        } else {                            pointList[pointList.length - 1][1] -= 5;                            pointList[0][1] -= 5;                        }                    }                    lastWidth = width;                }            }            if (funnelCase) {                pointList.unshift([                    lastPolygon[3][0] - 10,                    lastPolygon[3][1]                ]);                pointList.push([                    lastPolygon[2][0] + 10,                    lastPolygon[2][1]                ]);                if (lastWidth === 0) {                    lastPolygon = pointList.pop();                    align == 'center' && (pointList[0][0] += 10);                    align == 'right' && (pointList[0][0] = lastPolygon[0]);                    pointList[0][1] += align == 'center' ? 10 : 15;                } else {                    pointList[pointList.length - 1][1] += 5;                    pointList[0][1] += 5;                }                funnelCase.style.pointList = pointList;            }        },        _buildFunnelCase: function (seriesIndex) {            var serie = this.series[seriesIndex];            if (this.deepQuery([                    serie,                    this.option                ], 'calculable')) {                var location = this._paramsMap[seriesIndex].location;                var gap = 10;                var funnelCase = {                    hoverable: false,                    style: {                        pointListd: [                            [                                location.x - gap,                                location.y - gap                            ],                            [                                location.x + location.width + gap,                                location.y - gap                            ],                            [                                location.x + location.width + gap,                                location.y + location.height + gap                            ],                            [                                location.x - gap,                                location.y + location.height + gap                            ]                        ],                        brushType: 'stroke',                        lineWidth: 1,                        strokeColor: serie.calculableHolderColor || this.ecTheme.calculableHolderColor || ecConfig.calculableHolderColor                    }                };                ecData.pack(funnelCase, serie, seriesIndex, undefined, -1);                this.setCalculable(funnelCase);                funnelCase = new PolygonShape(funnelCase);                this.shapeList.push(funnelCase);                return funnelCase;            }        },        _getLocation: function (seriesIndex) {            var gridOption = this.series[seriesIndex];            var zrWidth = this.zr.getWidth();            var zrHeight = this.zr.getHeight();            var x = this.parsePercent(gridOption.x, zrWidth);            var y = this.parsePercent(gridOption.y, zrHeight);            var width = gridOption.width == null ? zrWidth - x - this.parsePercent(gridOption.x2, zrWidth) : this.parsePercent(gridOption.width, zrWidth);            return {                x: x,                y: y,                width: width,                height: gridOption.height == null ? zrHeight - y - this.parsePercent(gridOption.y2, zrHeight) : this.parsePercent(gridOption.height, zrHeight),                centerX: x + width / 2            };        },        _mapData: function (seriesIndex) {            var serie = this.series[seriesIndex];            var funnelData = zrUtil.clone(serie.data);            for (var i = 0, l = funnelData.length; i < l; i++) {                funnelData[i]._index = i;            }            function numDescending(a, b) {                if (a.value === '-') {                    return 1;                } else if (b.value === '-') {                    return -1;                }                return b.value - a.value;            }            function numAscending(a, b) {                return -numDescending(a, b);            }            if (serie.sort != 'none') {                funnelData.sort(serie.sort === 'descending' ? numDescending : numAscending);            }            return funnelData;        },        _buildItem: function (seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align) {            var series = this.series;            var serie = series[seriesIndex];            var data = serie.data[dataIndex];            var polygon = this.getPolygon(seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align);            ecData.pack(polygon, series[seriesIndex], seriesIndex, series[seriesIndex].data[dataIndex], dataIndex, series[seriesIndex].data[dataIndex].name);            this.shapeList.push(polygon);            var label = this.getLabel(seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align);            ecData.pack(label, series[seriesIndex], seriesIndex, series[seriesIndex].data[dataIndex], dataIndex, series[seriesIndex].data[dataIndex].name);            this.shapeList.push(label);            if (!this._needLabel(serie, data, false)) {                label.invisible = true;            }            var labelLine = this.getLabelLine(seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align);            this.shapeList.push(labelLine);            if (!this._needLabelLine(serie, data, false)) {                labelLine.invisible = true;            }            var polygonHoverConnect = [];            var labelHoverConnect = [];            if (this._needLabelLine(serie, data, true)) {                polygonHoverConnect.push(labelLine.id);                labelHoverConnect.push(labelLine.id);            }            if (this._needLabel(serie, data, true)) {                polygonHoverConnect.push(label.id);                labelHoverConnect.push(polygon.id);            }            polygon.hoverConnect = polygonHoverConnect;            label.hoverConnect = labelHoverConnect;            return polygon;        },        _getItemWidth: function (seriesIndex, value) {            var serie = this.series[seriesIndex];            var location = this._paramsMap[seriesIndex].location;            var min = serie.min;            var max = serie.max;            var minSize = number.parsePercent(serie.minSize, location.width);            var maxSize = number.parsePercent(serie.maxSize, location.width);            return (value - min) * (maxSize - minSize) / (max - min) + minSize;        },        getPolygon: function (seriesIndex, dataIndex, defaultColor, xLT, y, topWidth, bottomWidth, height, align) {            var serie = this.series[seriesIndex];            var data = serie.data[dataIndex];            var queryTarget = [                data,                serie            ];            var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};            var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};            var normalColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data) || defaultColor;            var emphasisColor = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data) || (typeof normalColor === 'string' ? zrColor.lift(normalColor, -0.2) : normalColor);            var xLB;            switch (align) {            case 'left':                xLB = xLT;                break;            case 'right':                xLB = xLT + (topWidth - bottomWidth);                break;            default:                xLB = xLT + (topWidth - bottomWidth) / 2;                break;            }            var polygon = {                zlevel: this.getZlevelBase(),                z: this.getZBase(),                clickable: this.deepQuery(queryTarget, 'clickable'),                style: {                    pointList: [                        [                            xLT,                            y                        ],                        [                            xLT + topWidth,                            y                        ],                        [                            xLB + bottomWidth,                            y + height                        ],                        [                            xLB,                            y + height                        ]                    ],                    brushType: 'both',                    color: normalColor,                    lineWidth: normal.borderWidth,                    strokeColor: normal.borderColor                },                highlightStyle: {                    color: emphasisColor,                    lineWidth: emphasis.borderWidth,                    strokeColor: emphasis.borderColor                }            };            if (this.deepQuery([                    data,                    serie,                    this.option                ], 'calculable')) {                this.setCalculable(polygon);                polygon.draggable = true;            }            return new PolygonShape(polygon);        },        getLabel: function (seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align) {            var serie = this.series[seriesIndex];            var data = serie.data[dataIndex];            var location = this._paramsMap[seriesIndex].location;            var itemStyle = zrUtil.merge(zrUtil.clone(data.itemStyle) || {}, serie.itemStyle);            var status = 'normal';            var labelControl = itemStyle[status].label;            var textStyle = labelControl.textStyle || {};            var lineLength = itemStyle[status].labelLine.length;            var text = this.getLabelText(seriesIndex, dataIndex, status);            var textFont = this.getFont(textStyle);            var textAlign;            var textColor = defaultColor;            labelControl.position = labelControl.position || itemStyle.normal.label.position;            if (labelControl.position === 'inner' || labelControl.position === 'inside' || labelControl.position === 'center') {                textAlign = align;                textColor = Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont) ? '#fff' : zrColor.reverse(defaultColor);            } else if (labelControl.position === 'left') {                textAlign = 'right';            } else {                textAlign = 'left';            }            var textShape = {                zlevel: this.getZlevelBase(),                z: this.getZBase() + 1,                style: {                    x: this._getLabelPoint(labelControl.position, x, location, topWidth, bottomWidth, lineLength, align),                    y: y + height / 2,                    color: textStyle.color || textColor,                    text: text,                    textAlign: textStyle.align || textAlign,                    textBaseline: textStyle.baseline || 'middle',                    textFont: textFont                }            };            status = 'emphasis';            labelControl = itemStyle[status].label || labelControl;            textStyle = labelControl.textStyle || textStyle;            lineLength = itemStyle[status].labelLine.length || lineLength;            labelControl.position = labelControl.position || itemStyle.normal.label.position;            text = this.getLabelText(seriesIndex, dataIndex, status);            textFont = this.getFont(textStyle);            textColor = defaultColor;            if (labelControl.position === 'inner' || labelControl.position === 'inside' || labelControl.position === 'center') {                textAlign = align;                textColor = Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont) ? '#fff' : zrColor.reverse(defaultColor);            } else if (labelControl.position === 'left') {                textAlign = 'right';            } else {                textAlign = 'left';            }            textShape.highlightStyle = {                x: this._getLabelPoint(labelControl.position, x, location, topWidth, bottomWidth, lineLength, align),                color: textStyle.color || textColor,                text: text,                textAlign: textStyle.align || textAlign,                textFont: textFont,                brushType: 'fill'            };            return new TextShape(textShape);        },        getLabelText: function (seriesIndex, dataIndex, status) {            var series = this.series;            var serie = series[seriesIndex];            var data = serie.data[dataIndex];            var formatter = this.deepQuery([                data,                serie            ], 'itemStyle.' + status + '.label.formatter');            if (formatter) {                if (typeof formatter === 'function') {                    return formatter.call(this.myChart, {                        seriesIndex: seriesIndex,                        seriesName: serie.name || '',                        series: serie,                        dataIndex: dataIndex,                        data: data,                        name: data.name,                        value: data.value                    });                } else if (typeof formatter === 'string') {                    formatter = formatter.replace('{a}', '{a0}').replace('{b}', '{b0}').replace('{c}', '{c0}').replace('{a0}', serie.name).replace('{b0}', data.name).replace('{c0}', data.value);                    return formatter;                }            } else {                return data.name;            }        },        getLabelLine: function (seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align) {            var serie = this.series[seriesIndex];            var data = serie.data[dataIndex];            var location = this._paramsMap[seriesIndex].location;            var itemStyle = zrUtil.merge(zrUtil.clone(data.itemStyle) || {}, serie.itemStyle);            var status = 'normal';            var labelLineControl = itemStyle[status].labelLine;            var lineLength = itemStyle[status].labelLine.length;            var lineStyle = labelLineControl.lineStyle || {};            var labelControl = itemStyle[status].label;            labelControl.position = labelControl.position || itemStyle.normal.label.position;            var lineShape = {                zlevel: this.getZlevelBase(),                z: this.getZBase() + 1,                hoverable: false,                style: {                    xStart: this._getLabelLineStartPoint(x, location, topWidth, bottomWidth, align),                    yStart: y + height / 2,                    xEnd: this._getLabelPoint(labelControl.position, x, location, topWidth, bottomWidth, lineLength, align),                    yEnd: y + height / 2,                    strokeColor: lineStyle.color || defaultColor,                    lineType: lineStyle.type,                    lineWidth: lineStyle.width                }            };            status = 'emphasis';            labelLineControl = itemStyle[status].labelLine || labelLineControl;            lineLength = itemStyle[status].labelLine.length || lineLength;            lineStyle = labelLineControl.lineStyle || lineStyle;            labelControl = itemStyle[status].label || labelControl;            labelControl.position = labelControl.position;            lineShape.highlightStyle = {                xEnd: this._getLabelPoint(labelControl.position, x, location, topWidth, bottomWidth, lineLength, align),                strokeColor: lineStyle.color || defaultColor,                lineType: lineStyle.type,                lineWidth: lineStyle.width            };            return new LineShape(lineShape);        },        _getLabelPoint: function (position, x, location, topWidth, bottomWidth, lineLength, align) {            position = position === 'inner' || position === 'inside' ? 'center' : position;            switch (position) {            case 'center':                return align == 'center' ? x + topWidth / 2 : align == 'left' ? x + 10 : x + topWidth - 10;            case 'left':                if (lineLength === 'auto') {                    return location.x - 10;                } else {                    return align == 'center' ? location.centerX - Math.max(topWidth, bottomWidth) / 2 - lineLength : align == 'right' ? x - (topWidth < bottomWidth ? bottomWidth - topWidth : 0) - lineLength : location.x - lineLength;                }                break;            default:                if (lineLength === 'auto') {                    return location.x + location.width + 10;                } else {                    return align == 'center' ? location.centerX + Math.max(topWidth, bottomWidth) / 2 + lineLength : align == 'right' ? location.x + location.width + lineLength : x + Math.max(topWidth, bottomWidth) + lineLength;                }            }        },        _getLabelLineStartPoint: function (x, location, topWidth, bottomWidth, align) {            return align == 'center' ? location.centerX : topWidth < bottomWidth ? x + Math.min(topWidth, bottomWidth) / 2 : x + Math.max(topWidth, bottomWidth) / 2;        },        _needLabel: function (serie, data, isEmphasis) {            return this.deepQuery([                data,                serie            ], 'itemStyle.' + (isEmphasis ? 'emphasis' : 'normal') + '.label.show');        },        _needLabelLine: function (serie, data, isEmphasis) {            return this.deepQuery([                data,                serie            ], 'itemStyle.' + (isEmphasis ? 'emphasis' : 'normal') + '.labelLine.show');        },        refresh: function (newOption) {            if (newOption) {                this.option = newOption;                this.series = newOption.series;            }            this.backupShapeList();            this._buildShape();        }    };    zrUtil.inherits(Funnel, ChartBase);    require('../chart').define('funnel', Funnel);    return Funnel;});
 |