UNPKG

chartx3d

Version:

Data Visualization Chart Library

331 lines (275 loc) 11.6 kB
import { global } from 'mmvis'; import { Component, _ } from "../Component"; import { DUCK } from "../../constants"; //const Circle = Canvax.Shapes.Circle let __legend_clickEvent = null; class Legend extends Component { constructor(chart3d, opt) { super(chart3d.currCoord); this.name = "Legend"; this.type = "legend3d"; this.mode = 'checkbox'; //checkbox radio this.opt = opt; /* data的数据结构为 [ //descartes中用到的时候还会带入yAxis {name: "uv", color: "#ff8533", field: '' ...如果手动传入数据只需要前面这三个 enabled: true, ind: 0, } //外部只需要传field和fillStyle就行了 activate是内部状态 ] */ this.data = null; //一般来讲,比如柱状图折线图等,是按照传入的field来分组来设置图例的,那么legend.field都是null //但是还有一种情况就是,是按照同一个field中的数据去重后来分组的,比如散点图中sex属性的男女两个分组作为图例, //以及pie饼图中的每个数据的name字段都是作为一个图例 //那么就想要给legend主动设置一个field字段,然后legend自己从dataFrame中拿到这个field的数据来去重,然后分组做为图例 //这是一个很屌的设计 this.field = null; this.margin = { top: 10, right: 10, bottom: 10, left: 10 }; this.icon = { height: 22, width: "auto", shapeType: "circle", radius: 5, lineWidth: 1, fillStyle: "#999", onChecked: function () { }, onUnChecked: function () { } }; this.label = { textAlign: "left", textBaseline: "middle", fillStyle: "#333", //obj.color cursor: "pointer", fontSize: 14, format: function (name, info) { return name } }; //this.onChecked=function(){}; //this.onUnChecked=function(){}; this._labelColor = "#999"; // this.position = "right"; //图例所在的方向top,right,bottom,left this.direction = "h"; //横向 top,bottom --> h left,right -- >v this.position = DUCK.TOPLEFT; //left top bottom right center topLeft topRight bottomLeft bottomRight this.offsetX = 0; this.offsetY = 0; _.extend(true, this, { icon: { onChecked: function (obj) { }, onUnChecked: function (obj) { } } }, opt); this.isInit = true; } _getLegendData(opt) { var legendData = opt.data; if (legendData) { _.each(legendData, function (item, i) { item.enabled = 'enabled' in item ? item.enabled : true; item.ind = i; }); delete opt.data; } else { legendData = this._coordSystem.getLegendData(); }; return legendData || []; } layout() { let currCoord = this._coordSystem; // if (this.direction == "h") { // app.padding[this.position] += (this.height + this.margin.top + this.margin.bottom); // } else { // app.padding[this.position] += (this.width + this.margin.left + this.margin.right); // }; //default lefttop var pos = { x: currCoord.padding.left + this.margin.left, y: currCoord.padding.top + this.margin.top }; switch (this.position && this.position.toLocaleLowerCase()) { case DUCK.TOPRIGHT.toLocaleLowerCase(): pos.x = currCoord.width - this.width - currCoord.padding.right - this.margin.right; pos.y = currCoord.padding.top + this.margin.top; break; case DUCK.BOTTOMLEFT.toLocaleLowerCase(): pos.x = currCoord.padding.left + this.margin.left; pos.y = currCoord.height - currCoord.padding.bottom - this.margin.bottom - this.height; break; case DUCK.BOTTOMRIGHT.toLocaleLowerCase(): pos.x = currCoord.width - this.width - currCoord.padding.right - this.margin.right; pos.y = currCoord.height - currCoord.padding.bottom - this.margin.bottom - this.height; break; case DUCK.LEFT.toLocaleLowerCase(): pos.x = currCoord.padding.left + this.margin.left; pos.y = (currCoord.height - currCoord.padding.bottom - this.margin.bottom - this.height) * 0.5; break; case DUCK.RIGHT.toLocaleLowerCase(): pos.x = currCoord.width - this.width - currCoord.padding.right - this.margin.right; pos.y = (currCoord.height - currCoord.padding.bottom - this.margin.bottom - this.height) * 0.5; break; case DUCK.TOP.toLocaleLowerCase(): pos.x = (currCoord.width - this.width - currCoord.padding.right - this.margin.right) * 0.5; pos.y = currCoord.padding.top + this.margin.top; break; case DUCK.BOTTOM.toLocaleLowerCase(): pos.x = (currCoord.width - this.width - currCoord.padding.right - this.margin.right) * 0.5; pos.y = currCoord.height - currCoord.padding.bottom - this.margin.bottom - this.height; break; case DUCK.CENTER.toLocaleLowerCase(): pos.x = (currCoord.width - this.width - currCoord.padding.right - this.margin.right) * 0.5; pos.y = (currCoord.height - currCoord.padding.bottom - this.margin.bottom - this.height) * 0.5; } pos.x += this.offsetX; pos.y += this.offsetY; this.pos = pos; } draw() { this.widget(); this.layout(); this.group.position.set(this.pos.x, this.pos.y, 0); } widget() { var me = this; let app = this._root.app; let currCoord = this._coordSystem if (this.isInit) { this.isInit = false; this.data = this._getLegendData(this.opt); } var viewWidth = currCoord.width - currCoord.padding.left - currCoord.padding.right - this.margin.left - this.margin.right; var viewHeight = currCoord.height - currCoord.padding.top - currCoord.padding.bottom - this.margin.top - this.margin.bottom; this.dispose(); var maxItemWidth = 0; var width = 0, height = 0; var x = 0, y = 0; var rows = 1; var isOver = false; //如果legend过多 __legend_clickEvent = this._getInfoHandler.bind(this); _.each(this.data, function (obj, i) { if (isOver) return; var _icon = app.createCirclePlane(me.icon.radius, { fillStyle: !obj.enabled ? "#ccc" : (obj.color || me._labelColor) }); _icon.name = Legend._legend_prefix + "icon_" + i; _icon.position.set(me.icon.radius, me.icon.height / 3, 0); _icon.on("click", __legend_clickEvent); let txtArr = app.creatSpriteText(me.label.format(obj.name || obj.label, obj), { fillStyle: me.label.fillStyle, fontSize: me.label.fontSize, textAlign: me.label.textAlign, textBaseline: me.label.textBaseline, }); let txt = txtArr[0]; txt.name = Legend._legend_prefix + "txt_" + i; txt.on("click", __legend_clickEvent); var txtW = txt.userData.size && txt.userData.size[0]; var itemW = txtW + me.icon.radius * 3 + 20; txt.position.set(me.icon.radius * 3 + txtW * 0.5, me.icon.height / 3, 0); maxItemWidth = Math.max(maxItemWidth, itemW); var spItemC = { height: me.icon.height }; if (me.direction == "v") { if (y + me.icon.height > viewHeight) { if (x > viewWidth) { isOver = true; return; }; x += maxItemWidth; y = 0; }; spItemC.x = x; spItemC.y = y; y += me.icon.height; height = Math.max(height, y); } else { //横向排布 if (x + itemW > viewWidth) { if (me.icon.height * (rows + 1) > viewHeight) { isOver = true; return; } x = 0; rows++; }; spItemC.x = x; spItemC.y = me.icon.height * (rows - 1); x += itemW; width = Math.max(width, x); }; // var sprite = new Canvax.Display.Sprite({ // id: "legend_field_" + i, // context: spItemC // }); let _group = app.addGroup({ name: Legend._legend_prefix + i }); _group.position.set(spItemC.x, spItemC.y, 0); _group.userData.info = obj; _group.add(_icon); _group.add(txt); me.group.add(_group); // sprite.context.width = itemW; // me.sprite.addChild(sprite); // sprite.on("click", function (e) { // //只有一个field的时候,不支持取消 // if (_.filter(me.data, function (obj) { return obj.enabled }).length == 1) { // if (obj.enabled) { // return; // } // }; // obj.enabled = !obj.enabled; // _icon.context.fillStyle = !obj.enabled ? "#ccc" : (obj.color || me._labelColor); if (obj.enabled) { me.icon.onChecked(obj); } else { me.icon.onUnChecked(obj); } // }); }); if (this.direction == "h") { me.width = width; me.height = me.icon.height * rows; } else { me.width = x + maxItemWidth; me.height = height; } this._root.labelGroup.add(this.group); //me.width = me.sprite.context.width = width; //me.height = me.sprite.context.height = height; } _getInfoHandler(e) { let info = e.target.parent.userData.info if (this.mode === 'radio') { _.each(this.data, item => { if (item.ind !== info.ind) { item.enabled = false; } else { item.enabled = true; } }) } if (this.mode === 'checkbox') { info.enabled = !info.enabled; } if (info) { // this._root.fire({ type: 'redraw', data: info }); this._root.fire({ type: 'legendchange', data: info }) } } dispose(group) { group = group || this.group; this.group.traverse(obj => { if (obj.name.indexOf(Legend._legend_prefix) !== -1) { obj.off('click', __legend_clickEvent); } }) super.dispose(group); } } Legend._legend_prefix = "legend_field_"; global.registerComponent(Legend, 'legend', 3); export default Legend;