首页 前端知识 关于在vue3中使用Svg绘制柱状统计图

关于在vue3中使用Svg绘制柱状统计图

2024-09-01 23:09:52 前端知识 前端哥 175 856 我要收藏

        在项目中有产品需要使用带有样式的柱状统计图,查找echarts并没有可以设置的样式,因此通过svg标签来实现,记录自己的编写过程,防止忘记。

统计图样式如下,需要有椭圆样式以及渐变色(背景可以svg标签外包div通过设置css背景图片实现)

1.html部分

<svg id = "tongji" width = "690" :height='tongheight'>
	<def id = "defs"></def>
</svg>

2.js部分开始进行绘制统计图

// 定义统计图数据
const = formData = ref(
  list:[
    {
      name:'数据1',
      increase:100,  //柱状数据
      zhuheight:20,  //柱状条高度
      upcolor1:'#ffb583',     // 涨幅渐变色1
      upcolor2:'#ff755f',     // 涨幅渐变色2
      downcolor1:'#94BFBB',   // 跌幅渐变色1
      downcolor2:'#7B8F93',   // 跌幅渐变色2
      tuocolor1:'#fbe5c0',    // 正值椭圆颜色配置
      tuocolor2:'#D2E0DF'     // 负值椭圆颜色配置
    },
    ...
  ]
);

// 定义svg画布的高度  (统计图数据的高低定义动态画布高度)
const = tongheight = ref(0);


// 定义绘制方法
const = drawColum = () => {
  // 找出一组数据中的最小值和最大值,方便确定svg的画布高度
  let min = Math.min.apply(Math,formData.value.list.map(item =>{
    return item.zhuheight
  }));

  let max = Math.min.apply(Math,formData.value.list.map(item =>{
    return item.zhuheight
  }));


  // 根据最大值和最小值确定画布高度
  if (min <= 0 && max > 0) {
    tongheight.value= Math.abs(min)+max
    console.log("数值存在负值时,取最大和最小值的绝对值之和");
  }else if (min > 0 && max > 0) {
    tongheight.value=max
    console.log("数值只有正值时,取最大值");
  } else if(max < 0 && min < 0){
    tongheight.value=Math.abs(min)
    console.log("数值只有负值时,取最小值的绝对值");
  }
  
  // 获取svg标签
  let childs = document.getElementById('tongjitu');

  // 数据更改,统计图进行更新时,需将上一张绘制好的统计图进行销毁,否则会造成无用标签的逐步增加,多张统计图重叠在一起

  if (document.getElementById('defs').childNodes.length>0) { // 判断def标签中是否存在颜色过滤器
    let child = document.getElementById('defs');
    for (let i = child.childNodes.length-1;i>=0; i--) {
      // 删除def标签中柱状图颜色过滤器
      if (child.childNodes[i].nodeName=='linearGradient') {
        document.getElementById('defs').removeChild(child.children[i])
      }
    }
  }

  // 删除柱状条、数据显示、修饰样式(如ellipse标签:椭圆标签)、x轴
  if (childs.childNodes.length>1) {
    for (let i = childs.childNodes.length-1;i>=0; i--) {
      // rect为矩形标签
      if (childs.childNodes[i].nodeName=='rect') {
        childs.removeChild(childs.children[i])
      }      
    }
    for (let j = childs.childNodes.length-1;j>=0; j--) {
      // text为字体标签
      if (childs.childNodes[j].nodeName=='text') {
        childs.removeChild(childs.children[j])
      }      
    }
    for (let a = childs.childNodes.length-1;a>=0; a--) {
      // ellipse为椭圆标签
      if (childs.childNodes[a].nodeName=='ellipse') {
        childs.removeChild(childs.children[a])
      }      
    }
    for (let a = childs.childNodes.length-1;a>=0; a--) {
      // g包裹的为X轴
      if (childs.childNodes[a].nodeName=='g') {
        childs.removeChild(childs.children[a])
      }      
    }
  }
  
  // 开始进行统计图的绘制
  var data = formData.value.list; // 获取统计图数据
  var w = 690;  // 定义宽度
  var h = 600;  // 定义高度

  // 计算每个柱子的高度
  // (w-200)确定第一个柱状条距离x轴起始点的最小距离,需要统计图更靠左边
  var colwidth = (w-200)/(data.length*2+1);

  // 开始设置每个柱状条的样式规格
  for(var i = 0; i < data.length; i++){
	var obj = data[i];
	var rw = colwidth;  // 柱子宽度
	var rh =Math.abs(obj.zhuheight)*3;  // 柱子高度
	var x = 110 + colwidth * (2 * i + 1);  // 柱状条在X轴的位置
	var y = 600 -48 - rh;  // 柱状条在Y轴的位置

	// 设置渐变色,线性渐变,数据为正值和负值时确定两个颜色组成渐变色
	var color = 
        `<linearGradient id = 'g${i}' x1 = '0' y1 = '0' x2 = '0' y2 = '100%'>
			<stop offset = '0' stop-color = '${obj.increase>0?obj.upcolor1:obj.downcolor1}'></stop>
			<stop offset = '100%' stop-color = '${obj.increase>0?obj.upcolor2:obj.downcolor2}'></stop>
		 </linearGradient>`;
	document.getElementById('defs').innerHTML += color;

	// 动态创建矩形
	var rect = document.createElementNS('http://www.w3.org/2000/svg','rect');
	rect.setAttribute('width',rw);   // 柱状条宽度
	rect.setAttribute('height',rh);   // 柱状条高度
	rect.setAttribute('x',x);   // 柱状条在X轴的起始位置
    // y轴位置设置成动态显示
    // y轴根据柱状条的高度为正值还是负值进行判断起始位置,(3.71*(100-max))此计算公式是根据我所绘制的统计图得来,有其他需求可以10个单位为数值进行换算
	rect.setAttribute('y',obj.zhuheight>0?y-3.71*(100-max):y+rh-4-3.71*(100-max));
    rect.setAttribute('rx','5');   // 柱状条的圆角设置
    rect.setAttribute('ry','5');   // 柱状条的圆角设置
	rect.setAttribute('fill',`url(#g${i})`);  // 配置对应的线性渐变颜色,最开始设置的
	document.getElementById('tongjitu').appendChild(rect);  // 将配置好的柱状条加入到标签中
    		
    // 动态创建椭圆样式  (椭圆样式根据需要进行绘制)
    var ellipse =document.createElementNS('http://www.w3.org/2000/svg','ellipse');
    ellipse.setAttribute('rx',rw/2);   // rx椭圆水平半径为柱状条宽度的一半
	ellipse.setAttribute('ry','8');    // ry为椭圆的高度
	ellipse.setAttribute('cx',x+rw/2);  // cx椭圆位置为柱状条位置再多出一半的距离
   	// y轴位置设置成动态显示,与矩形在y轴的距离一样,根据样式进行位置的调整(我这里负值时在位置上进行了‘-7’的处理)
	ellipse.setAttribute('cy',obj.zhuheight>0?y-3.71*(100-max):y+rh*2-3.71*(100-max)-7);
    // 椭圆的颜色配置
    ellipse.setAttribute('fill',`${obj.zhuheight>0?obj.tuocolor1:obj.tuocolor2}`);
	document.getElementById('tongjitu').appendChild(ellipse);
    
	// 动态数据显示
	var txt = document.createElementNS("http://www.w3.org/2000/svg", "text");
    txt.setAttribute("x", x-10);
    // y轴位置设置成动态显示,与椭圆样式思路一样,需要在原有位置基础上添加或者减去一段距离
    txt.setAttribute("y", obj.increase>0?y-20-3.71*(100-max):y+rh*2-3.71*(100-max)+35);
    txt.innerHTML = obj.increase+'%';
    txt.setAttribute('font-size',i==0?28:26)  // 根据需求定义字体大小,加粗,颜色
    txt.setAttribute('font-weight',i==0?700:400)
    txt.setAttribute("style", i==0?'fill:#e8843c':'fill:#888888' );
    document.getElementById('tongjitu').appendChild(txt);
  }
  
    // 动态设置统计图的X轴(注:因每次绘制后先前标签会被删除,浏览器从上往下识别,柱状条会遮盖住X轴,为保持X轴永远在柱状条之上,进行动态添加设置)
    var g = document.createElementNS('http://www.w3.org/2000/svg','g');
	g.setAttribute('stroke','#cccccc');
	g.setAttribute('stroke-width',5);  //设置轴线的宽度,因柱状条不支持单独两个圆角的设置,使用X轴遮挡住处在轴线上的柱状条的圆角样式
    g.setAttribute('id','g');  // 设置唯一值id
	document.getElementById('tongjitu').appendChild(g);
      // 设置X轴轴线  
      var line = document.createElementNS('http://www.w3.org/2000/svg','line');
	  line.setAttribute('x1','30');
	  line.setAttribute('y1',min<=0?`${550-(100-max)*3.71}`:`${550-3.71*(100-tongheight.value)}`);
      line.setAttribute('x2','660');
      line.setAttribute('y2',min<=0?`${550-(100-max)*3.71}`:`${550-3.71*(100-tongheight.value)}`);
      document.getElementById('g').appendChild(line);
}


// 调用绘制方法
onBeforeMount(() => {
  drawColum();
});

转载请注明出处或者链接地址:https://www.qianduange.cn//article/17427.html
标签
评论
发布的文章

Spring MVC-JSON

2024-06-02 09:06:53

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!