大年三十上午写文章,这绝对是第一次,哈哈哈,不知为何,此时的感觉异常的好,嘻嘻嘻。在这里祝新年快乐,身体健康,万事如意!
上篇文章,我们总结了D3画圆弧的方法,先是计算绘画的起点,然后逆时针画外圆弧,再是顺时针画内圆弧,最后stroke连线。
这篇文章我们来看看扇形之间的空隙是如何实现的。
一、padAngle
在D3里,它有一个生成器的概念,饼图有饼图生成器,圆弧有圆弧生成器。当你创建了生成器,生成器就可以调用padAngle函数来给你的图形添加“空隙”。
import d3 from 'd3';
// 圆弧生成器
const createArc = d3.arc();
// 饼图生成器
const createPie = d3.pie();
“padAngle”并不是真实绘画出来的“空隙弧度”,这点是要注意的。
不知道大家发现没有,D3画出来的图,“外圆弧的空隙弧的水平连线” 始终等于 “内圆弧的空隙弧的水平连线”。
“空隙弧”也是弧,也有起点、终点,如果将起点、终点连线,那么你会发现,外内“空隙弧”的水平连线是相等的。
而为了实现这样的效果,padAngle起到了类比的作用。
接下来我们一起来看看吧。
二、实现原理
首先,D3将内外圆弧的差值、用户输入的padAngle组成了一个三角形。
其中,ap是角度,就是角A,等于用户输入的padAngle的一半,AC就是内外半径的差值,根据正弦定理,BC真实的长度应该是:
BC = AC * sin(ap)
这个BC啊,就是咱们刚才说的“水平连线”,这个“水平连线”是要真实的绘画出来的。
因为BC是相等的,AC是不断变化的,它可以外圆弧的半径,也可以是内圆弧的半径,所以,真实绘画出来的ap它也是不断变化的。
根据下面的公式:
// 假设,AC的长度变化到了外圆弧的半径,无论在哪个三角形里,BC是一定相等的
内外半径 * sin(ap) = 外圆弧的半径 * sin(真实绘画出来的ap)
// 真实绘画出来的sin(ap)如下:
sin(真实绘画出来的ap) = 内外半径 / 外圆弧半径 * sin(ap)
根据“Math.sin”这个API来看的话,此时只是求出了角度对应的sin值,它并不是弧度。
那我们可以通过反正弦函数(Math.asin)来求出正弦值对应的(角度的弧度)。
上面通过反正弦函数求出来的弧度,就是真实要绘画出来的“空隙弧度”。
有了这个“空隙弧度”,那剩下来的工作就是每个弧度的加加减减。
下面给出了逻辑的一些关键代码
function arc(arcInfo) {
const da = Math.abs(arcInfo.startAngle - arcInfo.endAngle);
let da0 = da;
let da1 = da;
if (rp > 0.00000000000001) {
// 外圆弧的空隙弧度
var p0 = asin( rp / r0 * sin(ap) ),
// 内圆弧的空隙弧度
p1 = asin( rp / r1 * sin(ap) );
da0 = da0 - p0 * 2
// 外圆弧,如果当前数据的弧长占比足够
if (da0 > 0) {
p0 = p0 * (cw ? 1 : -1);
// 绘画的起始弧度
a00 = a00 + p0;
// 绘画的终止弧度
a10 = a10 - p0;
} else {
// 如果弧度不够,占比不够,直接塌陷成点
}
// 内圆弧的空隙弧度的绘画方式与外圆弧一样
da1 = da1 - 2 * p1
if (da1 > 0 ) {
p1 = p1 * (cw ? 1 : -1)
a01 = a01 + p1;
a11 = a11 - p1
} else {
// 依旧是弧度不够,塌陷成点
}
}
// 外圆弧绘画起点的横坐标(a01:起始弧度)
const x01 = r1 * cos(a01);
const y01 = r1 * sin(a01);
// a10: 终止弧度
const x10 = r0 * cos(a10);
const y10 = r0 * sin(a10)
// 画外弧、内弧
context.moveTo(x01, y01)
context.arc(0, 0, r1, a01, a11, !cw);
context.arc(0, 0, r0, a10, a00, cw);
}
下面给出了辅助图,方便大家理解d3的处理逻辑。
三、最后
本期的内容分享到这里就结束啦,下期我们继续分享,如何给扇形加圆角,那么,我们下期再见,希望我的分享能够对你有帮助