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 關注
- 128 瀏覽
添加回答
舉報