I have 5 checkboxes representing a To-Do list, for every checkbox I click, the circle's diameter should complete 1/5, 2/5 , 3/5, 4/5, 5/5 until it completes the 5 checkboxes, the order of how the checkboxes are created shouldn't matter. If I clicked "first ToDo" and "third ToDo" without selecting "second ToDo" the circle should still show 2/5 of itself.
So far I've been able to make the circle interact with the input of the checkboxes, but I'm struggling to make the visual progress happen, when I click one of the ToDos the progress circle it's marked but not in the right order and portions, also the idea is that the visual progress should move forward to the right until completing the circle.
Looking for information online, I see people uses <circle path>
but it is really confusing for me because some configurations are made through CSS while other people do it in the JS, according to me it is best that I use JS to show the progress, because it should behave in base of the checkboxes.
function update() {
var myBar = document.getElementById("myCircle");
//Reference the Form.
var toDo = document.getElementById("toDo");
//Reference all the CheckBoxes in Table.
boxes = toDo.querySelectorAll("input[type='checkbox']:checked");
checked = boxes.length
myBar.style.strokeDasharray = ((checked) * 100) "%";
if (checked === 0) {
alert("Please select CheckBox(s).");
}
return true;
}
checks = document.querySelectorAll("input[type='checkbox']");
checks.forEach(function(box) {
box.addEventListener("change", function() {
update()
});
});
#myCircle {
width: 0;
height: 30px;
stroke: #A3BBAD;
stroke-width: 3px;
}
#myProgress {
margin: auto;
width: 50%;
stroke: #357266;
stroke-width: 3px;
}
<svg height="100" width="100">
<circle id="myProgress" cx="50" cy="50" r="40" fill="transparent"/>
<circle id="myCircle" cx="50" cy="50" r="40" fill="transparent"/>
</svg>
<br>
<form id="toDo">
<input id="FirstToDo" type="checkbox" value="1" /><label for="FirstToDo">First</label>
<input id="SecondToDo" type="checkbox" value="2" /><label for="SecondToDo">Second</label>
<input id="ThirdToDo" type="checkbox" value="3" /><label for="ThirdToDo">Third</label>
<input id="FourthToDo" type="checkbox" value="4" /><label for="FourthToDo">Fourth</label>
<input id="FifthToDo" type="checkbox" value="5" /><label for="FifthToDo">Fifth</label>
</form>
<script src="Circle.js"></script>
CodePudding user response:
you can
set
pathLength="100"
to say percentage circle take 100% of circle paththe % calculation will be 100 - percent
the css property strokeDashoffset is the one where you can affect percentage with
myBar.style.strokeDashoffset = 100 - percent;
function update() {
var myBar = document.getElementById("myProgress");
//Reference the Form.
var toDo = document.getElementById("toDo");
//Reference all the CheckBoxes in Table.
boxes = toDo.querySelectorAll("input[type='checkbox']:checked");
checked = boxes.length
var percent = (checked) * 20;
myBar.style.strokeDashoffset = 100 - percent;
if (checked === 0) {
alert("Please select CheckBox(s).");
}
return true;
}
checks = document.querySelectorAll("input[type='checkbox']");
checks.forEach(function(box) {
box.addEventListener("change", function() {
update()
});
});
#mySvg {
transform: rotate(-90deg);
}
#myCircle {
width: 0;
height: 30px;
stroke: #A3BBAD;
stroke-width: 3px;
}
#myProgress {
margin: auto;
width: 50%;
stroke: #357266;
stroke-width: 3px;
stroke-dasharray: 100;
stroke-dashoffset: 100;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Circle</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<svg id="mySvg" height="100" width="100">
<circle id="myCircle" cx="50" cy="50" r="40" fill="transparent"/>
<circle id="myProgress" cx="50" cy="50" r="40" fill="transparent" pathLength="100"/>
</svg>
<br>
<form id="toDo">
<input id="FirstToDo" type="checkbox" value="1" /><label for="FirstToDo">First</label>
<input id="SecondToDo" type="checkbox" value="2" /><label for="SecondToDo">Second</label>
<input id="ThirdToDo" type="checkbox" value="3" /><label for="ThirdToDo">Third</label>
<input id="FourthToDo" type="checkbox" value="4" /><label for="FourthToDo">Fourth</label>
<input id="FifthToDo" type="checkbox" value="5" /><label for="FifthToDo">Fifth</label>
</form>
<script src="Circle.js"></script>
</body>
</html>
CodePudding user response:
First, you need to understand that percent unit isn't a fraction of the circle's path. In this context :
If a percentage is used, the value represents a percentage of the current viewport.
[ source w3.org emphasis mine ]
To set a total length to the circle's path, you can use pathLength="100"
(see MDN for more information on the pathLength).
Then, you can adjust your JS to set the strokeDasharray
value on a base 100 for the circle's path lentgh.
Here is a possible approach :
function update() {
var myBar = document.getElementById("myCircle");
//Reference the Form.
var toDo = document.getElementById("toDo");
//Reference all the CheckBoxes in Table.
boxes = toDo.querySelectorAll("input[type='checkbox']:checked");
checked = boxes.length
myBar.style.strokeDasharray = (100 - (checked) * 20) ', 100';
if (checked === 0) {
alert("Please select CheckBox(s).");
}
return true;
}
checks = document.querySelectorAll("input[type='checkbox']");
checks.forEach(function(box) {
box.addEventListener("change", function() {
update()
});
});
svg {
transform: rotate(-90deg);
}
#myCircle {
width: 0;
height: 30px;
stroke: #A3BBAD;
stroke-width: 3px;
}
#myProgress {
margin: auto;
width: 50%;
stroke: #357266;
stroke-width: 3px;
}
<svg height="100" width="100">
<circle id="myProgress" cx="50" cy="50" r="40" fill="transparent"/>
<circle id="myCircle" cx="50" cy="50" r="40" fill="transparent" pathLength="100"/>
</svg>
<br>
<form id="toDo">
<input id="FirstToDo" type="checkbox" value="1" /><label for="FirstToDo">First</label>
<input id="SecondToDo" type="checkbox" value="2" /><label for="SecondToDo">Second</label>
<input id="ThirdToDo" type="checkbox" value="3" /><label for="ThirdToDo">Third</label>
<input id="FourthToDo" type="checkbox" value="4" /><label for="FourthToDo">Fourth</label>
<input id="FifthToDo" type="checkbox" value="5" /><label for="FifthToDo">Fifth</label>
</form>
On a side note, as the circle stroke starts on the right of the cricle by default, I added a rotation (-90deg) to the svg to make the progress bar start at the top of the circle.
CodePudding user response:
Here is a CSS-only solution. For that the inputboxes must come above the circle in the markup and I added some layout hack, but you can adjust it with JS and keep the original markup.
The code uses a conic-gradient based on the sum of 5 CSS variables, which values are controlled by the checkboxes. Again this can be done via JS too if more flexibility is needed. The key point is coloring an area whose angle is based on a sum, rather than coloring 5 predefined areas.
#myProgress {
--task1: 0;
--task2: 0;
--task3: 0;
--task4: 0;
--task5: 0;
}
#FirstToDo:checked ~ #myProgress {
--task1: 1;
}
#SecondToDo:checked ~ #myProgress {
--task2: 1;
}
#ThirdToDo:checked ~ #myProgress {
--task3: 1;
}
#FourthToDo:checked ~ #myProgress {
--task4: 1;
}
#FifthToDo:checked ~ #myProgress {
--task5: 1;
}
.flex {
display: flex;
flex-direction: row;
}
.circle {
order: -1;
width: 100px;
height: 100px;
background: blue;
border-radius: 50%;
mask: radial-gradient(farthest-side, transparent 0 70%, #fff 70% 100%);
-webkit-mask: radial-gradient(farthest-side, transparent 0 70%, #fff 70% 100%);
}
#myProgress {
background-image: conic-gradient(
green 0 calc((var(--task1) var(--task2) var(--task3) var(--task4) var(--task5))* 20%),
transparent 0 100%);
}
<div >
<input id="FirstToDo" type="checkbox" value="1" /><label for="FirstToDo">First</label>
<input id="SecondToDo" type="checkbox" value="2" /><label for="SecondToDo">Second</label>
<input id="ThirdToDo" type="checkbox" value="3" /><label for="ThirdToDo">Third</label>
<input id="FourthToDo" type="checkbox" value="4" /><label for="FourthToDo">Fourth</label>
<input id="FifthToDo" type="checkbox" value="5" /><label for="FifthToDo">Fifth</label>
<div id="myProgress" ></div>
</div>