JS制作Antv图表实例

不折腾的前端不是好前端,今天来折腾下Antv图表,对比highcharts这个文档确实不怎么人性化,但是没办法么,毕竟highCharts还是蛮贵的,使用antv之前建议大家看下语雀上的文档。

G2

上手案例

先引入antv的js文件


在body中创建放图表的容器

在页面或者另一个单独的js文件中写图表的渲染代码,我这边是单独写的一个js文件,然后页面通过引入

var data = [
    {genre: 'Sports', sold: 275},
    {genre: 'Strategy', sold: 115},
    {genre: 'Action', sold: 120},
    {genre: 'Shooter', sold: 350},
    {genre: 'Other', sold: 150},
  ];
  var chart = new G2.Chart({
    container: 'c1',
    forceFit: true,//图表跟随图表容器宽度变化
    height : 400
  });
  chart.source(data);
  chart.interval().position('genre*sold').color('genre')
  chart.render();

如此,新鲜的图表出炉咯

image

饼图

先引入需要的js文件

 

css

 

html,因为forceFit设置的true随着父级的变化而变化,所以我在外面套了个有长宽的div,当然也可以forceFit设置为false,给容器设置宽高,都可以

  

js文件

const startAngle = -Math.PI / 2 - Math.PI / 4;
const data = [
  { type: '居住', value: 7140 },
  { type: '食品烟酒', value: 3875 },
  { type: '交通通信', value: 2267 },
  { type: '教育、文化、娱乐', value: 1853 },
  { type: '医疗保健', value: 1685 }
];
const ds = new DataSet();
const dv = ds.createView().source(data);//通过打印dv发现dv.rows中除了data的数据,每项添加了percent
dv.transform({
  type: 'percent',
  field: 'value',
  dimension: 'type',
  as: 'percent'
});
const chart = new G2.Chart({
  container: 'c1',
  forceFit: true,
  height: 500,
  padding: 'auto'
});
//载入dv数据
chart.source(dv);
//不显示图例
chart.legend(false);
chart.coord('theta', {
  radius: 0.75,
  innerRadius: 0.5,
  startAngle,
  endAngle: startAngle + Math.PI * 2
});
chart.intervalStack().position('value')
  .color('type', [ '#0a4291', '#0a57b6', '#1373db', '#2295ff', '#48adff' ])//有多少数据写多少颜色
  .opacity(1)//设置透明度
  .label('percent', {
    offset: -20,//图表上显示的数据的偏移位置
    //图表上数据的格式
    textStyle: {
      fill: 'white',
      fontSize: 12,
      shadowBlur: 20,
      shadowColor: 'rgba(0, 0, 0)'
    },
    //图表上显示的数据显示的格式
    formatter: val => {
      return parseInt(val * 100) + '%';
    }
  });
  //Guide 辅助元素:额外的标记注解
chart.guide().html({
  position: [ '50%', '50%' ],
  html: '

总计

19670

' }); //渲染图表 chart.render(); // draw label const OFFSET = 20; const APPEND_OFFSET = 50;//每个扇形标签相当于x轴的标签的位置 const LINEHEIGHT = 60; const coord = chart.get('coord'); // 获取坐标系对象 const center = coord.center; // 极坐标圆心坐标 const r = coord.radius; // 极坐标半径 const canvas = chart.get('canvas'); const canvasWidth = chart.get('width'); const canvasHeight = chart.get('height'); const labelGroup = canvas.addGroup(); const labels = []; addPieLabel(chart); canvas.draw(); chart.on('afterpaint', function() { addPieLabel(chart); }); // main //如下函数是用来放置每个扇形区域的标签的 function addPieLabel() { const halves = [[], []]; const data = dv.rows; let angle = startAngle; for (let i = 0; i < data.length; i++) { const percent = data[i].percent; const targetAngle = angle + (Math.PI * 2 * percent);//通过计算得出连线放置的位置 const middleAngle = angle + (targetAngle - angle) / 2; angle = targetAngle; const edgePoint = getEndPoint(center, middleAngle, r); const routerPoint = getEndPoint(center, middleAngle, r + OFFSET); // label const 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 const 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); }); } function getEndPoint(center, angle, r) { return { x: center.x + r * Math.cos(angle), y: center.y + r * Math.sin(angle) }; } function drawLabel(label) { const _anchor = label._anchor, _router = label._router, fill = label.fill, y = label.y; const labelAttrs = { y, fontSize: 12, // 字体大小 fill: '#808080', text: label._data.type + '\n' + label._data.value, textBaseline: 'bottom' }; const lastPoint = { 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'; } // 绘制文本 const text = labelGroup.addShape('Text', { attrs: labelAttrs }); labels.push(text); // 绘制连接线 let 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, lineWidth: 1, stroke: fill } }); } function antiCollision(half, isRight) { const startY = center.y - r - OFFSET - LINEHEIGHT; let overlapping = true; let totalH = canvasHeight; let i = void 0; let maxY = 0; let minY = Number.MIN_VALUE; const boxes = half.map(function(label) { const 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) { // eslint-disable-next-line no-loop-func boxes.forEach(box => { const 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) { const previousBox = boxes[i - 1]; const 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) { let 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) { const rPow2 = label.r * label.r; const dyPow2 = Math.pow(Math.abs(label.y - center.y), 2); if (rPow2 < dyPow2) { label.x = center.x; } else { const dx = Math.sqrt(rPow2 - dyPow2); if (!isRight) { // left label.x = center.x - dx; } else { // right label.x = center.x + dx; } } drawLabel(label); }); }

就酱紫,图表出来咯

image

L7(L代表location,7代表7大洲)

经过一次次的失败,我终于画出一个地图了,(┬_┬)

上手案例

先引入js文件(antv的地图是依赖高德地图的,所以一定要申请一个高德地图的key,不过文档中看到一句话:2.0版本在L7内部动态引入了高德地图JS API,因此不再需要单独引入高德JS API,只需设置 type 为 amap 并且传入token,所以我们可以直接使用antv提供的js文件)




在body中创建放图表的容器

js代码

//此处的构造函数Scene和GaodeMap是L7对象上的,所以一定要L7.Scene和L7.GaodeMap
const scene = new L7.Scene({
    id: 'map',
    map:new L7.GaodeMap({
        type: 'amap',
           style: 'dark', // 样式URL
           center: [120.19382669582967, 30.258134],
           pitch: 0,
           zoom: 12,
           token: '高德地图token',
   })
});

新鲜的地图出炉啦

image

气泡图

html,antv的容器是定位在父级元素上的,所以要改变它的大小,需要给容器加上positive:relative属性。

引入的外部的js文件跟上面一样,下面是js文件。

const scene = new L7.Scene({
    id: 'map',
    map:new L7.GaodeMap({
        pitch: 0,
    type: 'amap',
    style: 'light',
    center: [ 140.067171, 36.26186 ],
    zoom: 5.32,
    maxZoom: 10
   })
});
fetch(
    'https://gw.alipayobjects.com/os/basement_prod/d3564b06-670f-46ea-8edb-842f7010a7c6.json'
  )
    .then(res => res.json())
    .then(data => {
      const pointLayer = new L7.PointLayer({})
        .source(data)
        .shape('circle')
        .size('mag', [ 1, 25 ])
        .color('mag', mag => {
          return mag > 4.5 ? '#5B8FF9' : '#5CCEA1';
        })
        .active(true)
        .style({
          opacity: 0.3,
          strokeWidth: 1
        });
  
      scene.addLayer(pointLayer);
    });

气泡图出来啦

image

你可能感兴趣的:(JS制作Antv图表实例)