更新进度圈中的百分比

问题描述

我正在使用此处给出的备选方案之一 (CSS Progress Circle) 构建一个带有进度圈的待办事项列表。在我的 script.js 中,我定义了函数 drawRingProgress(),当我在脚本末尾执行它时呈现画布。

当我的脚本的其他函数被执行以添加任务、编辑、删除或将它们标记为完成时,参数 pendingTaskscompletedTasks 会更新。但是,如果我在其他提到的函数调用函数 drawRingProgress(),为了更新进度,画布会在其他地方多次错误地绘制(取决于这些函数所作用的 HTML 元素)。呈现更新的进度百分比的正确方法是什么?

工作示例链接https://jsfiddle.net/tailslider13/f4qtmhzj/7/

let pendingTasks = 31;
let completedTasks = 69;

function drawRingProgress(pendingTasks,completedTasks) {

    var el = document.getElementById('graph'); // get canvas

    let progress_percentage = Math.floor((completedTasks / (completedTasks + pendingTasks)) * 100) || 0;

    var options = {
      // percent:  el.getAttribute('data-percent') || 25,percent: progress_percentage,// size: 110,size: el.getAttribute('data-size') || 220,linewidth: el.getAttribute('data-line') || 15,rotate: el.getAttribute('data-rotate') || 0
    }

    var canvas = document.createElement('canvas');
    var span = document.createElement('span');
    span.textContent = options.percent + '%';

    if (typeof (G_vmlCanvasManager) !== 'undefined') {
      G_vmlCanvasManager.initElement(canvas);
    }

    var ctx = canvas.getContext('2d');
    canvas.width = canvas.height = options.size;

    el.appendChild(span);
    el.appendChild(canvas);

    ctx.translate(options.size / 2,options.size / 2); // change center
    ctx.rotate((-1 / 2 + options.rotate / 180) * Math.PI); // rotate -90 deg

    //imd = ctx.getimageData(0,240,240);
    var radius = (options.size - options.linewidth) / 3.2;

    var drawCircle = function (color,linewidth,percent) {
      percent = Math.min(Math.max(0,percent || 1),1);
      ctx.beginPath();
      ctx.arc(0,radius,Math.PI * 2 * percent,false);
      ctx.strokeStyle = color;
      ctx.lineCap = 'round'; // butt,round or square
      ctx.linewidth = linewidth
      ctx.stroke();
    };

    drawCircle('#efefef',options.linewidth,100 / 100);
    drawCircle('#046582',options.percent / 100)
}

drawRingProgress(pendingTasks,completedTasks);

解决方法

这是我绘制图形的方式。我已经从中删除了所有其他函数,因此它仅根据您设置的变量显示图形进度。弄清楚其他功能后,您可以通过该方法更新它们。

首先,我会在脚本的开头获取画布,并将变量指定为全局变量。

其次,我会将白色甜甜圈平展。除非您打算以某种方式更改它,否则函数 drawGraph() 将被调用一次,仅此而已。

第三,当您添加、删除或完成任务时,函数 drawRingProgress() 将被其他函数调用。确保这些函数在调用 pendingTasks 之前也更新 completedTasksdrawRingProgress()

drawRingProgress() 中,我添加了文本,因为画布具有内置方法,因此您无需使用 <span>。至于您的所有选项,我为此删除了它们,但您可以将它们添加回您认为合适的位置。

const inputField = document.getElementById("addTask");
const taskList = document.getElementById("taskList");

var canvas = document.getElementById('graph');
var ctx = canvas.getContext('2d');
canvas.width = 200;
canvas.height = 200;
let pendingTasks = 20;
let completedTasks = 5;

//Progress ring
function drawGraph() {
    ctx.beginPath();
    ctx.strokeStyle = "white";
    ctx.arc(canvas.width/2,canvas.height/2,50,Math.PI*2,false);
    ctx.lineCap = 'round'; // butt,round or square
    ctx.lineWidth = 15;
    ctx.stroke();
    ctx.closePath(); 
}
drawGraph();

function drawRingProgress(pendingTasks,completedTasks) {
    let progress_percentage = (completedTasks / pendingTasks) * 100;
    ctx.font = "30px sans-serif";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillStyle = "#046582";
    ctx.fillText(progress_percentage+'%',canvas.width/2,canvas.height/2);
    
    percent = Math.min(Math.max(0,(progress_percentage/100) || 1),1);

    ctx.beginPath();
    ctx.save();
    ctx.translate(0,canvas.height); // change center
    ctx.rotate((-1 / 2 + 0 / 180) * Math.PI); // rotate -90 deg
    ctx.arc(canvas.width/2,Math.PI *  2 * percent,false);
    ctx.strokeStyle = "#046582";
    ctx.lineCap = 'round'; // butt,round or square
    ctx.lineWidth = 15;
    ctx.stroke();
    ctx.restore();
    ctx.closePath();
}
drawRingProgress(pendingTasks,completedTasks);
#body {
  background-color: #046582;
}

header {
  background-color: #f39189;
  padding: 50px;
  margin: 50px;
  position: sticky;
  top: 0px;
}

h1 {
  text-align: center;
}

.listItem {
  margin: 20px 0px;
  background-color: white;
}

.container {
  background-color: #c4c4c4;
}

.taskList {
  list-style-type: none;
  background-color: transparent;
  overflow: hidden;
  margin-top: 150px;
}

.inputContainer {
  margin: 50px;
  padding: 20px;
  max-width: 50%;
  width: 100%;
  background-color: #f39189;
}

#footer {
  text-align: center;
  position: sticky;
  bottom: 0px;
  background-color: #f39189;
  padding: 20px;
}

.deleteButton {
  background-image: url("/content/delete.png");
  background-repeat: no-repeat;
  background-size: cover;
  cursor: pointer;
  width: 30px;
  height: 30px;
  margin: 15px;
}

#addTask {
  font-family: Verdana,Geneva,Tahoma,sans-serif;
  font-size: 1.3rem;
}

.taskName {
  font-family: Verdana,sans-serif;
  font-size: 1.2rem;
}

.listContainer {
  height: 1080px;
}

.inputContainer {
  position: fixed;
}

.checkedbox {
  text-decoration: line-through;
  color: #f39189;
}

/* START Styling Progress ring */

.chart {
  position: relative;
  /* margin:0px; */
  width: 220px;
  height: 220px;
}

canvas {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
}

span {
  color: #046582;
  display: block;
  line-height: 220px;
  text-align: center;
  width: 220px;
  font-family: sans-serif;
  font-size: 30px;
  font-weight: 100;
  margin-left: 5px;
}

/* Links progress ring */
/* https://stackoverflow.com/questions/14222138/css-progress-circle
http://jsfiddle.net/Aapn8/3410/ */

/* END Styling Progress ring */
<!DOCTYPE html>
<html lang="en">

  <head>

    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <title>Document</title>

    <!-- Required meta tags -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous" />

  </head>

  <div class="container">

    <body id="body">

      <header class="row justify-content-end">

        <h1 class="col-4">Take note</h1>

        <!-- Progress ring  -->
        <div class="col-4">
          <canvas class="chart" id="graph"></canvas>
        </div>

      </header>

      <!-- Input field and button -->
      <div class="row inputContainer rounded">
        <input class="col-auto" type="newTask" placeholder="Enter new Task" id="addTask" />
        <button class="col-auto" id="btnAdd">Add</button>
      </div>

      <!-- List of tasks created  -->
      <div class="listContainer">
        <ul class="taskList rounded" id="taskList"></ul>
      </div>

      <footer class="row" id="footer">
        <h6 class="col w-100">2021</h6>
      </footer>

      <!-- BOOTSTRAP -->
      <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous">
      </script>
      <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.min.js" integrity="sha384-lpyLfhYuitXl2zRZ5Bn2fqnhNAKOAaM/0Kr9laMspuaMiZfGmfwRNFh8HlMy49eQ" crossorigin="anonymous">
      </script>

    </body>

  </div>

</html>

我也不确定您使用引导程序图表的意图。我以前没有使用过它,但是从检查文档来看,您似乎并没有真正为它进行适当的编码。另外,您有一个 <div> 与图表类,而不是 <canvas> 在我看来是错误的(但就像我说的那样,我以前没有使用过它)。在此处的示例中,我将其更改为 <canvas> 并删除了您正在创建的画布以及跨度。

希望这就是您想要的,如果不是,也许您仍然可以将我在这里的内容与您想要的内容拼凑起来。

,

嗨,卡洛斯和所有对解决方案感兴趣的人。

在研究代码后,我注意到问题在于每次函数被调用但从未删除时创建元素 spancanvas

解决方案是让这些元素一开始就到位,即在 html 代码中 ||或者在调用函数之前创建一次。

对于变量 pendingTaskscompletedTasks,我建议将它们更改为 pendingTaskstotalAmountOfTasks。 (除非他们可以处于第三种状态。) 那么你输入圆圈的比例是pendingTasks/totalAmountOfTasks

当没有任务时,记得检查除以零!

干杯,

托马斯