前言
如果要在网页里绘制图形,立马想到的是canvas,但canvas绘制还是会执行若干js代码,而svg作为可缩放矢量图形,只需要给定相应标签和属性的值,即可实现图形展示、动画处理。svg恰好提供了<polygon>标签来处理多边形。
背景
我们要实现的是3个套起来的正3-n边形,还有一个按真实数据展示的不规则多边形。
前期思考
刚开始考虑是手动算出来对应点的坐标位置绘制即可,那么问题来了,n边形岂不是要算到宇宙尽头,而且不规则的多边形...
所以重点其实是如何计算出最合理的多边形顶点的坐标位置!
思路
- 根据画布宽高,算出圆心坐标
- 根据多边形边数、圆心坐标、三角函数、圆半径,算出多边形每个顶点对应的坐标
- 真实数据的不规则多边形坐标同2计算
- 拼接计算好的坐标值给标签属性使用
具体过程
- 圆心坐标 取画布中心位置为圆心绘图
const center = {
cx: width / 2,
cy: height / 2
}
- 多边形坐标【这里是重点】 因为是3个套起来的多边形,所以会传入一个正多边形对应圆的半径数组,根据圆心坐标、半径、多边形每条边偏移角度可算出相应坐标。 这里我们可以简单回忆初中学过的弧度及三角函数
弧度α = 角度数 * (π / 180)
sin α = 对边 / 斜边
cos α = 临边 / 斜边
- sides为多边形边数
- 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标签 - 对于多边形旁边的多行文本展示,可以按字数、字号算出不同的坐标,以达到最好的视觉效果
- 在文本展示层面,svg一般用
效果
结语
svg作为可缩放矢量图,在放大或者改变尺寸的情况下其图形质量不会失真,在任何分辨率下都可以高质量展示,与其他类型的图片比起来,尺寸更小,可压缩性更强,种种优点都告诉我们一件事,值得一学并应用到实际项目当中!
关注公众号:不zhi前端,一起学习,一起进步~