[Ext JS 4] Extjs 图表 Legend(图例)的分行与分列显示

Extjs 中的Chart 的legend.

Legend, 翻译过来的意思是图例。

在Extjs 的Chart 中, 到底代表什么呢? 直接看这张图:


右边红色框起来的部分就是Legend 了。

在 Extjs Chart 的定义中, 可以通过配置 legend 的配置值(configs)来设置Legend 显示的位置和样式:

position 配置显示的位置:可以设置的值有 "top","bottom", "left", "right", or "float"。

其他还可以设置图例显示的文字、图的样式等等, 详细可以参见 Ext.chart.Legend 的参考文档。

具体的设置类似:




Legend 如何分列或者分行 -- 历程

不管居于什么位置,Extjs  Legend 默认的显示方式都是单行或是当列。(根据位置配置的差异)

查阅Legend 的参考文档的所有配置,属性和方法都找不到, 如何将Legend 可以分行或分列显示的方法。

倒是在 ( [relativeTo] )的方法。

Legend是整个图例, LegendItem 应该就是里面的每一项图例了。

于是产生了一种解法:

1. 是否可以在合适的时候, 设置legendItem 的位置

首先第一个问题, 什么是合适的时候? 是否可以在 afterRender 事件中,

但是实际测试发现, 在Chart 的afterRender 中, 图例并没有产生出来。 这个Form , Grid 的机制看上去不太一样。

没办法,只能先整一个按钮, 通过点击来看看这种解法是否可行。

1)  得到Chart 的legend

	var chart = Ext.getCmp("testChart");
	var legend = chart.legend;
我的Chart Id 是: testChart

2)  得到ChartItems

var legendItems = legend.items;

3)  循环以上数组,设置各Item 的位置, 位置是自己计算出来的(x,y 坐标)

legendItem.updatePosition({x:xPos,y:yPos});
位置的确是改变了。


到此, 问题并没有结束,

legend 上的Item 是可以进行点击的, 而且点击之后,相应的图是可以进行显示和隐藏的切换的。

使用以上方式, 发现点击之后, 又恢复原状了。

于是又产生了一种想法,

2. 是否可以给LegendItem 添加, click 事件, 在click 中重新计算位置并更新/

legendItem.on("click",function(){
	legendItem.updatePosition({x:xPos,y:yPos});
});

这样的写法是否繁杂不说, 会发现一个问题:

就是legendItem 必须要点两次以上才能触发上面的function.

而且Dubug 发现, 此时Legend item 有两个click 事件,一个是原生的,一个是后来加的。

这样的方式肯定会存在问题。

所以以上的方式只能宣告失败。


Legend分列或者分行

看来只能从Extjs 的源码层级考虑了。

Extjs Chart 的最底层的技术无非是 SVG, VML.

关于此的详细介绍可以参考:

[Web Chart系列之一]Web端图形绘制SVG,VML, HTML5 Canvas 技术比较

而Extjs 的Chart 又是基于raphael 框架之上。  关于raphael 的详细,可以参考官方网站:

http://raphaeljs.com/

思考的历程就统统省去,直接给出结论就是: 重写  Ext.chart.LegendItem 和 Ext.chart.Legend 的 updatePosition方法:

<!--Add by oscar999-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> New Document </TITLE>
<META NAME="Author" CONTENT="oscar999">
<script>
</script>
</HEAD>

<BODY>

</BODY>
</HTML>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="../ext-all-debug.js"></script>
<link href="../resources/ext-theme-neptune/ext-theme-neptune-all.css" rel="stylesheet" type="text/css" />


<script>
Ext.chart.LegendItem.override({
    updateSpecPosition: function(positionTo) {
        var me = this,       
            items = me.items,
            ln = items.length,
            i = 0,
            item; 
		var posX = positionTo.x;
		var posY = positionTo.y;
        for (; i < ln; i++) {
            item = items[i];
            switch (item.type) {
                case ‘text‘:
                    item.setAttributes({
                        x: 20 + posX,
                        y: posY
                    }, true);
                    break;
                case ‘rect‘:
                    item.setAttributes({
                        translate: {
                            x: posX,
                            y: posY - 6
                        }
                    }, true);
                    break;
                default:
                    item.setAttributes({
                        translate: {
                            x: posX,
                            y: posY
                        }
                    }, true);
            }
        }
    }
});
Ext.chart.Legend.override({
 updatePosition: function() {
        var me = this,
            items = me.items,
            pos, i, l, bbox;

        if (me.isDisplayed()) {
            
            pos = me.calcPosition();
            
            me.x = pos.x;
            me.y = pos.y;

            //items[i].updatePosition({x:100,y:100});
            
            var posX = me.x;
            var posY = me.y;
            for (i = 0, l = items.length; i < l; i++) {
            	posX = me.x;
            	posY = me.y;
            	//items[i].updatePosition();               
            	if(i%2>0)
            	{
            		posX += 80;
            	}
                
            	posY += parseInt(i/2)*30;
            	
            	items[i].updateSpecPosition({x:posX,y:posY});
            }

            bbox = me.getBBox();

            
            
            
            
            
            if (isNaN(bbox.width) || isNaN(bbox.height)) {
                if (me.boxSprite) {
                    me.boxSprite.hide(true);
                }
            }
            else {
                if (!me.boxSprite) {
                    me.createBox();
                }
                
                
                me.boxSprite.setAttributes(bbox, true);
                me.boxSprite.show(true);
            }
        }
    }
});

</script>


<script>

Ext.onReady(function(){
	var store = Ext.create(‘Ext.data.JsonStore‘, {
        fields: [‘year‘, ‘comedy‘, ‘action‘, ‘drama‘, ‘thriller‘],
        data: [
                {year: 2005, comedy: 34000000, action: 23890000, drama: 18450000, thriller: 20060000},
                {year: 2006, comedy: 56703000, action: 38900000, drama: 12650000, thriller: 21000000},
                {year: 2007, comedy: 42100000, action: 50410000, drama: 25780000, thriller: 23040000},
                {year: 2008, comedy: 38910000, action: 56070000, drama: 24810000, thriller: 26940000}
              ]
    });

    var chart = Ext.create(‘Ext.chart.Chart‘,{
            id:‘testChart‘,
    		animate: true,
            shadow: true,
            store: store,
            legend: {
                //position: ‘right‘,
                //position:‘float‘,
                position: ‘bottom‘,
                boxStroke: ‘#FFF‘,
                legendCol:2,
                update:false
            },
            axes: [{
                type: ‘Numeric‘,
                position: ‘bottom‘,
                fields: [‘comedy‘, ‘action‘, ‘drama‘, ‘thriller‘],
                title: false,
                grid: true,
                label: {
                    renderer: function(v) {
                        return String(v).replace(/(.)00000$/, ‘.$1M‘);
                    }
                }
            }, {
                type: ‘Category‘,
                position: ‘left‘,
                fields: [‘year‘],
                title: false
            }],
            series: [{
                type: ‘bar‘,
                axis: ‘bottom‘,
                gutter: 80,
                xField: ‘year‘,
                yField: [‘comedy‘, ‘action‘, ‘drama‘, ‘thriller‘],
                stacked: true,
                tips: {
                    trackMouse: true,
                    width: 65,
                    height: 28,
                    renderer: function(storeItem, item) {
                        this.setTitle(String(item.value[1] / 1000000) + ‘M‘);
                    }
                }
            }]
        });


    var panel1 = Ext.create(‘widget.panel‘, {
        width: 800,
        height: 400,
        title: ‘Stacked Bar Chart - Movies by Genre‘,
        renderTo: Ext.getBody(),
        layout: ‘fit‘,
        tbar: [{
            text: ‘Save Chart‘,
            handler: function() {
                Ext.MessageBox.confirm(‘Confirm Download‘, ‘Would you like to download the chart as an image?‘, function(choice){
                    if(choice == ‘yes‘){
                        chart.save({
                            type: ‘image/png‘
                        });
                    }
                });
            }
        }],
        items: chart
    });
});
	

</script>
</head>
<body>
<div id="my-div"></div>
<div id="user-div" class="applicantItem" style="display:none"></div>
</body>
</html>

注意:

1. 以上并不是一个完整与完美的代码, 只是说明可以使用这种方式进行的POC的代码

2. 排列的位置, 需要自行去运算出来,多行, 还是多列?




效果与总结

最后,给出一下笔者实现的效果


当然, 如果LegendItem 比较少的话, 分行与分列并没有什么意义。

比较适用的场景是: legendItem 很多,超过20 甚至30, 大大影响了页面的感官。

不过实际开发中,这种状况应该比较少。

如果实在有这种状况, 就要从需求开始就的慎重考量了,尽量避免出现这种需求, 因为即使开发出来的, 针对如此多的Series , 速度自然快不了。



[Ext JS 4] Extjs 图表 Legend(图例)的分行与分列显示,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。