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

来源:互联网 发布:手机淘宝购物车 编辑:程序博客网 时间:2024/05/16 15:35

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 可以分行或分列显示的方法。

倒是在 legendItem 的参考文档中,找到了 updatePosition( [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 , 速度自然快不了。



0 0