seconds in a circle corner (like a clock) - javascript

I'm trying to create a circle that has small pins around (like seconds in a clock) to be like 60 of them (to count a minute)
here is a picture to describe what I mean
https://www.123rf.com/photo_91759207_stock-vector-close-up-of-digital-timer-showing-time-that-is-running-out-only-25-seconds-left-clock-on-vector-illu.html
I'm using React, javascript, css,
how can I make a loop that each pin would be stack to the circle 'corner' to fit his place ?
I'm having really hard time to find a way how to arrange them to look like it.
my final goal is to create a component that will receive a fill as prop that will represent the number of pins that needs to be in a different color, so I need a way to be able to control the background-color of each pin.
any advice would be awesome. thanks!

Do you mean something like the following? The code will create 60 "pins" for all tags with the class clock.
window.onload = function() {
var clocks = document.getElementsByClassName('clock'),
r = 0, i, j, d, clock;
for(j=0;j<clocks.length;j++) {
clock = clocks[j]
for(i=0;i<60;i++) {
d = document.createElement('div');
d.style.transform = "rotate("+ r +"deg)";
clock.appendChild(d);
r += 6;
}
}
}
.clock {
position:relative;
width:180px;
height:180px;
background:#eee;
}
.clock > div {
position:absolute;
margin-left:87px;
width:6px;
height:160px;
bottom:10px;
background: linear-gradient(to bottom, #491 16px, transparent 16px);
}
<div class="clock"></div>

Drawing the 'clock face' itself is easily done with SVG and stroke-dasharray.
Animating the clock can be done with an SVG mask, and some javascript to change the stroke-dashoffset.
An explanation of the maths for coming up with the values for the stroke-dasharray can be found in this answer.
const maskCircle = document.querySelector(".mask");
const clockText = document.querySelector(".clock-text");
const r = 50;
const c = 2 * r * Math.PI;
let secondsLeft = 60;
window.setInterval(function() {
if (secondsLeft > 0) {
secondsLeft--;
clockText.innerText = secondsLeft;
maskCircle.style.strokeDashoffset =
maskCircle.style.strokeDashoffset - c / 60 * -1;
} else {
clearInterval();
}
}, 1000);
body {
background: black;
}
.clock {
margin: 0 auto;
position: relative;
width: 200px;
height: 200px;
border-radius: 50%;
overflow: hidden;
}
.clock-face {
stroke-width: 15;
stroke-linecap: butt;
fill: transparent;
stroke-dasharray: 2.236 3;
}
.grey {
stroke: #333;
}
.white {
stroke: white;
}
.mask {
stroke-dasharray: 314.15 314.15;
stroke-dashoffset: 0;
}
.clock-text {
width: 100%;
margin: 0 auto;
color: white;
text-align: center;
position: absolute;
top: 50%;
font-size: 6em;
transform: translateY(-50%);
}
<div class="clock">
<svg viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="mask">
<circle class="clock-face white mask" cx="50" cy="50" r="50" transform="rotate(-90.5 50 50)" />
</mask>
</defs>
<circle class="clock-face grey" cx="50" cy="50" r="50" />
<circle class="clock-face white" cx="50" cy="50" r="50" mask="url(#mask)" />
</svg>
<div class="clock-text">60</div>
</div>

Related

Custom cursor not growing/scaling on hover [duplicate]

Hellol,I have a code for a custom cursor, and the cursor, which is a ball/circle, was supposed to grow/scale when hovering over a link, if you see the code below, this function is there, but it is not working, does anyone know what's wrong? Thank you in advance. Note, I am unable to create a snippet here. The code is from codepen: https://codepen.io/clementGir/pen/RQqvQx
<div class="cursor">
<div class="cursor__ball cursor__ball--big ">
<svg height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="0"></circle>
</svg>
</div>
<div class="cursor__ball cursor__ball--small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"></circle>
</svg>
</div>
</div>
<style>
body .cursor {
pointer-events: none;
}
body .cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 1000;
}
body .cursor__ball circle {
fill: #f7f8fa;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<script>
const $bigBall = document.querySelector('.cursor__ball--big');
const $smallBall = document.querySelector('.cursor__ball--small');
const $hoverables = document.querySelectorAll('a');
// Listeners
document.body.addEventListener('mousemove', onMouseMove);
for (let i = 0; i < $hoverables.length; i++) {
if (window.CP.shouldStopExecution(0)) break;
$hoverables[i].addEventListener('mouseenter', onMouseHover);
$hoverables[i].addEventListener('mouseleave', onMouseHoverOut);
}
// Move the cursor
window.CP.exitedLoop(0); function onMouseMove(e) {
TweenMax.to($bigBall, .4, {
x: e.clientX - 15,
y: e.clientY - 15
});
TweenMax.to($smallBall, .1, {
x: e.clientX - 5,
y: e.clientY - 7
});
}
// Hover an element
function onMouseHover() {
TweenMax.to($bigBall, .3, {
scale: 4
});
}
function onMouseHoverOut() {
TweenMax.to($bigBall, .3, {
scale: 1
});
}
</script>```
Growing cursor on hovering a link.
The codepen you are trying to copy links to external scripts and has been adapted to work inside codepen.
What I did to get the pen to work here (I think you only needed step 4):
For CSS & JS clicked down arrow top right, view compiled
Copy over all code
Settings>JS>External Scripts>Copy link into Stack Overflow snippet's external scripts
Removed 'window.CP' code junk which I assume is something to do with codepen and getting the window object of their sub-document
const $bigBall = document.querySelector('.cursor__ball--big');
const $smallBall = document.querySelector('.cursor__ball--small');
const $hoverables = document.querySelectorAll('.hoverable');
// Listeners
document.body.addEventListener('mousemove', onMouseMove);
for (let i = 0; i < $hoverables.length; i++) {
$hoverables[i].addEventListener('mouseenter', onMouseHover);
$hoverables[i].addEventListener('mouseleave', onMouseHoverOut);
}
// Move the cursor
function onMouseMove(e) {
TweenMax.to($bigBall, .4, {
x: e.pageX - 15,
y: e.pageY - 15 });
TweenMax.to($smallBall, .1, {
x: e.pageX - 5,
y: e.pageY - 7 });
}
// Hover an element
function onMouseHover() {
TweenMax.to($bigBall, .3, {
scale: 4 });
}
function onMouseHoverOut() {
TweenMax.to($bigBall, .3, {
scale: 1 });
}
body {
height: 100vh;
background: #010101;
cursor: none;
margin: 0;
display: flex;
font-family: monospace;
}
body h1,
body p,
body a {
color: #fff;
}
body a {
border-bottom: 2px solid #fff;
padding: 10px 0;
margin-top: 25px;
}
body .cursor {
pointer-events: none;
}
body .cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 1000;
}
body .cursor__ball circle {
fill: #f7f8fa;
}
body .left,
body .right {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
body .right {
background: #fff;
}
body .right a {
border-bottom: 2px solid #000;
}
body .right h1,
body .right p,
body .right a {
color: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script>
<div class="cursor">
<div class="cursor__ball cursor__ball--big ">
<svg height="30" width="30">
<circle cx="15" cy="15" r="12" stroke-width="0"></circle>
</svg>
</div>
<div class="cursor__ball cursor__ball--small">
<svg height="10" width="10">
<circle cx="5" cy="5" r="4" stroke-width="0"></circle>
</svg>
</div>
</div>
<div class="left">
<h1>Hello</h1>
<p>Check out this link:</p>
<a class="hoverable">Hover meh</a>
</div>
<div class="right">
<h1>Hello</h1>
<p>Check out this link:</p>
<a class="hoverable">Hover meh</a>
</div>

lazy line painter as a pre-loader

I have an animated SVG using lazy line painter that I would like to use as a pre-loader. How can I review the page content (with a transition or a simple fade-in) after the line animation is completed?
So the concept is this: on landing LOAD SVG animation when COMPLETE transition into page content.
<!-- Include lazylinepainter -->
<script src="https://cdn.jsdelivr.net/npm/ lazy-line-painter#1.9.4/lib/lazy-line-painter-1.9.4.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/d3js/5.15.0/d3.min.js"></script>
<script type="text/javascript">
(function(){
document.onreadystatechange = () => {
if (document.readyState === 'complete') {
let el = document.querySelector('#markin2');
let myAnimation = new LazyLinePainter(el, {"ease":"easeLinear","strokeWidth":2.2,"strokeOpacity":1,"strokeColor":"#fff"});
myAnimation.paint();
}
}
})();
</script>
<svg version="1.1" id="markin2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="314.094px" height="314.765px" viewBox="0 0 314.094 314.765" enable-background="new 0 0 314.094 314.765" xml:space="preserve" data-llp-composed="true" class="lazy-line-painter">
<circle fill="none" stroke-miterlimit="10" cx="157.828" cy="157.404" r="150.813" data-llp-id="markin2-0" data-llp-duration="2920" data-llp-delay="0" fill-opacity="1" data-llp-stroke-join="" data-llp-stroke-cap=""/>
<path id="#markin2" class=".markin2" fill="none" values="#000" stroke-miterlimit="10" d="M18.482,132.273
c1.019-4.147,1.774-8.38,3.099-12.427c7.988-24.405,24.611-39.774,49.798-44.317c26.582-4.795,47.878,5.064,64.031,26.525
c2.094,2.782,2.112,4.714-0.499,7.183c-3.496,3.306-6.592,7.035-10.095,10.837c-2.364-3.506-4.425-7.039-6.94-10.211
C104.843,93.427,81.91,88.297,62.884,97.426c-18.967,9.102-29.097,30.539-24.289,51.399c4.649,20.172,22.977,34.573,43.973,34.571
c13.572-0.002,24.751-5.104,33.906-15.18c22.505-24.771,44.815-49.735,67.885-73.971c17.361-18.239,39.164-24.295,63.441-17.369
c24.221,6.91,39.282,23.516,45.102,48.006c3.184,13.398,1.887,26.967-2.008,39.987c-1.322,4.422-5.715,7.985-8.899,11.78
c-10.94,13.035-24.349,21.988-41.47,24.67c-19.018,2.979-35.86-2.006-50.829-13.86c-1.162-0.92-2.304-1.863-4.361-3.533
c0,2.48-0.004,4.178,0,5.873c0.045,22.324,0.009,44.646,0.213,66.967c0.037,3.956-0.771,7.102-3.877,9.674
c-1.653,1.37-3.243,2.961-4.409,4.748c-2.552,3.912-5.974,4.865-10.668,3.637c0-6.688,0.01-13.473-0.003-20.258
c-0.041-22.488,0.024-44.979-0.227-67.465c-0.056-4.814,1.073-8.443,4.622-11.914c6.039-5.908,11.509-12.398,17.593-19.056
c4.06,8.205,9.36,14.85,16.678,19.736c15.507,10.355,35.698,10.172,51.128-0.49c15.202-10.506,22.338-29.406,18.086-47.903
c-3.979-17.298-19.188-31.573-37.106-33.975c-15.415-2.066-29.11,2.093-39.841,13.781c-21.17,23.057-42.371,46.092-63.192,69.461
c-14.497,16.271-31.743,25.988-53.992,25.404c-30.517-0.799-56.945-24.744-60.94-55.143c-0.123-0.934-0.602-1.821-0.915-2.73
C18.482,140.272,18.482,136.272,18.482,132.273z" data-llp-id="markin2-1" data-llp-duration="2920" data-llp-delay="0" fill-opacity="1" data-llp-stroke-join="" data-llp-stroke-cap=""/>
</svg>
CSS
body, html {
background: #000;
position: absolute;
width: 100%;
height: 100%;
top:0;
left:0;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
#markin2 {
width: 40vw;
height: 40vh;
position: relative;
overflow: visible;
}
.markin2 {
animation: stroke_fill 4s linear forwards, changeColor 2s, forwards;
stroke-dasharray: 1538.2169189453125px;
stroke-dashoffset: 0;
}
#keyframes stroke_fill {
0% {
fill: white;
}
50% {
fill: white;
stroke-dashoffset: 0;
}
100% {
fill: black;
stroke-dashoffset: 0;
}
}
#keyframes changeColor {
from{ fill: rgba(0,0,0,0);}
to{ fill: rgba(0,0,0,1)}
}
Here is the SVG animation example:
https://codepen.io/cpawl/pen/zYvEqYq
The Lazy Line Painter library has some custom events built in to which you can listen. Such as the complete event whenever you animation has been completed. Whenever that event is called, hide the SVG.
In the example below I've added this event listener but also wrapped your animation in a Promise. This way you can wait for it to finish and then do something. And also another Promise which listens for the load event on the document. So that this is an actual page loader and the SVG would not disappear before the page would be loaded.
In your HTML first load the SVG and then the JS you see below here. Inside the then callback function either delete your SVG, or set a class to the body which hides the class. Anyway you see fit.
body,
html {
background: #000;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
#markin2 {
width: 20vw;
height: 20vh;
position: relative;
overflow: visible;
}
.markin2 {
animation: stroke_fill 4s linear forwards, changeColor 2s, forwards;
stroke-dasharray: 1538.2169189453125px;
stroke-dashoffset: 0;
}
#keyframes stroke_fill {
0% {
fill: white;
}
50% {
fill: white;
stroke-dashoffset: 0;
}
100% {
fill: black;
stroke-dashoffset: 0;
}
}
#keyframes changeColor {
from {
fill: rgba(0, 0, 0, 0);
}
to {
fill: rgba(0, 0, 0, 1);
}
}
.hsvg {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
width: 100%;
height: 100%;
background: #000000;
z-index: 999;
transition: 2000ms ease-in-out;
transition-property: opacity, visibility;
}
body.is-loaded .hsvg {
opacity: 0;
visibility: hidden;
}
.main-content {
color: #fff;
}
<script src="https://cdn.jsdelivr.net/npm/lazy-line-painter#1.9.6/lib/lazy-line-painter-1.9.6.min.js"></script>
<!-- Include lazylinepainter -->
<div class="hsvg">
<svg version="1.1" id="markin2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="314.094px" height="314.765px" viewBox="0 0 314.094 314.765" enable-background="new 0 0 314.094 314.765" xml:space="preserve"
data-llp-composed="true" class="lazy-line-painter">
<circle fill="none" stroke-miterlimit="10" cx="157.828" cy="157.404" r="150.813" data-llp-id="markin2-0" data-llp-duration="2920" data-llp-delay="0" fill-opacity="1" data-llp-stroke-join="" data-llp-stroke-cap=""/>
<path id="#markin2" class=".markin2" fill="none" values="#000" stroke-miterlimit="10" d="M18.482,132.273
c1.019-4.147,1.774-8.38,3.099-12.427c7.988-24.405,24.611-39.774,49.798-44.317c26.582-4.795,47.878,5.064,64.031,26.525
c2.094,2.782,2.112,4.714-0.499,7.183c-3.496,3.306-6.592,7.035-10.095,10.837c-2.364-3.506-4.425-7.039-6.94-10.211
C104.843,93.427,81.91,88.297,62.884,97.426c-18.967,9.102-29.097,30.539-24.289,51.399c4.649,20.172,22.977,34.573,43.973,34.571
c13.572-0.002,24.751-5.104,33.906-15.18c22.505-24.771,44.815-49.735,67.885-73.971c17.361-18.239,39.164-24.295,63.441-17.369
c24.221,6.91,39.282,23.516,45.102,48.006c3.184,13.398,1.887,26.967-2.008,39.987c-1.322,4.422-5.715,7.985-8.899,11.78
c-10.94,13.035-24.349,21.988-41.47,24.67c-19.018,2.979-35.86-2.006-50.829-13.86c-1.162-0.92-2.304-1.863-4.361-3.533
c0,2.48-0.004,4.178,0,5.873c0.045,22.324,0.009,44.646,0.213,66.967c0.037,3.956-0.771,7.102-3.877,9.674
c-1.653,1.37-3.243,2.961-4.409,4.748c-2.552,3.912-5.974,4.865-10.668,3.637c0-6.688,0.01-13.473-0.003-20.258
c-0.041-22.488,0.024-44.979-0.227-67.465c-0.056-4.814,1.073-8.443,4.622-11.914c6.039-5.908,11.509-12.398,17.593-19.056
c4.06,8.205,9.36,14.85,16.678,19.736c15.507,10.355,35.698,10.172,51.128-0.49c15.202-10.506,22.338-29.406,18.086-47.903
c-3.979-17.298-19.188-31.573-37.106-33.975c-15.415-2.066-29.11,2.093-39.841,13.781c-21.17,23.057-42.371,46.092-63.192,69.461
c-14.497,16.271-31.743,25.988-53.992,25.404c-30.517-0.799-56.945-24.744-60.94-55.143c-0.123-0.934-0.602-1.821-0.915-2.73
C18.482,140.272,18.482,136.272,18.482,132.273z" data-llp-id="markin2-1" data-llp-duration="2920" data-llp-delay="0" fill-opacity="1" data-llp-stroke-join="" data-llp-stroke-cap=""/>
</svg>
</div>
<script>
const pageLoad = new Promise(resolve => {
window.addEventListener('load', resolve);
});
const animationLoad = new Promise(resolve => {
let el = document.querySelector('#markin2');
let myAnimation = new LazyLinePainter(el, {
"ease": "easeLinear",
"strokeWidth": 2.2,
"strokeOpacity": 1,
"strokeColor": "#fff"
});
myAnimation.paint();
myAnimation.on('complete', resolve);
});
Promise.all([pageLoad, animationLoad]).then(function() {
document.body.classList.add('is-loaded');
console.log('Load event fired and animation done');
});
</script>
<div class="main-content">
<h1>IS THIS THING ON?</h1>
</div>

How to programmatically build curves in svg path?

I'm trying to build a line chart using an SVG, but I'm having a hard time making the path have nice curves. As much as internet told me, the best way to accomplish this is using Q and T, but I don't really get the intended result.
Here are two of the versions I have built, first one doesn't have curves, but the second one overflows the svg container for some reason.
*, *:after, *:before {
box-sizing: border-box;
position: relative;
font-family: 'Montserrat', sans-serif;
}
svg {
width: 30vmin; height: 30vmin;
fill: none;
position: relative;
z-index: 3;
border: 2px solid red;
}
path {
stroke-width: 3px;
// stroke-dasharray: 4;
}
button {
left: 0; top: 0;
position: absolute;
}
html, body {
overflow: hidden;
width: 100%; height: 100%;
display: flex;
align-items: center;
justify-content: space-around;
margin: 0; padding: 0;
background-color: #333ddd;
}
<svg viewBox="0 0 100 100" preserveAspectRatio="none"><path d="M0,16.372727272727275 Q7.6923076923076925,29.09090909090908 15.384615384615385,29.09090909090908 23.076923076923077,2.7363636363636346 30.76923076923077,42.73636363636363 38.46153846153846,69.0909090909091 46.15384615384615,56.37272727272727 53.84615384615385,56.37272727272727 61.53846153846154,82.73636363636363 69.23076923076923,95.45454545454545 76.92307692307692,82.73636363636363 84.61538461538461,82.73636363636363 92.3076923076923,42.73636363636363 T100,42.73636363636363" fill="none" stroke="white" vector-effect="non-scaling-stroke"></path></svg>
<svg viewBox="0 0 100 100" preserveAspectRatio="none"><path d="M0,16.372727272727275 Q 7.6923076923076925,29.09090909090908 15.384615384615385,29.09090909090908 T 23.076923076923077,2.7363636363636346 30.76923076923077,42.73636363636363 38.46153846153846,69.0909090909091 46.15384615384615,56.37272727272727 53.84615384615385,56.37272727272727 61.53846153846154,82.73636363636363 69.23076923076923,95.45454545454545 76.92307692307692,82.73636363636363 84.61538461538461,82.73636363636363 92.3076923076923,42.73636363636363 100,42.73636363636363" fill="none" stroke="white" vector-effect="non-scaling-stroke"></path></svg>
<svg viewBox="0 0 100 100" preserveAspectRatio="none"><path d="M0,16.372727272727275 7.6923076923076925,29.09090909090908 15.384615384615385,29.09090909090908 23.076923076923077,2.7363636363636346 30.76923076923077,42.73636363636363 38.46153846153846,69.0909090909091 46.15384615384615,56.37272727272727 53.84615384615385,56.37272727272727 61.53846153846154,82.73636363636363 69.23076923076923,95.45454545454545 76.92307692307692,82.73636363636363 84.61538461538461,82.73636363636363 92.3076923076923,42.73636363636363 100,42.73636363636363" fill="none" stroke="white" vector-effect="non-scaling-stroke"></path></svg>
Any ideas what am I doing wrong or how to approach this better?

Converting seconds in mm:ss in running JavaScript Timer

I'm just trying to update a friend's timer, which we did for an event a while ago. The timer works perfectly and already does everything it should (see and run Snippet). However, now I would like to display the format mm:ss instead of only seconds (eg. 180 -> 3:00). This must of course be able to count down further. Different approaches I googled have failed. Has anyone an idea how to solve my problem elegantly?
Edit: You can start and stop the timer by pressing 'Space'
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
body {
background-color: #333;
font-family: sans-serif;
}
.item {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%) scale(3.0);
}
.item h1 {
text-align: center;
position: absolute;
width: 100%;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-110%);
color: #ffffff;
font-size: 3em;
}
.item h2 {
text-align: center;
position: absolute;
width: 50%;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-110%);
color: #ffffff;
font-size: 1.5em;
line-height: 0.9;
}
svg {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440;
/* this value is the pixel circumference of the circle */
stroke-dashoffset: 440;
transition: all 1s linear;
}
</style>
<script src="https://code.jquery.com/jquery-2.2.1.min.js"></script>
<script type="text/javascript">
var running = false;
var time = 180;
var initialOffset = '440';
var runtime = 0;
$(document).keydown(function(e) {
switch (e.which) {
case 32:
setTimeout(function() {
if (running === false) {
running = true;
} else {
running = false;
};
}, 1);
break;
}
});
$(document).ready(function() {
console.log("ready!");
$('#start').click(function() {
running = true;
});
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset - (runtime * (initialOffset / (time + 10.5))));
$('h1').text(time - runtime);
if (runtime == 420) {
audioElement1.play();
}
if (runtime == time) {
clearInterval(interval);
$('.circle_animation').css('stroke-dashoffset', 880);
$('h1').text('');
$('h2').text('Time is up!');
}
if (running) {
runtime++;
};
}, 1000);
});
</script>
</head>
<body>
<div class="item html">
<svg width="160" height="160" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<circle id="circle" r="65.85699" cy="81" cx="81" stroke-width="15" stroke="#ffffff" fill="none"/>
<circle id="circle" class="circle_animation" r="65.85699" cy="81" cx="81" stroke-width="16" stroke="#333" fill="none"/>
</g>
</svg>
<h1>180</h1>
<h2></h2>
</div>
</body>
</html>
Use modulus math:
function sec2human(seconds) {
sec = seconds % 60;
min = parseInt(seconds / 60);
if(sec.toString().length == 1) { // padding
sec = "0" + sec;
}
return min + ":" + sec;
}
Full working example:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
body {
background-color: #333;
font-family: sans-serif;
}
.item {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%) scale(3.0);
}
.item h1 {
text-align: center;
position: absolute;
width: 100%;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-110%);
color: #ffffff;
font-size: 3em;
}
.item h2 {
text-align: center;
position: absolute;
width: 50%;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-110%);
color: #ffffff;
font-size: 1.5em;
line-height: 0.9;
}
svg {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440;
/* this value is the pixel circumference of the circle */
stroke-dashoffset: 440;
transition: all 1s linear;
}
</style>
<script src="https://code.jquery.com/jquery-2.2.1.min.js"></script>
<script type="text/javascript">
var running = false;
var time = 180;
var initialOffset = '440';
var runtime = 0;
$(document).keydown(function(e) {
switch (e.which) {
case 32:
setTimeout(function() {
if (running === false) {
running = true;
} else {
running = false;
};
}, 1);
break;
}
});
$(document).ready(function() {
console.log("ready!");
$('#start').click(function() {
running = true;
});
$('h1').text(sec2human(time)); // added for initial display
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset - (runtime * (initialOffset / (time + 10.5))));
$('h1').text(sec2human(time - runtime)); // added function call
if (runtime == 420) {
audioElement1.play();
}
if (runtime == time) {
clearInterval(interval);
$('.circle_animation').css('stroke-dashoffset', 880);
$('h1').text('');
$('h2').text('Time is up!');
}
if (running) {
runtime++;
};
}, 1000);
});
function sec2human(seconds) {
sec = seconds % 60;
min = parseInt(seconds / 60);
if(sec.toString().length == 1) { // padding
sec = "0" + sec;
}
return min + ":" + sec;
}
</script>
</head>
<body>
<div class="item html">
<svg width="160" height="160" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<circle id="circle" r="65.85699" cy="81" cx="81" stroke-width="15" stroke="#ffffff" fill="none"/>
<circle id="circle" class="circle_animation" r="65.85699" cy="81" cx="81" stroke-width="16" stroke="#333" fill="none"/>
</g>
</svg>
<h1>180</h1>
<h2></h2>
</div>
</body>
</html>
Here is the important part:
var seconds = time - runtime,
mins = ("0" + Math.floor(seconds / 60)).substr(-2),
secs = ("0" + (seconds % 60)).substr(-2);
$('h1').text(mins + ":" + secs);
Basically split up each part by minutes and seconds and concatenate them. The substring is to enforce double digit padding (ie: 02:03 instead of 2:3)
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
body {
background-color: #333;
font-family: sans-serif;
}
.item {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%) scale(3.0);
}
.item h1 {
text-align: center;
position: absolute;
width: 100%;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-110%);
color: #ffffff;
font-size: 3em;
}
.item h2 {
text-align: center;
position: absolute;
width: 50%;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-110%);
color: #ffffff;
font-size: 1.5em;
line-height: 0.9;
}
svg {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
.circle_animation {
stroke-dasharray: 440;
/* this value is the pixel circumference of the circle */
stroke-dashoffset: 440;
transition: all 1s linear;
}
</style>
<script src="https://code.jquery.com/jquery-2.2.1.min.js"></script>
<script type="text/javascript">
var running = false;
var time = 180;
var initialOffset = '440';
var runtime = 0;
$(document).keydown(function(e) {
switch (e.which) {
case 32:
setTimeout(function() {
if (running === false) {
running = true;
} else {
running = false;
};
}, 1);
break;
}
});
$(document).ready(function() {
console.log("ready!");
$('#start').click(function() {
running = true;
});
var interval = setInterval(function() {
$('.circle_animation').css('stroke-dashoffset', initialOffset - (runtime * (initialOffset / (time + 10.5))));
var seconds = time - runtime,
mins = ("0" + Math.floor(seconds / 60)).substr(-2),
secs = ("0" + (seconds % 60)).substr(-2);
$('h1').text(mins + ":" + secs);
if (runtime == 420) {
audioElement1.play();
}
if (runtime == time) {
clearInterval(interval);
$('.circle_animation').css('stroke-dashoffset', 880);
$('h1').text('');
$('h2').text('Time is up!');
}
if (running) {
runtime++;
};
}, 1000);
});
</script>
</head>
<body>
<div class="item html">
<svg width="160" height="160" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<circle id="circle" r="65.85699" cy="81" cx="81" stroke-width="15" stroke="#ffffff" fill="none"/>
<circle id="circle" class="circle_animation" r="65.85699" cy="81" cx="81" stroke-width="16" stroke="#333" fill="none"/>
</g>
</svg>
<h1>180</h1>
<h2></h2>
</div>
</body>
</html>
Cool animation btw.

build semi circle progress bar with round corners and shadow in java script and CSS

I searched a lot and finding nothing on it. I want to make a progress bar with round corners.progress bar need to have shadow. All I did as of now is here :
$(".progress-bar").each(function(){
var bar = $(this).find(".bar");
var val = $(this).find("span");
var per = parseInt( val.text(), 10);
$({p:0}).animate({p:per}, {
duration: 3000,
easing: "swing",
step: function(p) {
bar.css({
transform: "rotate("+ (45+(p*1.8)) +"deg)"
});
val.text(p|0);
}
});
});
body{
background-color:#3F63D3;
}
.progress-bar{
position: relative;
margin: 4px;
float:left;
text-align: center;
}
.barOverflow{
position: relative;
overflow: hidden;
width: 150px; height: 70px;
margin-bottom: -14px;
}
.bar{
position: absolute;
top: 0; left: 0;
width: 150px; height: 150px;
border-radius: 50%;
box-sizing: border-box;
border: 15px solid gray;
border-bottom-color: white;
border-right-color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="progress-bar">
<div class="barOverflow">
<div class="bar"></div>
</div>
<span>100</span>%
</div>
I want to make corners round and having shadow. below given image represent what actually i want. Shadow is missing because i don't know to draw. :
I have tried Progressbar.js also, but I don't have much knowledge about SVG. Any answer would be appreciated.
#jaromanda for suggestion of learning SVG.
Yes is looks very hard to achieve from border-radius. So i looked into SVG and find it pretty handy. Here is my snippet:
// progressbar.js#1.0.0 version is used
// Docs: http://progressbarjs.readthedocs.org/en/1.0.0/
var bar = new ProgressBar.SemiCircle(container, {
strokeWidth: 10,
color: 'red',
trailColor: '#eee',
trailWidth: 10,
easing: 'easeInOut',
duration: 1400,
svgStyle: null,
text: {
value: '',
alignToBottom: false
},
// Set default step function for all animate calls
step: (state, bar) => {
bar.path.setAttribute('stroke', state.color);
var value = Math.round(bar.value() * 100);
if (value === 0) {
bar.setText('');
} else {
bar.setText(value+"%");
}
bar.text.style.color = state.color;
}
});
bar.text.style.fontFamily = '"Raleway", Helvetica, sans-serif';
bar.text.style.fontSize = '2rem';
bar.animate(0.45); // Number from 0.0 to 1.0
#container {
width: 200px;
height: 100px;
}
svg {
height: 120px;
width: 200px;
fill: none;
stroke: red;
stroke-width: 10;
stroke-linecap: round;
-webkit-filter: drop-shadow( -3px -2px 5px gray );
filter: drop-shadow( -3px -2px 5px gray );
}
<script src="https://rawgit.com/kimmobrunfeldt/progressbar.js/1.0.0/dist/progressbar.js"></script>
<link href="https://fonts.googleapis.com/css?family=Raleway:400,300,600,800,900" rel="stylesheet" type="text/css">
<div id="container"></div>
I want to suggest some stupid but quick solution since you're already using position: absolute. You can add background color to the circles when your animation starts.
html:
<div class="progress-bar">
<div class="left"></div>
<div class="right"><div class="back"></div></div>
<div class="barOverflow">
<div class="bar"></div>
</div>
<span>0</span>%
</div>
css:
/** all your css here **/
body{
background-color:#3F63D3;
}
.progress-bar{
position: relative;
margin: 4px;
float: left;
text-align: center;
}
.barOverflow{
position: relative;
overflow: hidden;
width: 150px; height: 70px;
margin-bottom: -14px;
}
.bar{
position: absolute;
top: 0; left: 0;
width: 150px; height: 150px;
border-radius: 50%;
box-sizing: border-box;
border: 15px solid gray;
border-bottom-color: white;
border-right-color: white;
transform: rotate(45deg);
}
.progress-bar > .left {
position: absolute;
background: white;
width: 15px;
height: 15px;
border-radius: 50%;
left: 0;
bottom: -4px;
overflow: hidden;
}
.progress-bar > .right {
position: absolute;
background: white;
width: 15px;
height: 15px;
border-radius: 50%;
right: 0;
bottom: -4px;
overflow: hidden;
}
.back {
width: 15px;
height: 15px;
background: gray;
position: absolute;
}
jquery:
$(".progress-bar").each(function(){
var bar = $(this).find(".bar");
var val = $(this).find("span");
var per = parseInt( val.text(), 10);
var $right = $('.right');
var $back = $('.back');
$({p:0}).animate({p:per}, {
duration: 3000,
step: function(p) {
bar.css({
transform: "rotate("+ (45+(p*1.8)) +"deg)"
});
val.text(p|0);
}
}).delay( 200 );
if (per == 100) {
$back.delay( 2600 ).animate({'top': '18px'}, 200 );
}
if (per == 0) {
$('.left').css('background', 'gray');
}
});
https://jsfiddle.net/y86qs0a9/7/
Same as the answers above, I found it much easier to implement using SVG instead of pure CSS.
However I couldn't find a single simplistic implementation using only HTML and CSS, or at least with no libraries, no external scripts or no dependencies. I found that given the math that needs to be calculated to make the SVG transformations to represent the percentage, JS needs to be included (if someone knows how to achieve this with only HTML and CSS I'd love to learn how). But what the JS script does is not long or complex enough to justify the overhead of adding yet another dependency to my codebase.
The JS calculations are pretty easy once you read through. You need to calculate the coordinate for the end point of the gauge in the coordinate system of the SVG. so basic trig.
Most of the CSS is not even needed and I added just to style it and to make it pretty. You can add shadow or gradients same as you could with any HTML pure shape.
Here is the codePen https://codepen.io/naticaceres/pen/QWQeyGX
You can easily tinker with this code to achieve any kind of shape of circular gauge (full circle, lower half of the semi-circle, or any variation including ellipsis).
Hope this is helpful.
// # Thanks to mxle for the first rounded corner CSS only solution https://stackoverflow.com/a/42478006/4709712
// # Thanks to Aniket Naik for the styling and the basic idea and implementation https://codepen.io/naikus/pen/BzZoLL
// - Aniket Naik has a library, linked to that codepen you should check out if you don't want to copy-paste or implement yourself
// the arc radius in the meter-value needs to stay the same, and must always be x=y, not lower than the possible circle that can connect the two points (otherwise the ratio is not preserved and the curvature doesn't match the background path).
// to style the gauge, make it bigger or smaller, play with its parent element and transform scale. don't edit width and height of SVG directly
function percentageInRadians(percentage) {
return percentage * (Math.PI / 100);
}
function setGaugeValue(gaugeElement, percentage, color) {
const gaugeRadius = 65;
const startingY = 70;
const startingX = 10;
const zeroBasedY = gaugeRadius * Math.sin(percentageInRadians(percentage));
const y = -zeroBasedY + startingY;
const zeroBasedX = gaugeRadius * Math.cos(percentageInRadians(percentage));
const x = -zeroBasedX + gaugeRadius + startingX;
// # uncomment this to log the calculations of the coordinates for the final point of the gauge value path.
//console.log(
// `percentage: ${percentage}, zeroBasedY: ${zeroBasedY}, y: ${y}, zeroBasedX: ${zeroBasedX}, x: ${x}`
//);
gaugeElement.innerHTML = `<path d="M ${startingX} ${startingY}
A ${gaugeRadius} ${gaugeRadius} 0 0 1 ${x} ${y}
" stroke="${color}" stroke-width="10" stroke-linecap="round" />`;
}
percentageChangedEvent = (gauge, newPercentage, color) => {
const percentage =
newPercentage > 100 ? 100 : newPercentage < 0 ? 0 : newPercentage;
setGaugeValue(gauge, percentage, color);
};
function initialGaugeSetup(gaugeElementId, inputId, meterColor, initialValue) {
const gaugeElement = document.getElementById(gaugeElementId);
setGaugeValue(gaugeElement, 0, meterColor);
const inputElement = document.getElementById(inputId);
inputElement.value = initialValue;
setGaugeValue(gaugeElement, initialValue, meterColor);
inputElement.addEventListener("change", (event) =>
percentageChangedEvent(gaugeElement, event.target.value, meterColor)
);
}
// Gauge Initial Config
initialGaugeSetup(
"svg-graph-meter-value",
"svg-gauge-percentage-2",
"rgb(227 127 215)",
40
);
body {
background-color: rgba(0, 0, 0, 0.8);
color: #999;
font-family: Hevletica, sans-serif;
}
/* SVG Path implementation */
.svg-container {
margin: 20px auto 10px;
height: 80px;
width: 150px;
}
svg {
fill: transparent;
}
.input-percent-container {
text-align: center;
}
.input-percent-container>* {
display: inline;
}
input {
text-align: right;
width: 40px;
margin: auto;
background-color: #5d5d5d;
color: white;
border-radius: 6px;
border: black;
}
<div class="svg-container">
<svg width="150" height="80" xmlns="http://www.w3.org/2000/svg">
<path d="M 10 70
A 65 65 0 1 1 140 70
" stroke="grey" stroke-width="3" stroke-linecap="round" />
<g id="svg-graph-meter-value">
</g>
</svg>
</div>
<div class="input-percent-container"><input id="svg-gauge-percentage-2" /><span>%<span/></div>

Categories

Resources