svg与n边形

459 阅读4分钟

前言

如果要在网页里绘制图形,立马想到的是canvas,但canvas绘制还是会执行若干js代码,而svg作为可缩放矢量图形,只需要给定相应标签和属性的值,即可实现图形展示、动画处理。svg恰好提供了<polygon>标签来处理多边形。

背景

我们要实现的是3个套起来的正3-n边形,还有一个按真实数据展示的不规则多边形。

前期思考

刚开始考虑是手动算出来对应点的坐标位置绘制即可,那么问题来了,n边形岂不是要算到宇宙尽头,而且不规则的多边形...

所以重点其实是如何计算出最合理的多边形顶点的坐标位置!

思路

  1. 根据画布宽高,算出圆心坐标
  2. 根据多边形边数、圆心坐标、三角函数、圆半径,算出多边形每个顶点对应的坐标
  3. 真实数据的不规则多边形坐标同2计算
  4. 拼接计算好的坐标值给标签属性使用

具体过程

  • 圆心坐标 取画布中心位置为圆心绘图
const center = {
  cx: width / 2,
  cy: height / 2
}
  • 多边形坐标【这里是重点】 因为是3个套起来的多边形,所以会传入一个正多边形对应圆的半径数组,根据圆心坐标、半径、多边形每条边偏移角度可算出相应坐标。 这里我们可以简单回忆初中学过的弧度及三角函数
弧度α = 角度数 * (π / 180)
sin α = 对边 / 斜边
cos α = 临边 / 斜边

角度复习.png

  1. sides为多边形边数
  2. rArr为多边形半径数组,三个值,[外层装饰, 总值, 及格值],算不规则多边形顶点时用的rArr[1]是因为按总值的半径和相应概率计算。
/**
 * @param r 圆半径(斜边)
 * @param angle 角度
 * @param cx 圆心横坐标
 * @param cy 圆心纵坐标
 */
const getCoordinates = (r, angle, cx, cy) => {
  const radian = Math.PI / 180 * angle
  return {
    y: Math.sin(radian) * r + cy,
    x: Math.cos(radian) * r + cx,
  }
}

// 数据所有坐标
const coordinates = rArr.map((v) =>
  new Array(sides)
    .fill(undefined)
    .map((_, index) =>
      getCoordinates(
        v,
        360 / sides * index,
        center.cx,
        center.cy
      )
    )
)

注意:这里在数据map时取的角度为360 / sides * index,即效果为第一个在svg的x坐标轴的正方向-水平向右,如果有其他展示需求,可以加相应角度

  • 数据多边形坐标 数据坐标同多边形坐标计算,只是半径乘了相应概率
const dataCoordinates = data.map((item, index) =>
  getCoordinates(
    rArr[1] * item.rate,
    360 / sides * index,
    center.cx,
    center.cy
  )
)
  • 展示 拼接polygon标签的points属性值
const getPoints = (data) => data.reduce((prev, next) => `${prev}${next.x} ${next.y},`,'')


coordinates.map((item, index) => {
  const points = getPoints(item)
  const strokeDasharray = index !== 1 ? '4 4' : ''
  return (
    <polygon
      key={`polygon_${index}`}
      points={points}
      strokeDasharray={strokeDasharray}
      fill={fillColor}
      stroke={strokeColor}
    />
  )
})
  • 此外还有坐标点及圆心到各多边形顶点连线的展示,在已算出坐标的情况下,用<circle><line>等标签画出即可
    • 需指定r, cx, cy, fill等基本属性
    • 需指定x1, y1, x2, y2, strokeDasharray, stroke, strokeWidth等基本属性
    • 不规则多边形的填充颜色用线性渐变做
    <defs>
      <linearGradient x1="0%" y1="0%" x2="0%" y2="100%" id="dataGradient">
        <stop offset="0%" style={{ stopColor: '#008380' }} />
        <stop offset="100%" style={{ stopColor: '#004359' }} />
      </linearGradient>
    </defs>
    
    <polygon points={dataPoints} fill="url(#dataGradient)" strokeWidth="2" stroke="#F71629" />
    
    

我们在使用过程中,传入画布宽高,正多边形边数、对应的半径数组即可获取我们要的正n边形

其他

  • svg在渲染时是按代码从上到下,所以如果对图形有上下层次要求,要将ui最上层展示的后写
  • 文本展示
    • 在文本展示层面,svg一般用<text>标签处理
    • svg与canvas的处理类似,对换行支持不太友好,如果在svg内涉及到换行文本,需要用<foreignObject>做处理,内部可以包裹html标签
    • 对于多边形旁边的多行文本展示,可以按字数、字号算出不同的坐标,以达到最好的视觉效果

效果

3.png

4.png

5.png

6.png

7.png

8.png

结语

svg作为可缩放矢量图,在放大或者改变尺寸的情况下其图形质量不会失真,在任何分辨率下都可以高质量展示,与其他类型的图片比起来,尺寸更小,可压缩性更强,种种优点都告诉我们一件事,值得一学并应用到实际项目当中!


关注公众号:不zhi前端,一起学习,一起进步~

logo.jpg