SVG 画一个有 4 个扇区的圆
SVG Draw a circle with 4 sectors
我需要画一个有 4 个扇区的圆。我正在尝试绘制这样的扇区:
<path d="M200, 200, l 100,-100 a180,180 0 0,0 -127.27,127.27 z"/>
我从等式中得到 -127.27、127.27:
x=cos(angle) * radius
y=sin(angle) * radius
我的角度是135,我的半径是180。
这是我得到的 codepen。蓝色的是我在这里说的那个,黑色的是我用不同的数字尝试的。
为什么我没有得到正确的 1/4 圆?我错过了什么?
这些数字没有多大意义。您首先移动到 (200,200)
,然后画一条直线到 (300,100)
(长度:141 个单位),然后是一个圆弧,结束于 (172.73,227.27)
(半径 180 个单位)。直线段的长度不应该至少等于圆的半径吗?
你让自己的生活变得非常困难。如果要绘制四个圆段,第一步是使用 <g>
元素将坐标系移动到圆心。然后您可以使用几乎相同的代码创建所有四个段。
这是一个例子:
<svg width="200" height="200" viewBox="0 0 200 200">
<g transform="translate(100,100)" stroke="#000" stroke-width="2">
<path d="M0 0-70 70A99 99 0 0 1-70-70Z" fill="#f00"/>
<path d="M0 0-70-70A99 99 0 0 1 70-70Z" fill="#080"/>
<path d="M0 0 70-70A99 99 0 0 1 70 70Z" fill="#dd0"/>
<path d="M0 0 70 70A99 99 0 0 1-70 70Z" fill="#04e"/>
</g>
</svg>
如果您想要一个不同半径的圆,请将 99
替换为您想要的半径,并将 70
替换为该值乘以 sqrt(0.5)。
路径数据细分:
M0 0-70 70
Move到(0,0)
,然后画一条直线到(-70,70)
(隐含L
)。
A99 99 0 0 1-70-70
画一个elliptical arc from this point to (-70,-70)
with rx=rx=99
, x-axis-rotation=0
, large-arc-flag=1
, and sweep-flag=0
. (The last two parameters are described here).
Z
Close路径。
我真的很懒,所以当我需要画弧线的时候,我是用下面的脚本:
我创建一个单位向量:
var p = svgElem.createSVGPoint()
p.x = 0
p.y = 1
然后我为我的旋转创建一个矩阵:
var m = svgElem.createSVGMatrix()
最后我旋转了单位向量并translate/scale它到我想要的位置。
var p2 = p.matrixTransform(m.rotate(45))
p2.x = cx + p2.x*rx
p2.y = cy + p2.y*ry
现在我可以 console.log(p2.x,p2.y)
如果我想对片段进行硬编码,或者您可以从脚本创建片段。
这是一个基本示例(我知道,对于像上面这样的简单案例,这不是必需的,但它是一个简单的通用解决方案,在过去几年中对我帮助很大...)
var svgElem=document.getElementById("svg");
var cx=100;
var cy=100;
var rx=90;
var ry=90;
var p = svgElem.createSVGPoint();
p.x = 0;
p.y = 1;
var m = svgElem.createSVGMatrix();
var p2 = p.matrixTransform(m.rotate(45));
p2.x = cx + p2.x*rx;
p2.y = cy + p2.y*ry;
console.log(p2.x,p2.y);
var path = document.createElementNS("http://www.w3.org/2000/svg","path");
svgElem.appendChild(path);
var d="M"+cx+" "+(cy+ry)+"A"+rx+" "+ry+" 0 0 1"+p2.x+" "+p2.y+"L"+cx+" "+cy+"z";
path.setAttribute("d",d)
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
</svg>
这是我在 React
组件中使用的 SVG
扇区路径的编程生成函数:
getSectorPath(x, y, outerDiameter, a1, a2) {
const degtorad = Math.PI / 180;
const halfOuterDiameter = outerDiameter / 2;
const cr = halfOuterDiameter - 5;
const cx1 = (Math.cos(degtorad * a2) * cr) + x;
const cy1 = (-Math.sin(degtorad * a2) * cr) + y;
const cx2 = (Math.cos(degtorad * a1) * cr) + x;
const cy2 = (-Math.sin(degtorad * a1) * cr) + y;
return "M" + x + " " + y + " " + cx1 + " " + cy1 + " A" + cr + " " + cr + " 0 0 1 " + cx2 + " " + cy2 + "Z";
}
这是用法:
<svg width={outerDiameter} height={outerDiameter}>
<path d={this.getSectorPath(outerDiameter / 2, outerDiameter / 2, outerDiameter, 45, 135)} fill="#f00"/>
<path d={this.getSectorPath(outerDiameter / 2, outerDiameter / 2, outerDiameter, 135, 225)} fill="#f00"/>
<path d={this.getSectorPath(outerDiameter / 2, outerDiameter / 2, outerDiameter, 225, 315)} fill="#f00"/>
<path d={this.getSectorPath(outerDiameter / 2, outerDiameter / 2, outerDiameter, 315, 45)} fill="#f00"/>
</svg>
这是我从 JavaScript 创建 svg 的示例。我写它的灵感来自@zmechanic 回答。一个扇区有梯度。
要执行它,只需创建一个 html 文档并复制内容。
<!DOCTYPE html>
<html>
<body>
<div id="svgRoot"></div>
<script type="text/javascript">
const DIAMETER = 200;
const SVG_SIZE = DIAMETER + 12;
const STROKE = "black";
const STROKE_WIDTH = "2";
const getSectorPath = (x, y, outerDiameter, a1, a2) => {
const degtorad = Math.PI / 180;
const cr = outerDiameter / 2;
const cx1 = Math.cos(degtorad * a2) * cr + x;
const cy1 = -Math.sin(degtorad * a2) * cr + y;
const cx2 = Math.cos(degtorad * a1) * cr + x;
const cy2 = -Math.sin(degtorad * a1) * cr + y;
return `M${x} ${y} ${cx1} ${cy1} A${cr} ${cr} 0 0 1 ${cx2} ${cy2}Z`;
};
const svgRoot = document.getElementById("svgRoot");
const pieChartSvgString = `<svg width="${SVG_SIZE}" height="${SVG_SIZE}">
<defs>
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#DD5E89;stop-opacity:1" />
<stop offset="100%" style="stop-color:#F7BB97;stop-opacity:1" />
</linearGradient>
</defs>
<g fill="url(#gradient1)">
<path
stroke="${STROKE}"
strokeWidth="${STROKE_WIDTH}"
d="${getSectorPath(SVG_SIZE / 2, SVG_SIZE / 2 - 5, DIAMETER, 45, 135)}"
/>
</g>
<path
stroke="${STROKE}"
strokeWidth="${STROKE_WIDTH}"
d="${getSectorPath(SVG_SIZE / 2, SVG_SIZE / 2, DIAMETER, 135, 225)}"
fill="#00ff00"
/>
<path
stroke="${STROKE}"
strokeWidth="${STROKE_WIDTH}"
d="${getSectorPath(SVG_SIZE / 2, SVG_SIZE / 2, DIAMETER, 225, 315)}"
fill="#0000ff"
/>
<path
stroke="${STROKE}"
strokeWidth="${STROKE_WIDTH}"
d="${getSectorPath(SVG_SIZE / 2, SVG_SIZE / 2, DIAMETER, 315, 45)}"
fill="#ffff00"
/>
</svg>`;
const svgNode = document.createRange().createContextualFragment(pieChartSvgString);
svgRoot.appendChild(svgNode);
</script>
</body>
</html>
我需要画一个有 4 个扇区的圆。我正在尝试绘制这样的扇区:
<path d="M200, 200, l 100,-100 a180,180 0 0,0 -127.27,127.27 z"/>
我从等式中得到 -127.27、127.27:
x=cos(angle) * radius
y=sin(angle) * radius
我的角度是135,我的半径是180。
这是我得到的 codepen。蓝色的是我在这里说的那个,黑色的是我用不同的数字尝试的。
为什么我没有得到正确的 1/4 圆?我错过了什么?
这些数字没有多大意义。您首先移动到 (200,200)
,然后画一条直线到 (300,100)
(长度:141 个单位),然后是一个圆弧,结束于 (172.73,227.27)
(半径 180 个单位)。直线段的长度不应该至少等于圆的半径吗?
你让自己的生活变得非常困难。如果要绘制四个圆段,第一步是使用 <g>
元素将坐标系移动到圆心。然后您可以使用几乎相同的代码创建所有四个段。
这是一个例子:
<svg width="200" height="200" viewBox="0 0 200 200">
<g transform="translate(100,100)" stroke="#000" stroke-width="2">
<path d="M0 0-70 70A99 99 0 0 1-70-70Z" fill="#f00"/>
<path d="M0 0-70-70A99 99 0 0 1 70-70Z" fill="#080"/>
<path d="M0 0 70-70A99 99 0 0 1 70 70Z" fill="#dd0"/>
<path d="M0 0 70 70A99 99 0 0 1-70 70Z" fill="#04e"/>
</g>
</svg>
如果您想要一个不同半径的圆,请将 99
替换为您想要的半径,并将 70
替换为该值乘以 sqrt(0.5)。
路径数据细分:
M0 0-70 70
Move到(0,0)
,然后画一条直线到(-70,70)
(隐含L
)。
A99 99 0 0 1-70-70
画一个elliptical arc from this point to (-70,-70)
with rx=rx=99
, x-axis-rotation=0
, large-arc-flag=1
, and sweep-flag=0
. (The last two parameters are described here).
Z
Close路径。
我真的很懒,所以当我需要画弧线的时候,我是用下面的脚本: 我创建一个单位向量:
var p = svgElem.createSVGPoint()
p.x = 0
p.y = 1
然后我为我的旋转创建一个矩阵:
var m = svgElem.createSVGMatrix()
最后我旋转了单位向量并translate/scale它到我想要的位置。
var p2 = p.matrixTransform(m.rotate(45))
p2.x = cx + p2.x*rx
p2.y = cy + p2.y*ry
现在我可以 console.log(p2.x,p2.y)
如果我想对片段进行硬编码,或者您可以从脚本创建片段。
这是一个基本示例(我知道,对于像上面这样的简单案例,这不是必需的,但它是一个简单的通用解决方案,在过去几年中对我帮助很大...)
var svgElem=document.getElementById("svg");
var cx=100;
var cy=100;
var rx=90;
var ry=90;
var p = svgElem.createSVGPoint();
p.x = 0;
p.y = 1;
var m = svgElem.createSVGMatrix();
var p2 = p.matrixTransform(m.rotate(45));
p2.x = cx + p2.x*rx;
p2.y = cy + p2.y*ry;
console.log(p2.x,p2.y);
var path = document.createElementNS("http://www.w3.org/2000/svg","path");
svgElem.appendChild(path);
var d="M"+cx+" "+(cy+ry)+"A"+rx+" "+ry+" 0 0 1"+p2.x+" "+p2.y+"L"+cx+" "+cy+"z";
path.setAttribute("d",d)
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
</svg>
这是我在 React
组件中使用的 SVG
扇区路径的编程生成函数:
getSectorPath(x, y, outerDiameter, a1, a2) {
const degtorad = Math.PI / 180;
const halfOuterDiameter = outerDiameter / 2;
const cr = halfOuterDiameter - 5;
const cx1 = (Math.cos(degtorad * a2) * cr) + x;
const cy1 = (-Math.sin(degtorad * a2) * cr) + y;
const cx2 = (Math.cos(degtorad * a1) * cr) + x;
const cy2 = (-Math.sin(degtorad * a1) * cr) + y;
return "M" + x + " " + y + " " + cx1 + " " + cy1 + " A" + cr + " " + cr + " 0 0 1 " + cx2 + " " + cy2 + "Z";
}
这是用法:
<svg width={outerDiameter} height={outerDiameter}>
<path d={this.getSectorPath(outerDiameter / 2, outerDiameter / 2, outerDiameter, 45, 135)} fill="#f00"/>
<path d={this.getSectorPath(outerDiameter / 2, outerDiameter / 2, outerDiameter, 135, 225)} fill="#f00"/>
<path d={this.getSectorPath(outerDiameter / 2, outerDiameter / 2, outerDiameter, 225, 315)} fill="#f00"/>
<path d={this.getSectorPath(outerDiameter / 2, outerDiameter / 2, outerDiameter, 315, 45)} fill="#f00"/>
</svg>
这是我从 JavaScript 创建 svg 的示例。我写它的灵感来自@zmechanic 回答。一个扇区有梯度。 要执行它,只需创建一个 html 文档并复制内容。
<!DOCTYPE html>
<html>
<body>
<div id="svgRoot"></div>
<script type="text/javascript">
const DIAMETER = 200;
const SVG_SIZE = DIAMETER + 12;
const STROKE = "black";
const STROKE_WIDTH = "2";
const getSectorPath = (x, y, outerDiameter, a1, a2) => {
const degtorad = Math.PI / 180;
const cr = outerDiameter / 2;
const cx1 = Math.cos(degtorad * a2) * cr + x;
const cy1 = -Math.sin(degtorad * a2) * cr + y;
const cx2 = Math.cos(degtorad * a1) * cr + x;
const cy2 = -Math.sin(degtorad * a1) * cr + y;
return `M${x} ${y} ${cx1} ${cy1} A${cr} ${cr} 0 0 1 ${cx2} ${cy2}Z`;
};
const svgRoot = document.getElementById("svgRoot");
const pieChartSvgString = `<svg width="${SVG_SIZE}" height="${SVG_SIZE}">
<defs>
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#DD5E89;stop-opacity:1" />
<stop offset="100%" style="stop-color:#F7BB97;stop-opacity:1" />
</linearGradient>
</defs>
<g fill="url(#gradient1)">
<path
stroke="${STROKE}"
strokeWidth="${STROKE_WIDTH}"
d="${getSectorPath(SVG_SIZE / 2, SVG_SIZE / 2 - 5, DIAMETER, 45, 135)}"
/>
</g>
<path
stroke="${STROKE}"
strokeWidth="${STROKE_WIDTH}"
d="${getSectorPath(SVG_SIZE / 2, SVG_SIZE / 2, DIAMETER, 135, 225)}"
fill="#00ff00"
/>
<path
stroke="${STROKE}"
strokeWidth="${STROKE_WIDTH}"
d="${getSectorPath(SVG_SIZE / 2, SVG_SIZE / 2, DIAMETER, 225, 315)}"
fill="#0000ff"
/>
<path
stroke="${STROKE}"
strokeWidth="${STROKE_WIDTH}"
d="${getSectorPath(SVG_SIZE / 2, SVG_SIZE / 2, DIAMETER, 315, 45)}"
fill="#ffff00"
/>
</svg>`;
const svgNode = document.createRange().createContextualFragment(pieChartSvgString);
svgRoot.appendChild(svgNode);
</script>
</body>
</html>