vue封装G2图表
real_忘川 人气:0vue封装G2图表
<template> <div id="id"></div> </template>
<script> import G2 from '@antv/g2' import { DataSet } from '@antv/data-set' export default { name: 'pie', data () { return { chart: null }; }, props:{ gtwopiedata:{ type: Array }, // gtwopiecolor:{ // type: Array // }, }, methods:{ g2pie(){ if(this.chart){ // 如果存在的话就删除图表再重新生成 this.chart.destroy() } var startAngle = - Math.PI / 2 - Math.PI / 4; var data = this.gtwopiedata.data; var ds = new DataSet(); var dv = ds.createView().source(data); dv.transform({ type: 'percent', field: 'value', dimension: 'type', as: 'percent' }); this.chart = new G2.Chart({ container: 'id', forceFit: true, height: this.gtwopiedata.height, padding: 'auto' }); this.chart.source(dv); this.chart.legend(false); this.chart.coord('theta', { radius: 0.75, innerRadius: 0.5, startAngle: startAngle, endAngle: startAngle + Math.PI * 2 }); this.chart.intervalStack().position('value').color('type', this.gtwopiedata.color).opacity(1).label('percent', { offset: -20, textStyle: { fill: 'white', fontSize: 12, shadowBlur: 2, shadowColor: 'rgba(0, 0, 0, .45)' }, formatter: function formatter(val) { return parseInt(val * 100) + '%'; } }); this.chart.guide().html({ position: ['50%', '50%'], html: '<div class="g2-guide-html"><p class="title">'+this.gtwopiedata.title+'</p></div>' }); this.chart.render(); //draw label var OFFSET = 20; var APPEND_OFFSET = 50; var LINEHEIGHT = 60; var coord = this.chart.get('coord'); // 获取坐标系对象 var center = coord.center; // 极坐标圆心坐标 var r = coord.radius; // 极坐标半径 var canvas = this.chart.get('canvas'); var canvasWidth = this.chart.get('width'); var canvasHeight = this.chart.get('height'); var labelGroup = canvas.addGroup(); var labels = []; // addPieLabel(this.chart); var halves = [[], []]; var data = dv.rows; var angle = startAngle; for (var i = 0; i < data.length; i++) { var percent = data[i].percent; var targetAngle = angle + Math.PI * 2 * percent; var middleAngle = angle + (targetAngle - angle) / 2; angle = targetAngle; var edgePoint = this.getEndPoint(center, middleAngle, r); var routerPoint = this.getEndPoint(center, middleAngle, r + OFFSET); //label var label = { _anchor: edgePoint, _router: routerPoint, _data: data[i], x: routerPoint.x, y: routerPoint.y, r: r + OFFSET, fill: '#bfbfbf' }; // 判断文本的方向 if (edgePoint.x < center.x) { label._side = 'left'; halves[0].push(label); } else { label._side = 'right'; halves[1].push(label); } } // end of for var maxCountForOneSide = parseInt(canvasHeight / LINEHEIGHT, 10); halves.forEach(function(half, index) { // step 2: reduce labels if (half.length > maxCountForOneSide) { half.sort(function(a, b) { return b._percent - a._percent; }); half.splice(maxCountForOneSide, half.length - maxCountForOneSide); } // step 3: distribute position (x and y) half.sort(function(a, b) { return a.y - b.y; }); // antiCollision(half, index); var startY = center.y - r - OFFSET - LINEHEIGHT; var overlapping = true; var totalH = canvasHeight; var i = void 0; var maxY = 0; var minY = Number.MIN_VALUE; var boxes = half.map(function(label) { var labelY = label.y; if (labelY > maxY) { maxY = labelY; } if (labelY < minY) { minY = labelY; } return { size: LINEHEIGHT, targets: [labelY - startY] }; }); if (maxY - startY > totalH) { totalH = maxY - startY; } while (overlapping) { boxes.forEach(function(box) { var target = (Math.min.apply(minY, box.targets) + Math.max.apply(minY, box.targets)) / 2; box.pos = Math.min(Math.max(minY, target - box.size / 2), totalH - box.size); }); // detect overlapping and join boxes overlapping = false; i = boxes.length; while (i--) { if (i > 0) { var previousBox = boxes[i - 1]; var box = boxes[i]; if (previousBox.pos + previousBox.size > box.pos) { // overlapping previousBox.size += box.size; previousBox.targets = previousBox.targets.concat(box.targets); // overflow, shift up if (previousBox.pos + previousBox.size > totalH) { previousBox.pos = totalH - previousBox.size; } boxes.splice(i, 1); // removing box overlapping = true; } } } } // step 4: normalize y and adjust x i = 0; boxes.forEach(function(b) { var posInCompositeBox = startY; // middle of the label b.targets.forEach(function() { half[i].y = b.pos + posInCompositeBox + LINEHEIGHT / 2; posInCompositeBox += LINEHEIGHT; i++; }); }); // (x - cx)^2 + (y - cy)^2 = totalR^2 half.forEach(function(label) { var rPow2 = label.r * label.r; var dyPow2 = Math.pow(Math.abs(label.y - center.y), 2); if (rPow2 < dyPow2) { label.x = center.x; } else { var dx = Math.sqrt(rPow2 - dyPow2); if (!index) { // left label.x = center.x - dx; } else { // right label.x = center.x + dx; } } // drawLabel(label); var _anchor = label._anchor, _router = label._router, fill = label.fill, y = label.y; var labelAttrs = { y: y, fontSize: 12, // 字体大小 fill: '#808080', text: label._data.type + '\n' + label._data.value, textBaseline: 'bottom' }; var lastPoint = { y: y }; if (label._side === 'left') { // 具体文本的位置 lastPoint.x = APPEND_OFFSET; labelAttrs.x = APPEND_OFFSET; // 左侧文本左对齐并贴着画布最左侧边缘 labelAttrs.textAlign = 'left'; } else { lastPoint.x = canvasWidth - APPEND_OFFSET; labelAttrs.x = canvasWidth - APPEND_OFFSET; // 右侧文本右对齐并贴着画布最右侧边缘 labelAttrs.textAlign = 'right'; } // 绘制文本 var text = labelGroup.addShape('Text', { attrs: labelAttrs }); labels.push(text); // 绘制连接线 var points = void 0; if (_router.y !== y) { // 文本位置做过调整 points = [[_anchor.x, _anchor.y], [_router.x, y], [lastPoint.x, lastPoint.y]]; } else { points = [[_anchor.x, _anchor.y], [_router.x, _router.y], [lastPoint.x, lastPoint.y]]; } labelGroup.addShape('polyline', { attrs: { points: points, lineWidth: 1, stroke: fill } }); }); }); canvas.draw(); // this.chart.on('afterpaint', function() { // addPieLabel(this.chart); // }); }, // g2获取饼图点位置 getEndPoint(center, angle, r) { return { x: center.x + r * Math.cos(angle), y: center.y + r * Math.sin(angle) }; } }, watch: { gtwopiedata: function (val, oldVal) { // 监听数据,当发生变化时,触发回调函数绘制图表,使用mounted无法正常绘制 // if(this.dothisfun){ this.g2pie(val); // this.dothisfun = false // } } }, // mounted(){ // this.g2pie(); // } } </script>
<style scoped> #id{ width: 100%; height: 100%; } </style>
本来是想将生成的方法封装到js文件中的,但是不知道为什么,import G2 进入js文件之后,vue便会卡在92%无法继续热更新,node的cpu占用率也会饱满,所以只好封装在.vue文件中,以子组件的形式被父组件调用。
本处需要注意的第一个问题,即为data中定义的chart,如果不定义,直接用let chart = new G2.chart(),也确实能够正常生成图表,但是当数据更新的时候,便会重新渲染生成新的图表,此时页面上会同时存在多个图表,所以需要提前定义chart,并使用this.chart = new G2.chart()。
本处需要注意的第二个问题,即为使用mounted钩子函数运行此函数时,因为并未检测到数据变化,所以不会生成有效图表,所以需要使用watch监听数据变化,当发生变化的时候,执行方法渲染图表。
vue引入G2图表
G2 是一套基于图形语法理论的可视化底层引擎,以数据驱动,提供图形语法与交互语法,具有高度的易用性和扩展性。使用 G2,你可以无需关注图表各种繁琐的实现细节,一条语句即可使用 Canvas 或 SVG 构建出各种各样的可交互的统计图表;
线上示例
特性
加载全部内容