likes
comments
collection
share

ReCharts如何借助三角函数绘制Label

作者站长头像
站长
· 阅读数 13

前言

Recharts是一个基于React和D3的开源图表库,提供了一系列易于使用的图表组件和可自定义的样式选项。它被设计为简单易用,旨在帮助开发人员快速创建交互式的数据可视化图表。

与Echarts相比,Recharts都有哪些优势?

在比较Recharts和ECharts时,Recharts通常被认为是更轻量级的数据可视化库之一。这是因为Recharts是基于React构建的,它的体积相对较小,而且可以更好地与React应用程序进行集成,因此在使用Recharts时,不需要为了集成图表而额外引入其他JavaScript库。

另外,由于Recharts的设计初衷是为了提供简单易用的API和基于React的组件化开发方式,因此其代码库的规模相对较小,而且在使用上更加直观和易于理解。相比之下,ECharts的代码库相对更大,并且它提供了许多高级功能和复杂的API,需要更长的学习时间和更复杂的配置。

ReCharts如何借助三角函数绘制Label

不符合预期的原生UI

这是笔者在某次需求时接到的UI图

ReCharts如何借助三角函数绘制Label

在使用Recharts进行饼图生成时,得到的效果如下

ReCharts如何借助三角函数绘制Label

ReCharts如何借助三角函数绘制Label

真是有点丑,我不能接受

自定义Label

Recharts是一个处理图表的类库,其中的“re”除了指代“React”之外,还有“Redefined”之意,即Recharts支持我们自定义想要的元素。

在自定义元素之前,我们需要先了解一下SVG。SVG是一种基于XML的矢量图形格式,它具有可伸缩性和可编辑性的优点。Recharts可使用SVG来绘制图表元素,因此在自定义图表元素前需要了解SVG的相关知识。

SVG之path

SVG是一种XML语言,可以用来绘制矢量图形。SVG可以通过定义必要的线和形状来创建一个图形。

path是SVG中一个很强大的标签,我们可以使用path来绘制矩形、折线、圆形、多边形,以及一些其他的复杂形状。

先看一段示例代码

<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M250 150 L150 350 L350 350 Z" fill="none" stroke="black"/>
</svg>

d属性表示我们要绘画的路径。

M x y:画笔移动到 (x, y),作为起点

L x y :画一条直线到 (x, y)

H x :水平划线到横坐标 x

V y :水平划线到纵坐标 y

Z :闭合路径回到起点(用于创建一个形状)

所以以上示例最终就会画出一个三角形。

ReCharts如何借助三角函数绘制Label

那么有了path的支持,我们就可以轻易得画出ui中的折线引导线。

关键在于我们要如何获取到画笔的起点。

图表属性分析

在Recharts自带的renderLabel方法中,我们可以通过props获取一些关键属性

const renderLabel = (props) => {
  const { cx, cy, midAngle, outerRadius } = props
}
  1. cx:圆心的横坐标

  2. cy:圆心的纵坐标

  3. midAngle:扇形中心角的一半

  4. outerRadius:圆的半径

根据图示,我们所需要的画笔起点,其实就是图上红点对应的位置。

那么直角三角形在已知半径和角度的情况下,如何去获取直角三角形的另外两条边的长度,就得借助我们伟大的三角函数

三角函数

先来简单复习一下三角函数

ReCharts如何借助三角函数绘制Label

有了以上的基本公式后,我们就可以把要求的坐标转化为求三角形边长,即下图中的sx和sy

在已知角度大小的前提下,我们可以求其 和 值

const RADIAN = Math.PI / 180
const sin = Math.sin(-RADIAN * midAngle)
const cos = Math.cos(-RADIAN * midAngle)

为什么上面的代码中计算cos和sin时要多添加一个减号

圆中以圆心为起始坐标,可以划分为四个区域,我们称为四个象限。

在第一象限中,引导线画笔起点 (这里并非真正的坐标轴和象限,要忽略正负,而是考虑页面中坐标的相对位置)

又结合cos和sin各自的奇偶性可得, 。

cos与计算sx值相关,sin与计算sy值相关,那么这里以第一象限为例,最终计算出来的sy为负值就很合情理了。

因为我们最终的sy坐标是基于cy的,即

const sy = cy + outerRadius * sin

开画

有了上方提及的所有知识点后,我们就可以轻松计算出画笔起始点的坐标了

  const RADIAN = Math.PI / 180
  const { cx, cy, midAngle, outerRadius, fill, percent, value } = props
  //三角函数基于弧度制,所以需要先转为弧度制获取已知角度的cos和sin值
  const sin = Math.sin(-RADIAN * midAngle)
  const cos = Math.cos(-RADIAN * midAngle)
  //已知cos和sin之后,利用三角函数最终计算出sx和sy的坐标
  const sx = cx + outerRadius * cos
  const sy = cy + outerRadius * sin

有了起点后,我们需要往外延伸,画出第一条直线。

只需要在原来的基础上加长半径,这样就能得到第二个点的坐标了

 //继续往外延伸
 const mx = cx + (outerRadius + 15) * cos
 const my = cy + (outerRadius + 15) * sin

第三个点的坐标,只需要将x往水平方向延伸即可。需要注意的是往左边延伸,还是右边延伸。

 const ex = mx + (cos >= 0 ? 1 : -1) * 42
 const ey = my

完整代码

const renderLabel = (props) => {
  const RADIAN = Math.PI / 180
  const { cx, cy, midAngle, outerRadius, fill, percent, value } = props
  const sin = Math.sin(-RADIAN * midAngle)
  const cos = Math.cos(-RADIAN * midAngle)
  const sx = cx + outerRadius * cos
  const sy = cy + outerRadius * sin
  const mx = cx + (outerRadius + 15) * cos
  const my = cy + (outerRadius + 15) * sin
  const ex = mx + (cos >= 0 ? 1 : -1) * 42
  const ey = my
  const textAnchor = cos >= 0 ? 'start' : 'end'
  return (
    <g>
      <path
        d={`M${sx} ${sy} L${mx} ${my} L${ex} ${ey}`}
        stroke={'rgba(0,0,0,0.3)'}
        fill="none"
      />

      <text
        x={ex + (cos >= 0 ? 1 : -1) * 5}
        y={ey}
        textAnchor={textAnchor}
        fill={'rgba(0,0,0,0.3)'}
      >{`41~50`}</text>
      <text
        x={ex + (cos >= 0 ? 1 : -1) * 12}
        y={ey}
        dy={18}
        textAnchor={textAnchor}
        fill="#222222"
      >
        {`10%`}
      </text>
    </g>
  )
}

最终效果

ReCharts如何借助三角函数绘制Label