How can I make a circle progress bar fluid? - javascript

I’m trying to fluidify a circle progress bar in a timer. The progress bar represents the progression in the duration of the timer. I made an svg <circle> circle element for the progress bar. Every 10th of second, I change the css attribute stroke-dashoffset of the circle. This works fine but if we choose less than 5 minutes for the time, the movements of the progress bar are not fluid. What can I do to make that fluid ?
class Timer {
constructor(circle, text) {
this.circle = circle
this.text = text
this.text.innerHTML
}
async start(hours, minutes, seconds) {
this.circle.style["stroke-dasharray"] = parseInt(Math.PI * this.circle.getBoundingClientRect().width)
this.circle.style["stroke-dashoffset"] = parseInt(Math.PI * this.circle.getBoundingClientRect().width)
await this.countdown()
this.circle.classList = "progress"
var remaining, interval, duration, end;
duration = parseInt(hours) * 3600000 + parseInt(minutes) * 60000 + (parseInt(seconds) + 1) * 1000
end = Date.now() + duration
interval = setInterval(async () => {
remaining = end - Date.now()
this.circle.style["stroke-dashoffset"] = remaining * parseInt(Math.PI * this.circle.getBoundingClientRect().width) / duration;
if (remaining < 0) {
this.circle.style["stroke-dashoffset"] = 0
clearInterval(interval)
window.location.href = "./"
return true
} else {
this.text.innerHTML = `${("0" + parseInt(remaining / 3600000)).slice(-2)}:${("0" + parseInt(remaining % 3600000 / 60000)).slice(-2)}:${("0" + parseInt(remaining % 3600000 % 60000 / 1000)).slice(-2)}`
}
}, 100)
}
countdown() {
var duration;
duration = 2
this.text.innerHTML = 3
return new Promise(resolve => {
setInterval(async () => {
if (duration <= 0) {
resolve(true)
} else {
this.text.innerHTML = duration
duration -= 1
}
}, 1000)
})
}
}
const timer = new Timer(document.getElementById("progress"), document.getElementById("text"))
const params = new URLSearchParams(window.location.search)
timer.start(0, 0, 10)
:root {
--pi: 3.141592653589793
}
circle.progress {
display: block;
position: absolute;
fill: none;
stroke: url(#circle.progress.color);
stroke-width: 4.5vmin;
stroke-linecap: round;
transform-origin: center;
transform: rotate(-90deg);
}
circle.progress.animation {
animation: circle 3s linear forwards;
}
.progress-container {
left: 50vw;
top: 50vh;
width: 90vmin;
height: 90vmin;
margin-top: -45vmin;
margin-left: -45vmin;
position: absolute;
padding: none;
}
.outer {
margin: none;
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: 6px 6px 10px -1px rgba(0, 0, 0, 0.15), -6px -6px 10px -1px rgba(255, 255, 255, 0.7);
padding: 2.5%;
}
.inner {
margin: 2.5%;
width: 95%;
height: 95%;
border-radius: 50%;
box-shadow: inset 4px 4px 6px -1px rgba(0, 0, 0, 0.15), inset -4px -4px 6px -1px rgba(255, 255, 255, 0.7);
}
svg {
display: block;
position: absolute;
left: 50vw;
top: 50vh;
width: 90.5vmin;
height: 90.5vmin;
margin-top: -45.25vmin;
margin-left: -45.25vmin;
}
svg text {
font-size: 10vmin;
font-family: 'Roboto', sans-serif;
}
#keyframes circle {
0% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: calc(45.5vmin * var(--pi) * 2);
}
}
<link href="https://cdn.jsdelivr.net/npm/#materializecss/materialize#1.2.1/dist/css/materialize.min.css" rel="stylesheet"/>
<div class="progress-container">
<div class="outer center-align">
<div class="inner"></div>
</div>
</div>
<svg xmlns="http://www.w3.org/2000/svg" class="center" version="1.1">
<defs>
<linearGradient id="circle.progress.color">
<stop offset="0%" stop-color="BlueViolet" />
<stop offset="100%" stop-color="MediumVioletRed" />
</linearGradient>
</defs>
<circle id="progress" class="progress animation" cy="45.25vmin" cx="45.25vmin" r="42.75vmin" />
<text id="text" text-anchor="middle" x="50%" y="50%">Temps restant</text>
</svg>
<script src="https://cdn.jsdelivr.net/npm/#materializecss/materialize#1.2.1/dist/js/materialize.min.js"></script>
The code here runs the timer for 10 seconds. Normally, you would have to choose the time in another page. To have the time input, go to that page (the page is in french).

I'm not able to explain the issue, so here I have an alternative solution. The function setInterval has issues with the timing (maybe that is actually you issue...). You cannot expect it to be precise. Instead of controlling the progress using setInterval you can use a keyframe animation with the Web Animations API. This is a better alternative to the JavaScript animation where you update an attribute/style, and easier to work with then SVG SMIL animations.
So, I rely on the animation doing its job and then update the time displayed by asking for the currenttime on the animation object.
const progress = document.getElementById('progress');
const text = document.getElementById('text');
document.forms.form01.addEventListener('click', e => {
if(e.target.value){
var progressKeyframes = new KeyframeEffect(
progress,
[
{ strokeDasharray: '0 100' },
{ strokeDasharray: '100 100' }
],
{ duration: parseInt(e.target.value), fill: 'forwards' }
);
var a1 = new Animation(progressKeyframes, document.timeline);
a1.play();
let timer = setInterval(function(){
if(a1.playState == 'running'){
text.textContent = Math.floor(a1.currentTime/1000);
}else if(a1.playState == 'finished'){
text.textContent = Math.round(e.target.value/1000);
clearInterval(timer);
}
}, 100);
}
});
<form name="form01">
<input type="button" value="2000" />
<input type="button" value="5000" />
<input type="button" value="10000" />
</form>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="300">
<defs>
<linearGradient id="circle.progress.color">
<stop offset="0%" stop-color="BlueViolet" />
<stop offset="100%" stop-color="MediumVioletRed" />
</linearGradient>
<filter id="shadow">
<feDropShadow dx=".4" dy=".4" stdDeviation="1" flood-color="gray"/>
</filter>
</defs>
<circle r="40" stroke="white" stroke-width="8" fill="none" transform="translate(50 50)" filter="url(#shadow)"/>
<circle id="progress" r="40" stroke="url(#circle.progress.color)" stroke-width="8" fill="none" pathLength="100" stroke-dasharray="0 100" transform="translate(50 50) rotate(-90)" stroke-linecap="round"/>
<text id="text" dominant-baseline="middle" text-anchor="middle" x="50" y="50">0</text>
</svg>

Related

Re-Center text position as str length changes

I am new to javascript and am having an issue repositioning animated text as the string length varies. I have an SVG element and a string within it, where that string needs to be centered within that SVG. Using ' | ' as a center reference, the centering would look like:
| | |
g g g g g g
If I start the animation with a str of len 3, it will be centered properly for Len 3 strs, but then other lens would be equivalent to:
| |
g g g
Example code:
function animateValue(obj, start, end, duration) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
const str = obj.innerHTML;
// console.log(`${str.length}` );
if (`${str.length}`==="1"){
obj.style.x = '200px';
}
obj.innerHTML = Math.floor(progress * (end - start) + start);
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
const obj = document.getElementById("heading");
animateValue(obj, 100, 0, 5000);
svg {
position: absolute ;
width: 40%;
border: 1px solid rgba(255,255,255,0.3);
margin-left: 30%;
border-radius: 50%;
}
#roseline, #majline {
stroke: #eee;
stroke-width: .5;
}
text {
font-family: Montserrat, sans-serif;
font-size: 10;
fill: #eee;
}
text.heading1{
font-size:4.5em;
fill: #0ee;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" id="compassrose">
<defs>
<symbol>
<line x1="40" y1="250" x2="50" y2="250" id="roseline" />
<line x1="40" y1="250" x2="60" y2="250" id="majline" />
<path d="M10,250a240,240 0 1,0 480,0a240,240 0 1,0 -480,0" id="rosecircle" transform='rotate(90 250 250)' />
</symbol>
</defs>
<div class="triangle-container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" id="compassrose">
<polygon points="250,40 280,0 220,000" class="triangle" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" >
<polygon points="0,260 0,220 40,240" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 500 500" >
<polygon points="500,260 500,220 460,240" />
<text class="heading1" id="heading" x='190px' y='250px'
fontSize="36">100 </text>
</svg>
</div>
</svg>
I have tried re-arranging the divs to allow for the absolute and relative positioning, however that was not properly maintaining size relationships as needed.
If you use dominant-baseline="middle" text-anchor="middle" on the text element and position it in the middle of the SVG (250,250) it should work.
function animateValue(obj, start, end, duration) {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
obj.innerHTML = Math.floor(progress * (end - start) + start);
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
const obj = document.getElementById("heading");
animateValue(obj, 200, 0, 5000);
svg {
display: block;
position: absolute;
width: 40%;
border: 1px solid rgba(255, 255, 255, .3);
margin-left: 30%;
border-radius: 50%;
}
#roseline,
#majline {
stroke: #eee;
stroke-width: .5;
}
text {
font-family: Montserrat, sans-serif;
font-size: 10;
fill: #eee;
}
text.heading1 {
fill: #0ee;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" id="compassrose">
<defs>
<symbol>
<line x1="40" y1="250" x2="50" y2="250" id="roseline" />
<line x1="40" y1="250" x2="60" y2="250" id="majline" />
<path d="M10,250a240,240 0 1,0 480,0a240,240 0 1,0 -480,0"
id="rosecircle" transform='rotate(90 250 250)' />
</symbol>
</defs>
<polygon points="250,40 280,0 220,000" class="triangle" />
<polygon points="0,260 0,220 40,240" />
<polygon points="500,260 500,220 460,240" />
<text class="heading1" id="heading" x="250" y="250" font-size="60"
dominant-baseline="middle" text-anchor="middle">100</text>
</svg>

Creating a infinite forward SVG animation with CSS and JavaScript

I want to create a spin load component that infinitely loops adding more dashoffset to the stroke
Here is a example:
const spinLoad = setInterval(() => {
const loading = document.querySelector('#loading')
loading.style.strokeDashoffset = `${Number(loading.style.strokeDashoffset) + 800}`
}, 1000)
#loading {
width: 100px;
overflow: visible;
position: absolute;
z-index: 2;
fill: transparent;
stroke: #f1f1f1;
stroke-width: 0.5rem;
stroke-dasharray: 130px;
stroke-dashoffset: 0;
transition: ease-in-out 1s;
top: calc(50% - 50px);
left: calc(50% - 50px);
filter: drop-shadow(0px 3px 2px rgba(0, 0, 0, 0.6));
}
<svg id="loading" viewBox="0 0 240 240" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="icon" d="M42.3975 90C72.7709 37.3917 87.9576 11.0876 108.479 3.61845C121.734 -1.20615 136.266 -1.20615 149.521 3.61845C170.042 11.0876 185.229 37.3917 215.603 90C245.976 142.608 261.163 168.912 257.37 190.419C254.921 204.311 247.655 216.895 236.849 225.963C220.12 240 189.747 240 129 240C68.2532 240 37.8798 240 21.1507 225.963C10.3448 216.895 3.07901 204.311 0.629501 190.419C-3.16266 168.912 12.024 142.608 42.3975 90Z" />
</svg>
But this have three problems:
It takes too much long for start spinning
It spin at different speed sometimes, like lagging when go to other tab
Sometimes happens a bug and the speed go CRAZY
Anyone knows how to build this spinning load? I would be very thankful
:D
When doing animations it's much better to use requestAnimationFrame to control it. In this case it's best to remove the css transition because adding two different ways of animating the same thing causes chaos. Then just keep adding to the dash-offset in the frame function.
const loading = document.querySelector('#loading')
let loadingAnimation;
const spinLoad = (time) => {
const speed = 0.7 // lower number goes slower
loading.style.strokeDashoffset = time * speed
loadingAnimation = requestAnimationFrame(spinLoad)
}
loadingAnimation = requestAnimationFrame(spinLoad);
#loading {
width: 100px;
overflow: visible;
position: absolute;
z-index: 2;
fill: transparent;
stroke:#f1f1f1;
stroke-width: 0.5rem;
stroke-dasharray: 130px;
stroke-dashoffset: 0;
top: calc(50% - 50px);
left: calc(50% - 50px);
filter: drop-shadow(0px 3px 2px rgba(0, 0, 0, 0.6));
}
<svg
id="loading"
viewBox="0 0 240 240"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
id="icon"
d="M42.3975 90C72.7709 37.3917 87.9576 11.0876 108.479 3.61845C121.734 -1.20615 136.266 -1.20615 149.521 3.61845C170.042 11.0876 185.229 37.3917 215.603 90C245.976 142.608 261.163 168.912 257.37 190.419C254.921 204.311 247.655 216.895 236.849 225.963C220.12 240 189.747 240 129 240C68.2532 240 37.8798 240 21.1507 225.963C10.3448 216.895 3.07901 204.311 0.629501 190.419C-3.16266 168.912 12.024 142.608 42.3975 90Z"
/>
</svg>

progressbar.js with image on the inside and text on the stroke

I want to reproduce the progress bar in the image below
but I am having trouble with the center image and the text on the side. I am using progressbar.js and this is where I am at right now:
// progressbar.js#1.0.0 version is used
// Docs: http://progressbarjs.readthedocs.org/en/1.0.0/
var bar = new ProgressBar.Circle(container, {
strokeWidth: 20,
easing: 'easeInOut',
duration: 1400,
color: 'url(#gradient)',
trailColor: '#eee',
trailWidth: 1,
svgStyle: null
});
let linearGradient = `
<defs>
<linearGradient id="gradient" x1="100%" y1="0%" x2="0%" y2="0%" gradientUnits="userSpaceOnUse">
<stop offset="20%" stop-color="mediumaquamarine"/>
<stop offset="50%" stop-color="turquoise"/>
</linearGradient>
</defs>
`
let percentage_text = "<span>100%</span>";
bar.svg.insertAdjacentHTML('afterBegin', linearGradient);
bar.svg.insertAdjacentHTML('beforeend', percentage_text);
bar.animate(1.0); // Number from 0.0 to 1.0
#container {
margin: 20px;
width: 200px;
height: 200px;
}
span{
position: absolute;
z-index: 9;
width: 90px;
height: 90px;
color: black;
}
<script src="https://rawgit.com/kimmobrunfeldt/progressbar.js/1.0.0/dist/progressbar.js"></script>
<div id="container"></div>
I have tried inserting the percentage as a span but it doesn't show, for the image I think I can add another div on top of this one with the image in the center but it wouldn't be perfectly centered and might overlap the progress bar.
Any help would go a long way, and if there is another better library to imitate the image it would be perfect. thanks in advance.
I hope this helps you. Clean the code as you want!
Add an id for second path in svg for percentage:
let path = document.querySelector('svg path:last-child').setAttribute("id", "MyPath");
Add the step key to your bar:
step: (state, bar) => {
var value = Math.round(bar.value() * 100);
update(value)
}
Add an update method to handle the percentage on the path:
function update(val) {
if (!bar) return;
if (document.getElementById('text-tp'))
document.getElementById('text-tp').outerHTML = "";
bar.svg.insertAdjacentHTML('beforeend',
`<text id="text-tp"> <textPath id="tp" href="#MyPath">${val}%</textPath> </text>`);
let tp = document.getElementById('tp');
if (val > 7)
tp.setAttributeNS(null, "startOffset", val - 7 + "%");
else tp.setAttributeNS(null, "startOffset", val + "%");
}
Add clipPath to your defs part
<clipPath id="circleView">
<circle cx="50" cy="50" r="30" fill="none" />
</clipPath>
Add image to your svg:
bar.svg.insertAdjacentHTML('beforeend',
` <image
width="250"
height="150" xlink:href="https://www.amrita.edu/sites/default/files/news-images/new/news-events/images/l-nov/grass.jpg"
clip-path="url(#circleView)"
/>`);
Full code is:
var bar = new ProgressBar.Circle(container, {
strokeWidth: 20,
easing: 'easeInOut',
duration: 4400,
color: 'url(#gradient)',
trailColor: '#eee',
trailWidth: 1,
svgStyle: null,
step: (state, bar) => {
var value = Math.round(bar.value() * 100);
update(value)
}
});
let linearGradient = `
<defs>
<linearGradient id="gradient" x1="100%" y1="0%" x2="0%" y2="0%" gradientUnits="userSpaceOnUse">
<stop offset="20%" stop-color="mediumaquamarine"/>
<stop offset="50%" stop-color="turquoise"/>
</linearGradient>
<clipPath id="circleView">
<circle cx="50" cy="50" r="30" fill="none" />
</clipPath>
</defs>
`
bar.svg.insertAdjacentHTML('afterBegin', linearGradient);
let path = document.querySelector('svg path:last-child').setAttribute("id", "MyPath");
bar.svg.insertAdjacentHTML('beforeend',
` <image
width="250"
height="150" xlink:href="https://www.amrita.edu/sites/default/files/news-images/new/news-events/images/l-nov/grass.jpg"
clip-path="url(#circleView)"
/>`);
function update(val) {
if (!bar) return;
if (document.getElementById('text-tp'))
document.getElementById('text-tp').outerHTML = "";
bar.svg.insertAdjacentHTML('beforeend',
`<text id="text-tp"> <textPath id="tp" href="#MyPath">${val}%</textPath> </text>`);
let tp = document.getElementById('tp');
if (val > 7)
tp.setAttributeNS(null, "startOffset", val - 7 + "%");
else tp.setAttributeNS(null, "startOffset", val + "%");
}
bar.animate(1.0); // Number from 0.0 to 1.0
#container {
margin: 20px;
width: 200px;
height: 200px;
}
span {
position: absolute;
z-index: 9;
width: 90px;
height: 90px;
color: black;
}
#text-tp {
position: absolute;
font-size: 8px;
}
<script src="https://rawgit.com/kimmobrunfeldt/progressbar.js/1.0.0/dist/progressbar.js"></script>
<div id="container"></div>

Javscript to dynamically build HTML/SVG for mobile view

I have a full screen SVG image/mask reveal; it works great on desktop but it doesn't scale/fit on mobile devices. So im guessing the best solution would be to dynamically build the SVG and SVG container depending on screen width and have a mobile & desktop build. So i'm looking for advice and the best solution and if someone could point me in the right direction or know of an example I can look at.
// DRAW SVG MASK /////////////////////////////
var svg = document.querySelector("#svg__bg");
var tl = new gsap.timeline({ onUpdate: onUpdate });
var pt = svg.createSVGPoint();
var ratio = 0.5625;
tl.to("#masker", {duration: 2, attr: {r: 2400}, ease: "power2.in" });
tl.reversed(true);
function mouseHandler() {
tl.reversed(!tl.reversed());
}
function getPoint(evt) {
pt.x = evt.clientX;
pt.y = evt.clientY;
return pt.matrixTransform(svg.getScreenCTM().inverse());
}
function mouseMove(evt) {
var newPoint = getPoint(evt);
gsap.set("#dot", { cx: newPoint.x, cy: newPoint.y });
gsap.to("#ring, #masker", {duration: 0.88, attr: { cx: newPoint.x, cy: newPoint.y }, ease: "power2.out" });
}
function onUpdate() {
var prog = (tl.progress() * 100);
}
function newSize() {
var w = window.innerWidth;
var h = window.innerHeight;
if (w > h * (16 / 9)) {
gsap.set("#svg__bg", { attr: { width: w, height: w * ratio } });
} else {
gsap.set("#svg__bg", {
attr: {
width: h / ratio,
height: h
}
});
}
var data = svg.getBoundingClientRect();
gsap.set("#svg__bg", {
x: w / 2 - data.width / 2
});
gsap.set("#svg__bg", {
y: h / 2 - data.height / 2
});
}
window.addEventListener("mousedown", mouseHandler);
window.addEventListener("mouseup", mouseHandler);
window.addEventListener("mousemove", mouseMove);
newSize();
window.addEventListener("resize", newSize);
#import url('https://fonts.googleapis.com/css?family=Montserrat:700&display=swap');
body {
min-height: 100vh;
background-color: #1F242D;
cursor: none;
overflow: hidden;
font-family: 'Montserrat', sans-serif;
}
.intro-svg {
position: relative;
padding: 0;
margin: 0;
width: 100%;
min-height: 100vh;
height: calc(var(--vh, 1vh) * 100);
overflow: hidden;
z-index: 1;
}
.svg__container {
height: 100%;
width: 100%;
position: relative;
overflow: hidden;
}
.svg__image {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
cursor: none;
}
.text-svg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
cursor: none;
z-index:2;
}
.impact-text {
font-size: 7em;
line-height: 1.2;
text-transform: uppercase;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js"></script>
<div class="intro-svg">
<div class="svg__container">
<svg id="svg__bg" class="svg__image" xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="1600" height="900" viewBox="0 0 1600 900">
<defs>
<radialGradient id="mask-gradient">
<stop offset="80%" stop-color="#fff"/><stop offset="100%" stop-color="#000" />
</radialGradient>
<mask id="the-mask">
<circle id="masker" r="250" fill="url(#mask-gradient)" cx="800" cy="450">
</circle>
</mask>
<mask id="mask-text" width="100" height="100" x="0" y="0">
<text id="masker" class="impact-text row-1" fill="none" stroke="#fff" stroke-width="3" x="10.1%" y="42%">A</text>
<text id="masker" class="impact-text row-2" fill="white" x="10%" y="55%">Digital</text>
<text id="masker" class="impact-text row-3" fill="white" x="10%" y="68%">Designer</text>
</mask>
</defs>
<image id="lines" xlink:href="https://i.imgur.com/1TQRj56.jpg" x="0" y="0" width="1600" height="900" />
<g id="mask-reveal" mask="url(#the-mask)">
<image id="regular" xlink:href="https://i.imgur.com/7VtEKv3.jpg" x="0" y="0" width="1600" height="900" />
</g>
<g mask="url(#mask-text)">
<image id="text-before" xlink:href="https://i.imgur.com/7VtEKv3.jpg" x="0" y="0" width="1600" height="900" />
</g>
<circle id="ring" r="20" fill="none" stroke="#D74A53" stroke-width="2" cx="800" cy="450" />
<circle id="dot" r="4" fill="#D74A53" cx="800" cy="450"/>
</svg>
</div>
</div>
just add
#svg__bg {
width: 100%;
height:auto;
}
andchange them with media query
don't forget to change
<svg id="svg__bg" class="svg__image" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 1600 900">
without width="1600" height="900"

Circular countdown JavaScript from 10 to Zero

I have been trying to reverse the countdown in this demo from 10 down to zero Without luck.
I have tried reversing the countdown by doing this:
(1*(initialOffset/time))-initialOffset )
It did reverse the animated circle but not the countdown.
Any ideas?
Thanks
var time = 10;
var initialOffset = '440';
var i = 1
/* Need initial run as interval hasn't yet occured... */
$('.circle_animation').css('stroke-dashoffset', initialOffset-(1*(initialOffset/time)));
var interval = setInterval(function() {
$('h2').text(i);
if (i == time) {
clearInterval(interval);
return;
}
$('.circle_animation').css('stroke-dashoffset', initialOffset-((i+1)*(initialOffset/time)));
i++;
}, 1000);
.item {
position: relative;
float: left;
}
.item h2 {
text-align:center;
position: absolute;
line-height: 125px;
width: 100%;
}
svg {
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440; /* this value is the pixel circumference of the circle */
stroke-dashoffset: 440;
transition: all 1s linear;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="item">
<h2>0</h2>
<svg width="160" height="160" xmlns="http://www.w3.org/2000/svg">
<circle id="circle" class="circle_animation" r="69.85699" cy="81" cx="81" stroke-width="8" stroke="#6fdb6f" fill="none"/>
</svg>
</div>
Here is also a codepen copy:
https://codepen.io/kaolay/pen/LRVxKd
Try $('h2').text(time - i); instead of $('h2').text(i);
I also added $('h2').text(time); as the 4th line to draw 10 at the beginning
Also, the first part of the circle is not animated in your code, so I changed this line:
$('.circle_animation').css('stroke-dashoffset', initialOffset-(1*(initialOffset/time)));
To this block:
$('.circle_animation').css('stroke-dashoffset', initialOffset);
setTimeout(() => {
$('.circle_animation').css('stroke-dashoffset', initialOffset-(1*(initialOffset/time)));
})
var time = 10;
var initialOffset = '440';
var i = 1;
$('h2').text(time); // adding 10 at the beginning if needed
/* Need initial run as interval hasn't yet occured... */
$('.circle_animation').css('stroke-dashoffset', initialOffset);
setTimeout(() => {
$('.circle_animation').css('stroke-dashoffset', initialOffset-(1*(initialOffset/time)));
})
var interval = setInterval(function() {
$('h2').text(time - i); // here is the clue
if (i == time) {
clearInterval(interval);
return;
}
$('.circle_animation').css('stroke-dashoffset', initialOffset-((i+1)*(initialOffset/time)));
i++;
}, 1000);
.item {
position: relative;
float: left;
}
.item h2 {
text-align:center;
position: absolute;
line-height: 125px;
width: 100%;
}
svg {
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440; /* this value is the pixel circumference of the circle */
stroke-dashoffset: 440;
transition: all 1s linear;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="item">
<h2>0</h2>
<svg width="160" height="160" xmlns="http://www.w3.org/2000/svg">
<circle id="circle" class="circle_animation" r="69.85699" cy="81" cx="81" stroke-width="8" stroke="#6fdb6f" fill="none"/>
</svg>
</div>
If you update this line $('h2').text(time - i); then you'll get the numeric countdown. I also initalize i = 0 so that the starting number is 10:
var time = 10;
var initialOffset = '440';
var i = 0
/* Need initial run as interval hasn't yet occured... */
$('.circle_animation').css('stroke-dashoffset', initialOffset-(1*(initialOffset/time)));
var interval = setInterval(function() {
$('h2').text(time - i);
if (i == time) {
clearInterval(interval);
return;
}
$('.circle_animation').css('stroke-dashoffset', initialOffset-((i+1)*(initialOffset/time)));
i++;
}, 1000);
.item {
position: relative;
float: left;
}
.item h2 {
text-align:center;
position: absolute;
line-height: 125px;
width: 100%;
}
svg {
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440; /* this value is the pixel circumference of the circle */
stroke-dashoffset: 440;
transition: all 1s linear;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="item">
<h2>0</h2>
<svg width="160" height="160" xmlns="http://www.w3.org/2000/svg">
<circle id="circle" class="circle_animation" r="69.85699" cy="81" cx="81" stroke-width="8" stroke="#6fdb6f" fill="none"/>
</svg>
</div>
What exactly are you asking here?
"It did reverse the animated circle but not the countdown."
you are just trying to countdown?
why not set i = 10 and then do i--
If you want to invert the animation just invert all states of initial values and change i to (time-i). So it goes like this:
<div class="item">
<h2>10</h2>
<svg width="160" height="160" xmlns="http://www.w3.org/2000/svg">
<circle id="circle" class="circle_animation" r="69.85699" cy="81" cx="81" stroke-width="8" stroke="#6fdb6f" fill="none"/>
</svg>
</div>
.item {
position: relative;
float: left;
}
.item h2 {
text-align:center;
position: absolute;
line-height: 125px;
width: 100%;
}
svg {
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440; /* this value is the pixel circumference of the circle */
stroke-dashoffset: 0;
transition: all 1s linear;
}
var time = 10;
var initialOffset = 440;
var i = 0
/* Need initial run as interval hasn't yet occured... */
$('.circle_animation').css('stroke-dashoffset', 0);
var interval = setInterval(function() {
$('h2').text(time-i);
if (i == time) {
clearInterval(interval);
return;
}
$('.circle_animation').css('stroke-dashoffset', initialOffset*i/time);
i++;
}, 1000);
Code pen:
https://codepen.io/anon/pen/Xybpge

Categories