进度圆圈 - 在圆圈的末端画一个小圆弧 + 更多
Progress circle - draw a small arc at the end tip of the circle + more
如何在进度圆圈的末端绘制一个小圆圈,并在其下方/上方添加一个小文本块?
示例图片:
<div class="radial-progress" data-progress="0">
<div class="circle">
<div class="img"></div>
<div class="mask full">
<div class="fill"></div>
</div>
<div class="mask half">
<div class="fill"></div>
<div class="fill fix"></div>
</div>
<div class="shadow"></div>
</div>
<div class="inset">
<div class="percentage">
<div class="numbers"><span>-</span><span>0%</span><span>1%</span> <!--- lots of spans --->
</div>
</div>
</div>
感谢 Andre 的 medium.com 文章,我已经对他的版本进行了一些编辑 - 更新版本说明了我希望针对给定的 % 值动态实现的目标:http://codepen.io/Inlesco/pen/pgKXeG
然而,编译后的CSS实在太多了。对于 100 中的每 %,将有太多 CSS 用于定位事物。当前的 CSS(@codepen 示例)已经重约 50 KB。不是最佳做法。
我已经在基于 JS Canvas 的变体上取得了一些进展,将 vert&horz 居中的 img 定位在 canvas 之上。但它真的是一个好看的响应式网站的唯一方法和最佳实践吗?
CSS 绝对不是执行此操作的正确工具,我强烈建议您放弃该想法。 Canvas 绝对是一个不错的选择,但对于响应式网站,我建议您使用 SVG。使用 SVG,可以很容易地根据用户输入绘制进度圈,并在其尖端添加 circle/dot。
以下是首先创建进度圈必须执行的步骤:
- 获取进度圆(0 到 100)的用户输入,然后根据它计算圆弧的角度。公式为 (input * 360/100) 因为圆有 360 度。
- SVG 圆弧通常从时钟中的 3 位置开始,因此计算出的圆弧角度必须在负方向上偏移 90 度。也就是说,弧度必须是从-90度到270度。
- 使用公式 -(角度(度数)* PI / 180)将计算出的角度转换为弧度。
一旦计算出以弧度为单位的角度,使用简单的三角函数根据角度在圆上找到一个点:
- X 坐标 = Cos(弧度角)* 半径 + 中心点的 X 坐标。
- Y 坐标 = Sin(角度弧度) * 半径 + 中心点的 Y 坐标。
找到点后,我们需要创建路径,从上一步找到的点开始,到圆心,然后到起点圆弧并从该点再次绘制圆弧到原点。以这种方式创建路径是因为我们需要路径在所需点结束(因为我们将标记附加到终点)。
- 创建圆弧需要注意的一点是,任何 > 180 度的角度都需要两个圆弧命令来创建。第一个弧将是时钟中 12 的位置到 6 的位置,下一个弧将用于其余部分。因此,我们使用 if/else 循环。
要在其顶端添加dot/circle,需要做以下工作:
- 一个
marker
元素被添加到 SVG 中,并且使用 circle
元素创建了一个圆圈形式的标记。 circle
元素具有 3 个属性 - cx、cy 是圆点的中心点,r 是圆点的半径。
- 然后使用
marker-end
属性将此标记添加到 path
。设置此属性将意味着创建的任何路径都会在其结束点自动附加此点。不需要为此进行其他编码,因为 SVG 会自动处理定位。
也可以使用 text
元素添加文本,然后可以使用 x
和 y
属性设置其位置。 (这个位仍然需要在下面的代码片段中进行调整。)
演示:
下面是一个非常粗略的实现演示。
window.onload = function() {
var btn = document.querySelector('button'),
inp = document.querySelector('#progress'),
path = document.querySelector('#p'),
text = document.querySelector('#val'),
rect = document.querySelector('rect'),
output = document.querySelector('#path-output');
var x = 0,
y = 0,
r = 30,
cx = 50,
cy = 50,
d = '',
fill = 'yellowgreen',
stroke = 'transparent';
btn.addEventListener('click', function() {
progress = (inp.value == '') ? 0 : inp.value;
arcRad = ((progress * 360 / 100) - 90) * Math.PI / 180;
x = Math.cos(arcRad) * r + cx;
y = Math.sin(arcRad) * r + cy;
if (progress > 0 && progress <= 50) {
d = 'M' + x + ',' + y + ' L' + cx + ',' + cy + ' L' + cx + ',' + (cy - r) + ' A' + r + ',' + r + ' 0 0,1' + x + ',' + y;
setColors(fill, stroke);
setOutput();
} else if (progress > 50 && progress <= 100) {
d = 'M' + x + ',' + y + ' L' + cx + ',' + cy + ' L' + cx + ',' + (cy - r) + ' A' + r + ',' + r + ' 0 0,1' + cx + ',' + (cy + r) + ' A' + r + ',' + r + ' 0 0,1' + x + ',' + y;
setColors(fill, stroke);
setOutput();
} else {
text.innerHTML = '';
path.setAttribute('d', '');
output.innerHTML = 'Enter a value between 0 and 100';
setColors('transparent', 'transparent');
}
if (progress > 0 && progress <= 10) {
rect.setAttribute('x', x);
rect.setAttribute('y', y + 7.5);
text.setAttribute('x', x + 2);
text.setAttribute('y', y + 15);
} else if (progress > 10 && progress <= 62) {
rect.setAttribute('x', x - 5);
rect.setAttribute('y', y + 7.5);
text.setAttribute('x', x - 3.5);
text.setAttribute('y', y + 15);
} else if (progress > 62 && progress <= 100) {
rect.setAttribute('x', x - 5);
rect.setAttribute('y', y - 15);
text.setAttribute('x', x - 3.5);
text.setAttribute('y', y - 7.5);
}
});
function setColors(fill, stroke) {
rect.setAttribute('fill', fill);
rect.setAttribute('stroke', stroke);
path.setAttribute('fill', fill);
path.setAttribute('stroke', stroke);
}
function setOutput() {
path.setAttribute('d', d);
text.innerHTML = progress;
output.innerHTML = 'Angle in Radians: ' + arcRad + '<br/>';
output.innerHTML += 'Point in Circle: ' + x + ',' + y + '<br/>';
output.innerHTML += 'Path d attribute: ' + d;
}
}
svg {
width: 200px;
height: 200px;
}
.output {
min-height: 20px;
}
h4 {
border-bottom: 1px solid;
}
<input id='progress' type='number' />
<button>Generate</button>
<br/>
<svg viewBox='0 0 100 100'>
<defs>
<marker id='dot' viewBox='0 0 10 10' markerHeight='10' markerWidth='10' refX='5' refY='5'>
<circle cx='5' cy='5' r='2' />
</marker>
</defs>
<path d='' marker-end='url(#dot)' id='p' stroke='transparent' fill='transparent' />
<rect height='10' width='10' x='10' y='10' stroke='transparent' fill='transparent' />
<text x='10' y='10' id='val' font-family='Arial' font-size='6'></text>
</svg>
<div class='output'>
<h4>Output:</h4>
<output id='path-output'></output>
</div>
进一步阅读:
您可以在以下链接中阅读有关 SVG 及其元素和属性的更多信息:
如何在进度圆圈的末端绘制一个小圆圈,并在其下方/上方添加一个小文本块?
示例图片:
<div class="radial-progress" data-progress="0">
<div class="circle">
<div class="img"></div>
<div class="mask full">
<div class="fill"></div>
</div>
<div class="mask half">
<div class="fill"></div>
<div class="fill fix"></div>
</div>
<div class="shadow"></div>
</div>
<div class="inset">
<div class="percentage">
<div class="numbers"><span>-</span><span>0%</span><span>1%</span> <!--- lots of spans --->
</div>
</div>
</div>
感谢 Andre 的 medium.com 文章,我已经对他的版本进行了一些编辑 - 更新版本说明了我希望针对给定的 % 值动态实现的目标:http://codepen.io/Inlesco/pen/pgKXeG
然而,编译后的CSS实在太多了。对于 100 中的每 %,将有太多 CSS 用于定位事物。当前的 CSS(@codepen 示例)已经重约 50 KB。不是最佳做法。
我已经在基于 JS Canvas 的变体上取得了一些进展,将 vert&horz 居中的 img 定位在 canvas 之上。但它真的是一个好看的响应式网站的唯一方法和最佳实践吗?
CSS 绝对不是执行此操作的正确工具,我强烈建议您放弃该想法。 Canvas 绝对是一个不错的选择,但对于响应式网站,我建议您使用 SVG。使用 SVG,可以很容易地根据用户输入绘制进度圈,并在其尖端添加 circle/dot。
以下是首先创建进度圈必须执行的步骤:
- 获取进度圆(0 到 100)的用户输入,然后根据它计算圆弧的角度。公式为 (input * 360/100) 因为圆有 360 度。
- SVG 圆弧通常从时钟中的 3 位置开始,因此计算出的圆弧角度必须在负方向上偏移 90 度。也就是说,弧度必须是从-90度到270度。
- 使用公式 -(角度(度数)* PI / 180)将计算出的角度转换为弧度。
一旦计算出以弧度为单位的角度,使用简单的三角函数根据角度在圆上找到一个点:
- X 坐标 = Cos(弧度角)* 半径 + 中心点的 X 坐标。
- Y 坐标 = Sin(角度弧度) * 半径 + 中心点的 Y 坐标。
找到点后,我们需要创建路径,从上一步找到的点开始,到圆心,然后到起点圆弧并从该点再次绘制圆弧到原点。以这种方式创建路径是因为我们需要路径在所需点结束(因为我们将标记附加到终点)。
- 创建圆弧需要注意的一点是,任何 > 180 度的角度都需要两个圆弧命令来创建。第一个弧将是时钟中 12 的位置到 6 的位置,下一个弧将用于其余部分。因此,我们使用 if/else 循环。
要在其顶端添加dot/circle,需要做以下工作:
- 一个
marker
元素被添加到 SVG 中,并且使用circle
元素创建了一个圆圈形式的标记。circle
元素具有 3 个属性 - cx、cy 是圆点的中心点,r 是圆点的半径。 - 然后使用
marker-end
属性将此标记添加到path
。设置此属性将意味着创建的任何路径都会在其结束点自动附加此点。不需要为此进行其他编码,因为 SVG 会自动处理定位。
也可以使用 text
元素添加文本,然后可以使用 x
和 y
属性设置其位置。 (这个位仍然需要在下面的代码片段中进行调整。)
演示:
下面是一个非常粗略的实现演示。
window.onload = function() {
var btn = document.querySelector('button'),
inp = document.querySelector('#progress'),
path = document.querySelector('#p'),
text = document.querySelector('#val'),
rect = document.querySelector('rect'),
output = document.querySelector('#path-output');
var x = 0,
y = 0,
r = 30,
cx = 50,
cy = 50,
d = '',
fill = 'yellowgreen',
stroke = 'transparent';
btn.addEventListener('click', function() {
progress = (inp.value == '') ? 0 : inp.value;
arcRad = ((progress * 360 / 100) - 90) * Math.PI / 180;
x = Math.cos(arcRad) * r + cx;
y = Math.sin(arcRad) * r + cy;
if (progress > 0 && progress <= 50) {
d = 'M' + x + ',' + y + ' L' + cx + ',' + cy + ' L' + cx + ',' + (cy - r) + ' A' + r + ',' + r + ' 0 0,1' + x + ',' + y;
setColors(fill, stroke);
setOutput();
} else if (progress > 50 && progress <= 100) {
d = 'M' + x + ',' + y + ' L' + cx + ',' + cy + ' L' + cx + ',' + (cy - r) + ' A' + r + ',' + r + ' 0 0,1' + cx + ',' + (cy + r) + ' A' + r + ',' + r + ' 0 0,1' + x + ',' + y;
setColors(fill, stroke);
setOutput();
} else {
text.innerHTML = '';
path.setAttribute('d', '');
output.innerHTML = 'Enter a value between 0 and 100';
setColors('transparent', 'transparent');
}
if (progress > 0 && progress <= 10) {
rect.setAttribute('x', x);
rect.setAttribute('y', y + 7.5);
text.setAttribute('x', x + 2);
text.setAttribute('y', y + 15);
} else if (progress > 10 && progress <= 62) {
rect.setAttribute('x', x - 5);
rect.setAttribute('y', y + 7.5);
text.setAttribute('x', x - 3.5);
text.setAttribute('y', y + 15);
} else if (progress > 62 && progress <= 100) {
rect.setAttribute('x', x - 5);
rect.setAttribute('y', y - 15);
text.setAttribute('x', x - 3.5);
text.setAttribute('y', y - 7.5);
}
});
function setColors(fill, stroke) {
rect.setAttribute('fill', fill);
rect.setAttribute('stroke', stroke);
path.setAttribute('fill', fill);
path.setAttribute('stroke', stroke);
}
function setOutput() {
path.setAttribute('d', d);
text.innerHTML = progress;
output.innerHTML = 'Angle in Radians: ' + arcRad + '<br/>';
output.innerHTML += 'Point in Circle: ' + x + ',' + y + '<br/>';
output.innerHTML += 'Path d attribute: ' + d;
}
}
svg {
width: 200px;
height: 200px;
}
.output {
min-height: 20px;
}
h4 {
border-bottom: 1px solid;
}
<input id='progress' type='number' />
<button>Generate</button>
<br/>
<svg viewBox='0 0 100 100'>
<defs>
<marker id='dot' viewBox='0 0 10 10' markerHeight='10' markerWidth='10' refX='5' refY='5'>
<circle cx='5' cy='5' r='2' />
</marker>
</defs>
<path d='' marker-end='url(#dot)' id='p' stroke='transparent' fill='transparent' />
<rect height='10' width='10' x='10' y='10' stroke='transparent' fill='transparent' />
<text x='10' y='10' id='val' font-family='Arial' font-size='6'></text>
</svg>
<div class='output'>
<h4>Output:</h4>
<output id='path-output'></output>
</div>
进一步阅读:
您可以在以下链接中阅读有关 SVG 及其元素和属性的更多信息: