1 回答
TA贡献1826条经验 获得超6个赞
当你处于 时,你实现它的方式总是有一些“问题” 0%
。如果您希望什么都没有,0%
并且在百分比增长时保持一致,那么您不想使用ctx.lineJoin = "round"
作为解决方法,您可以使用arc()方法来绘制圆角。
关于arc(x, y, radius, startAngle, endAngle)
,我们知道x = r
,y = r
并且radius = r
z
我们只需要一些几何计算就可以得到所需的值startAngle
(α)和endAngle
(α+Δ)。
利用三角函数余弦,我们有Math.cos(θ) = (r - p) / r
⇒ θ = Math.acos((r - p) / r)
。
我们有并且 α = Math.PI - θ
我们知道 Δ = 2 * θ
⇒(α+Δ) = Math.PI + θ
所以最后:
startAngle α = Math.PI - Math.acos((r - p) / r)
endAngle (α+Δ) = Math.PI + Math.acos((r - p) / r)
在我们的例子中,r = h /2
当p < r
⇔时p < h / 2
,我们得到:
ctx.arc(h / 2, h / 2, h / 2, Math.PI - Math.acos((h - 2 * p) / h), Math.PI + Math.acos((h - 2 * p) / h)) ctx.fillStyle = '#FF1700'; ctx.fill();
const canvas = $("#progressBar");
const ctx = canvas.get(0).getContext("2d");
const h = 100;
const p = 30;
/* To visalize ------------------------------------------------------*/
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI / 2, 3 / 2 *Math.PI);
ctx.lineTo(500, 0);
ctx.arc((h / 2) + 500, h / 2, h / 2, 3 / 2 *Math.PI,Math.PI / 2);
ctx.lineTo(h / 2, h);
ctx.strokeStyle = '#000000';
ctx.stroke();
ctx.closePath();
/* ------------------------------------------------------------------*/
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI - Math.acos((h - 2 * p) / h), Math.PI + Math.acos((h - 2 * p) / h));
ctx.fillStyle = '#FF1700';
ctx.fill();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="progressBar" width="750" height="120">
</canvas>
现在,如果我们想要这种外观(红色部分,我们想要骑上灰色部分)。该方法包括执行相同的操作,但只进行一半的进度,然后对称地重复相同的图形(阴影区域)。
为了绘制对称形状,我们将使用ctx.scale(-1, 1)和 方法save() restore()。第二个圆弧中心的 x 位置将是- (r - p)⇔ -((h / 2) - p),就像我们将在水平对称中工作一样,它最终将是(h / 2) - p
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
ctx.save();
ctx.scale(-1, 1);
ctx.arc((h / 2) - p, h / 2, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
ctx.restore();
ctx.fillStyle = '#FF1700';
ctx.fill();
const canvas = $("#progressBar");
const ctx = canvas.get(0).getContext("2d");
const h = 100;
const p = 25;
/* To visalize ------------------------------------------------------*/
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI / 2, 3 / 2 *Math.PI);
ctx.lineTo(500, 0);
ctx.arc((h / 2) + 500, h / 2, h / 2, 3 / 2 *Math.PI,Math.PI / 2);
ctx.lineTo(h / 2, h);
ctx.strokeStyle = '#000000';
ctx.stroke();
ctx.closePath();
/* ------------------------------------------------------------------*/
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
ctx.save();
ctx.scale(-1, 1);
ctx.arc((h / 2) - p, h / 2, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
ctx.restore();
ctx.fillStyle = '#FF1700';
ctx.fill();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="progressBar" width="750" height="120">
</canvas>
p <= h在我们需要更改代码以考虑矩形部分之前,情况都是如此。我们将使用 if...else 来做到这一点。
if(p <= h){
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
ctx.save();
ctx.scale(-1, 1);
ctx.arc((h / 2) - p, h / 2, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
ctx.restore();
ctx.fillStyle = '#FF1700';
ctx.fill();
} else {
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI / 2, 3 / 2 *Math.PI);
ctx.lineTo(p - 2 * h, 0);
ctx.arc(p - (h / 2), h / 2, h / 2, 3 / 2 *Math.PI,Math.PI / 2);
ctx.lineTo(h / 2, h);
ctx.fillStyle = '#FF1700';
ctx.fill();
}
const canvas = $("#progressBar");
const ctx = canvas.get(0).getContext("2d");
const h = 100;
const p = 350;
/* To visalize ------------------------------------------------------*/
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI / 2, 3 / 2 *Math.PI);
ctx.lineTo(500, 0);
ctx.arc((h / 2) + 500, h / 2, h / 2, 3 / 2 *Math.PI,Math.PI / 2);
ctx.lineTo(h / 2, h);
ctx.strokeStyle = '#000000';
ctx.stroke();
ctx.closePath();
/* ------------------------------------------------------------------*/
if(p <= h){
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
ctx.save();
ctx.scale(-1, 1);
ctx.arc((h / 2) - p, h / 2, h / 2, Math.PI - Math.acos((h - p) / h), Math.PI + Math.acos((h - p) / h));
ctx.restore();
ctx.fillStyle = '#FF1700';
ctx.fill();
} else {
ctx.beginPath();
ctx.arc(h / 2, h / 2, h / 2, Math.PI / 2, 3 / 2 *Math.PI);
ctx.lineTo(p - 2 * h, 0);
ctx.arc(p - (h / 2), h / 2, h / 2, 3 / 2 *Math.PI,Math.PI / 2);
ctx.lineTo(h / 2, h);
ctx.fillStyle = '#FF1700';
ctx.fill();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="progressBar" width="750" height="120">
</canvas>
现在,我们可以总结一下了:
const canvas = $("#progressBar");
const ctx = canvas.get(0).getContext("2d");
const canvasWidth = ctx.canvas.width;
const canvasHeight = ctx.canvas.height;
class progressBar {
constructor(dimension, color, percentage){
({x: this.x, y: this.y, width: this.w, height: this.h} = dimension);
this.color = color;
this.percentage = percentage / 100;
this.p;
}
static clear(){
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
}
draw(){
// Visualize -------
this.visualize();
// -----------------
this.p = this.percentage * this.w;
if(this.p <= this.h){
ctx.beginPath();
ctx.arc(this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, Math.PI - Math.acos((this.h - this.p) / this.h), Math.PI + Math.acos((this.h - this.p) / this.h));
ctx.save();
ctx.scale(-1, 1);
ctx.arc((this.h / 2) - this.p - this.x, this.h / 2 + this.y, this.h / 2, Math.PI - Math.acos((this.h - this.p) / this.h), Math.PI + Math.acos((this.h - this.p) / this.h));
ctx.restore();
ctx.closePath();
} else {
ctx.beginPath();
ctx.arc(this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, Math.PI / 2, 3 / 2 *Math.PI);
ctx.lineTo(this.p - this.h + this.x, 0 + this.y);
ctx.arc(this.p - (this.h / 2) + this.x, this.h / 2 + this.y, this.h / 2, 3 / 2 * Math.PI, Math.PI / 2);
ctx.lineTo(this.h / 2 + this.x, this.h + this.y);
ctx.closePath();
}
ctx.fillStyle = this.color;
ctx.fill();
}
visualize(){
if (wholeprogressbar.checked === true){
this.showWholeProgressBar();
}
}
showWholeProgressBar(){
ctx.beginPath();
ctx.arc(this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, Math.PI / 2, 3 / 2 * Math.PI);
ctx.lineTo(this.w - this.h + this.x, 0 + this.y);
ctx.arc(this.w - this.h / 2 + this.x, this.h / 2 + this.y, this.h / 2, 3 / 2 *Math.PI, Math.PI / 2);
ctx.lineTo(this.h / 2 + this.x, this.h + this.y);
ctx.strokeStyle = '#000000';
ctx.stroke();
ctx.closePath();
}
get PPercentage(){
return this.percentage * 100;
}
set PPercentage(x){
this.percentage = x / 100;
}
}
// We create new progress bars
progressbar2 = new progressBar({x: 10, y: 10, width: 400, height: 35}, "#FF1700", 50);
// progressbar2.draw(); ---> No need coz we draw them later
progressbar = new progressBar({x: 10, y: 60, width: 400, height: 35}, "#FF1700", 0);
// progressbar.draw(); ---> No need coz we draw them later
// For showing the current percentage (just for example)
setInterval(function() {
let currentPercentage = progressbar.PPercentage;
document.getElementById("percentage").innerHTML = `${Math.round(currentPercentage)} %`;
}, 20);
// We draw the progress-bars (just for example, one fix at 50% and one moving on a range from 0 to 100 %)
let i=0;
setInterval(function() {
const start = 0;
const end = 100;
const step = 0.3;
progressbar.PPercentage = i * step;
progressBar.clear();
progressbar.draw();
progressbar2.draw();
i++;
if(progressbar.PPercentage > end){
i = start;
}
}, 20);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="progressBar" width="420" height="100"></canvas>
<div>
<p> Progression at <span id="percentage"></span></p>
<input type="checkbox" id="wholeprogressbar" name="wholeprogressbar" onclick="progressbar.draw()">
<label for="wholeprogressbar">Visualize all the progress bar (100%)</label>
</div>
编辑 :
要创建进度条,您只需创建一个新实例
progressbar = new progressBar({x: PositionXinTheCanvas, y: PositionYinTheCanvas, width: WidthOfTheProgressBar, height: HeightOfTheProgressBar}, "ColorOfTheProgressBar", CurrentProgression);
..然后画它
progressbar.draw();
如果需要清除画布,请调用该clear()
方法。如果您想要为进度条设置动画,您将需要它。由于它是静态方法,因此您需要在类上调用它progressBar
:
progressBar.clear();
TA贡献1836条经验 获得超13个赞
我开始修复你的代码,最后重写了它。以下是有关如何更好地解决此问题的一些重要事项。
您确实应该创建一个函数来在特定位置和长度处绘制一条线。然后你就有了空间一次可以干净地完成一行所需的所有数学运算。
function drawLine(x, y, length) { /* ... */ }
如果您希望线条长 40 像素,但角半径为 40,那么您想描画长度和宽度为零的单个点,半径为 20,这样整体宽度为 40,圆更小。
// Get length of line that will be stroked
let innerLength = length - cornerRadius * 2
// If the line would have a length less than zero, set the length to zero.
if (innerLength < 0) innerLength = 0
// If the innerLength is less than the corner diameter, reduce the corner radius to fit.
let actualCornerRadius = cornerRadius
if (length < cornerRadius * 2) {
actualCornerRadius = length / 2
}
由于您正在绘制一条描边线,而不是一个矩形,因此它简化了一些数学运算,以便能够仅从起点到终点绘制一条线。
// Find the left and right endpoints of the inner line.
const leftX = x + actualCornerRadius
const rightX = leftX + innerLength
// Draw the path and then stroke it.
ctx.beginPath()
ctx.moveTo(leftX, y)
ctx.lineTo(rightX, y)
ctx.stroke()
最后,要在开放路径笔划上放置圆角笔划,只需将lineCap上下文的属性设置为'round'。
ctx.lineCap = "round";
- 1 回答
- 0 关注
- 97 浏览
添加回答
举报