需要做一个展示统计数据(百分比)的小部件,默认情况下该小部件是隐藏的。页面右边放置一个圆形的按钮,当点击按钮时小部件从右边滑出显示。
本着尽可能多的展示数据而又不失简约的原则,希望在按钮上展示排名前几名数据的值,这样的话,不必手动点击按钮就知道数据的大概情况,增强了按钮的表现力。通过多个同心圆弧可以做到这一点。
要绘制多个圆弧,可先从绘制一个圆弧着手。
其中,点 A A 是圆弧的起点,点 O O 是圆心,点B是圆弧的终点, θ θ 是 OB O B 与 OA O A 的夹角,即 ∠BOA ∠ B O A 。 Y Y 轴的箭头是朝下的,因为SVG使用的是屏幕坐标系。
已知:
1. 圆弧起点 A A 的坐标 (startX,startY) ( s t a r t X , s t a r t Y )
2. 圆弧的半径 R R
3. 圆弧终点 B B 与起点 A A 构成的圆心角 θ θ ,单位为度
4. 圆弧线条的宽度 width w i d t h
5. 圆弧线条的颜色 color c o l o r
为简化问题,先做一些假设:
1. 圆弧起点 A A 为圆弧上 Y Y 坐标值最小的点(在屏幕坐标系中)
2. 只按逆时针方向绘制圆弧(起点到终点)
求
用Javascript动态绘制出符合输入要求的SVG弧形。
SVG中的path
元素可以用来绘复杂的图形。它的形状是通过属性d
来定义的,属性d
的值是”命令+参数”序列。有过在AutoCAD
中使用命令来绘图的应该能很快理解。
这里我们只需要用到两个命令:M
和A
命令:
M
x y M
是 Move的缩写)A
rx ry x-axis-rotation large-arc-flag sweep-flag x y A
命令也可以用来绘制椭圆弧的,如果是圆弧则 rx=ry r x = r y )关于图形样式的控制,path
的stroke
属性用来控制线条的颜色,stroke-width
属性用来控制线条宽度,fill
属性用来控制线条所包围部分的填充颜色,默认是黑色。
根据以上对path
元素的属性d
中M
和A
命令的解说,结合第一部分的问题描述,可以有以下分析结果:
M
命令来移动到圆弧的起点 A(startX,startY) A ( s t a r t X , s t a r t Y ) ,即 M startX startYpath
的stroke-width
属性path
的stroke
属性path
的fill
属性应设为none
,即无填充现在就剩下圆弧的终点坐标 B(x,y) B ( x , y ) 为未知数了。
回忆一下之前学过的平面几何知识,结合问题描述的草图,可得出如下关系式:
SVG中,对于圆弧而言,线条的宽度不包含在半径中,线条宽度是向外延伸的。因此在后续参数的计算中需要考虑到这一点,相关参数需要根据线条宽度来调节。
根据以上分析,编写一个JS函数,生成一个SVGpath
元素对象,如下:
function createSVGPath(startX, startY, R, theta, width, color) {
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
var realR = R - width;
var dArr = ["M" + startX, startY + width, "A" + realR, realR, 0, theta>=180 ? 1 : 0, 0];
var cx = startX, cy = startY + R;
var theta2 = theta%360;
// 避免360度与0度一样的情况
theta = theta > 0 && theta2 == 0 ? 359.9 : theta2;
var alpha = (theta + 90)/180 * Math.PI;
var dx = realR * Math.cos(alpha);
var dy = realR * Math.sin(alpha);
var x = cx + dx, y = cy - dy;
dArr.push(x.toFixed(2));
dArr.push(y.toFixed(2));
var d = dArr.join(" ");
path.setAttribute('d', d);
path.setAttribute('stroke', color);
path.setAttribute('stroke-width', width);
path.setAttribute('fill', 'none');
return path;
}
创建一个html文档,其中包含一个空的svg元素,然后用javascript动态生成一系列圆弧,从360到0度,相邻间隔45度。
源代码如下:
<html>
<head>
<meta charset="utf8">
<title>动态生成SVG圆弧测试title>
head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="150px" width="150px" id="svg-01">
svg>
body>
<script>
function createSVGPath(startX, startY, R, theta, width, color) {
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
var realR = R - width;
var dArr = ["M" + startX, startY + width, "A" + realR, realR, 0, theta>=180 ? 1 : 0, 0];
var cx = startX, cy = startY + R;
var theta2 = theta%360;
// 避免360度与0度一样的情况
theta = theta > 0 && theta2 == 0 ? 359.9 : theta2;
var alpha = (theta + 90)/180 * Math.PI;
var dx = realR * Math.cos(alpha);
var dy = realR * Math.sin(alpha);
var x = cx + dx, y = cy - dy;
dArr.push(x.toFixed(2));
dArr.push(y.toFixed(2));
var d = dArr.join(" ");
path.setAttribute('d', d);
path.setAttribute('stroke', color);
path.setAttribute('stroke-width', width);
path.setAttribute('fill', 'none');
return path;
}
var svg = document.getElementById('svg-01');
var colors = ['red', 'orange', 'yellow', 'green', 'blue', 'cyan', 'purple', 'black'];
for(var i=0; i<8; i++){
var startY = i*5;
var R = 70 - startY;
var theta = 360 - 45* i;
var path = createSVGPath(75, startY, R, theta, 5, colors[i]);
svg.appendChild(path);
}
script>
html>
效果如下: