在vue中如何封装G2图表
vue封装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 构建出各种各样的可交互的统计图表;
官网地址:https://antv.gitee.io/zh
线上示例
特性
- ? 完善的图形语法:数据到图形的映射,能够绘制出所有的图表;
- ? 全新的交互语法:通过触发和反馈机制可以组合出各种交互行为,对数据进行探索;
- ? 强大的 View 模块:可支持开发个性化的数据多维分析图形;
- ? 双引擎渲染:Canvas 或 SVG 任意切换;
- ? 可视化组件体系:面向交互、体验优雅;
- ✨全面拥抱 TypeScript:提供完整的类型定义文件;
介绍一下在vue中使用G2
安装G2依赖:
npm instal @antv/g2
在绘图前我们需要为 G2 准备一个 DOM 容器:
<div id="c1"></div>
执行代码:
import * as G2 from '@antv/g2';
export default {
mounted() {
const data = [
{ genre: 'Sports', sold: 275 },
{ genre: 'Strategy', sold: 115 },
{ genre: 'Action', sold: 120 },
{ genre: 'Shooter', sold: 350 },
{ genre: 'Other', sold: 150 },
];
// Step 1: 创建 Chart 对象
const chart = new G2.Chart({
container: 'c1', // 指定图表容器 ID
width: 600, // 指定图表宽度
height: 300, // 指定图表高度
});
// Step 2: 载入数据源
chart.data(data);
// Step 3:创建图形语法,绘制柱状图
chart.interval().position('genre*sold');
// Step 4: 渲染图表
chart.render();
}
}
效果展示:
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341