亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

帶有圓角末端的畫布矩形(進度條) - 值較低的問題

帶有圓角末端的畫布矩形(進度條) - 值較低的問題

小唯快跑啊 2023-09-25 16:56:10
我正在嘗試制作一個寬度取決于百分比的矩形,直到我用 0% 測試某些內容為止,它都可以正常工作。我希望它在 0% 時消失,但由于我選擇了圓角,所以存在最小寬度。對于較低的百分比數字,同樣的問題也很明顯,根據我所收集的信息,如果百分比低于 6%,它會以相反的方式推動對象,此時矩形會變成圓形,并且不能再變小。有解決方法嗎?我一心只想著它,目前只需要解決這個問題。const canvas = $("#progressBar");const ctx = canvas.get(0).getContext("2d");// rectWidth = 630 * percent / 100 (in this case 100%)const rectX = 60;const rectY = 10;const rectWidth = 630 * 100 / 100;const rectHeight = 38;const cornerRadius = 37;ctx.lineJoin = "round";ctx.lineWidth = cornerRadius;ctx.strokeStyle = '#FF1700';ctx.fillStyle = '#FF1700';ctx.strokeRect(rectX + (cornerRadius / 2), rectY + (cornerRadius / 2), rectWidth - cornerRadius, rectHeight - cornerRadius);ctx.fillRect(rectX + (cornerRadius / 2), rectY + (cornerRadius / 2), rectWidth - cornerRadius, rectHeight - cornerRadius);// rectWidth = 630 * percent / 100 (in this case 0%)const rectX2 = 60;const rectY2 = 60;const rectWidth2 = 630 * 0 / 100;const rectHeight2 = 38;const cornerRadius2 = 37;ctx.lineJoin = "round";ctx.lineWidth = cornerRadius;ctx.strokeStyle = '#FF1700';ctx.fillStyle = '#FF1700';ctx.strokeRect(rectX2 + (cornerRadius2 / 2), rectY2 + (cornerRadius2 / 2), rectWidth2 - cornerRadius2, rectHeight2 - cornerRadius2);ctx.fillRect(rectX2 + (cornerRadius2 / 2), rectY2 + (cornerRadius2 / 2), rectWidth2 - cornerRadius2, rectHeight2 - cornerRadius2);<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><canvas id="progressBar" width="750" height="120"></canvas>
查看完整描述

1 回答

?
躍然一笑

TA貢獻1826條經驗 獲得超6個贊

當你處于 時,你實現它的方式總是有一些“問題”?0%。如果您希望什么都沒有,0%并且在百分比增長時保持一致,那么您不想使用ctx.lineJoin = "round"

作為解決方法,您可以使用arc()方法來繪制圓角。

關于arc(x, y, radius, startAngle, endAngle),我們知道x = r,y = r并且radius = r

https://img1.sycdn.imooc.com/652953a400015c6805000372.jpgz

我們只需要一些幾何計算就可以得到所需的值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 /2p < 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>

現在,如果我們想要這種外觀(紅色部分,我們想要騎上灰色部分)。該方法包括執行相同的操作,但只進行一半的進度,然后對稱地重復相同的圖形(陰影區域)。

https://img1.sycdn.imooc.com/65114b760001eb0b05100387.jpg

為了繪制對稱形狀,我們將使用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();


查看完整回答
反對 回復 2023-09-25
?
開心每一天1111

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";

單擊此處查看工作演示。



查看完整回答
反對 回復 2023-09-25
  • 1 回答
  • 0 關注
  • 128 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號