How to programmatically build curves in svg path? - javascript

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?

Related

why does event Listener need two elements?

In the code below, you'll notice i have two event listeners
window.addEventListener("click", jump);
window.addEventListener("click", jump());
The problem is removing either one of these makes the event listener invalid i.e. it doesnt work, they have to both be present and I'm having a hard time figuring out why.
note: click to jump and view snippet in full page since i havent done the responsive part yet
const ball = document.querySelector(".ball");
const obs = document.querySelector(".obstacle");
const score = document.querySelector("#s");
let temp = 0; // a temporary variable to stop event listener from running as soon as game starts
function jump() {
let bottom = parseInt(window.getComputedStyle(ball).getPropertyValue('bottom'));
if (temp > 0 && bottom === 150) {
ball.style.bottom = "400px";
setTimeout(() => {
ball.style.bottom = "150px";
}, 300);
}
temp++;
}
window.addEventListener("click", jump);
window.addEventListener("click", jump());
let z = 0; // intitial score to be incremented by each succesful second
setInterval(() => {
let ballX = parseInt(window.getComputedStyle(ball).getPropertyValue('bottom'));
let obsY = parseInt(window.getComputedStyle(obs).getPropertyValue('right'));
if (ballX >= 150 && ballX <= 240 && obsY >= 785 && obsY <= 970) {
ball.style.animationPlayState = "paused";
obs.style.animationPlayState = "paused";
ball.style.bottom = `${ballX}px`
window.removeEventListener("keydown", jump);
clearInterval();
} else {
s.innerHTML = z;
z += 1;
}
}, 10);
window.onload = () => {
if (screen.availHeight > screen.availWidth) {
alert("This game is best played in landscape, so rotate your phone if you can :)");
}
}
#import url('https://fonts.googleapis.com/css2?family=Comic+Neue&display=swap');
* {
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
.screen {
margin: 20px;
width: 1000px;
height: 500px;
background: skyblue;
outline: solid 3px;
position: absolute;
overflow: hidden;
}
.score {
position: absolute;
top: 5px;
right: 5px;
width: 200px;
height: 50px;
display: flex;
align-items: flex-end;
font-size: 150%;
font-family: "Comic Neue";
font-weight: bold;
}
.s {
display: flex;
justify-content: center;
align-items: center;
line-height: 50px;
text-shadow: 3px 3px 0px white;
}
#s-title {
width: 40%;
}
#s {
width: 60%;
}
.ball {
position: absolute;
left: 30px;
bottom: 150px;
width: 80px;
height: 80px;
border-radius: 50%;
background: red;
transition: bottom 0.3s ease-in-out;
animation: spin 1s linear infinite;
}
#keyframes spin {
to {
transform: rotate(720deg);
}
}
.obstacle {
width: 100px;
height: 100px;
position: absolute;
right: -150px;
bottom: 150px;
background: red;
animation: attack 1.5s linear infinite;
animation-play-state: running;
}
#keyframes attack {
to {
right: 1150px;
}
}
.grass {
position: absolute;
bottom: 125px;
width: 100%;
height: 25px;
background: rgb(55, 141, 52);
}
.ground {
position: absolute;
bottom: 0;
width: 100%;
height: 125px;
background: rgb(82, 80, 69);
}
<!doctype html>
<html lang='en'>
<head>
<!-- Required meta tags -->
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>Rollin</title>
<link rel='stylesheet' href='rollin.css'>
<link rel='icon' href='assets/logo.ico'>
</head>
<body>
<div class="screen">
<div class="score">
<div id="s-title" class="s">Score:</div>
<div id="s" class="s">000000</div>
</div>
<div class="ball">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 1024 1024" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<style type="text/css">
.str0 {stroke:black;stroke-width:0.667;stroke-miterlimit:2.61313}
.fil1 {fill:black}
.fil2 {fill:#1A1A1A}
.fil0 {fill:#AE0000}
</style>
</defs>
<g id="Layer_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g>
<path class="fil0" d="M517.231 11.182c277.954,0 503.28,225.327 503.28,503.281 0,277.954 -225.326,503.281 -503.28,503.281 -277.954,0 -503.281,-225.327 -503.281,-503.281 0,-277.954 225.327,-503.281 503.281,-503.281z"/>
<path id="1" class="fil1" d="M517.231 11.182c277.954,0 503.28,225.327 503.28,503.281 0,277.954 -225.326,503.281 -503.28,503.281 -277.954,0 -503.281,-225.327 -503.281,-503.281 0,-277.954 225.327,-503.281 503.281,-503.281zm338.197 165.084c-86.55,-86.551 -206.121,-140.084 -338.197,-140.084 -132.076,0 -251.648,53.533 -338.198,140.084 -86.55,86.55 -140.083,206.121 -140.083,338.197 0,132.076 53.533,251.647 140.083,338.197 86.551,86.551 206.122,140.084 338.198,140.084 132.076,0 251.647,-53.533 338.197,-140.084 86.551,-86.55 140.083,-206.121 140.083,-338.197 0,-132.076 -53.532,-251.647 -140.083,-338.197z"/>
</g>
<g>
<path class="fil2" d="M517.231 246.225c149.688,0 271.035,121.346 271.035,271.035 0,149.688 -121.347,271.035 -271.035,271.035 -149.689,0 -271.035,-121.347 -271.035,-271.035 0,-149.689 121.346,-271.035 271.035,-271.035zm184.58 86.454c-47.237,-47.237 -112.497,-76.454 -184.58,-76.454 -72.084,0 -137.343,29.217 -184.581,76.454 -47.237,47.238 -76.455,112.497 -76.455,184.581 0,72.083 29.218,137.343 76.455,184.58 47.238,47.237 112.497,76.455 184.581,76.455 72.083,0 137.343,-29.218 184.58,-76.455 47.237,-47.237 76.455,-112.497 76.455,-184.58 0,-72.084 -29.218,-137.343 -76.455,-184.581z"/>
</g>
<circle class="fil1 str0" cx="517.231" cy="517.26" r="100.656"/>
<path class="fil1 str0" d="M213.398 645.876c-54.056,-27.029 -41.008,-69.901 -48.444,-92.065 -3.228,-9.623 -41.293,134.779 56.413,170.477 18.411,6.727 36.767,13.855 70.319,8.264 25.978,-4.33 61.512,-22.368 72.238,-53.613 12.934,-37.676 6.05,-56.363 -16.318,-82.46 -8.188,-9.553 -35.416,-37.28 -89.472,-20.504 -33.759,19.875 -36.584,43.161 -44.736,69.901z"/>
<path class="fil1 str0" d="M561.667 188.198c52.09,-14.249 81.039,-0.564 103.952,4.079 9.948,2.016 -96.076,-103.15 -175.843,-36.384 -15.032,12.581 -30.384,24.914 -42.317,56.766 -9.24,24.663 -11.386,64.455 10.311,89.367 26.161,30.039 45.787,33.421 79.571,27.098 12.367,-2.315 49.994,-12.031 62.493,-67.234 -0.333,-39.173 -19.087,-53.263 -38.167,-73.692z"/>
<path class="fil1 str0" d="M786.104 714.481c3.621,60.329 -40.031,70.465 -55.509,87.986 -6.72,7.607 137.369,-31.629 119.431,-134.093 -3.38,-19.308 -6.384,-38.769 -28.003,-65.031 -16.738,-20.332 -50.127,-42.086 -82.549,-35.753 -39.095,7.637 -51.837,22.942 -63.253,55.361 -4.179,11.868 -14.578,49.312 26.979,87.737 34.092,19.299 55.671,10.103 82.904,3.793z"/>
</g>
</svg>
</div>
<div class="obstacle">
<img src="assets/kakashi.png" alt="">
</div>
<div class="grass"></div>
<div class="ground"></div>
</div>
<script src='rollin.js' defer></script>
</body>
</html>
How does this not work? You can try this better here: JSFidlle
const ball = document.querySelector(".ball");
const obs = document.querySelector(".obstacle");
const score = document.querySelector("#s");
let temp = 0; // a temporary variable to stop event listener from running as soon as game starts
function jump() {
let bottom = parseInt(window.getComputedStyle(ball).getPropertyValue('bottom'));
if (temp > 0 && bottom === 150) {
ball.style.bottom = "400px";
setTimeout(() => {
ball.style.bottom = "150px";
}, 300);
}
temp++;
}
window.addEventListener("click", jump);
let z = 0; // intitial score to be incremented by each succesful second
setInterval(() => {
let ballX = parseInt(window.getComputedStyle(ball).getPropertyValue('bottom'));
let obsY = parseInt(window.getComputedStyle(obs).getPropertyValue('right'));
if (ballX >= 150 && ballX <= 240 && obsY >= 785 && obsY <= 970) {
ball.style.animationPlayState = "paused";
obs.style.animationPlayState = "paused";
ball.style.bottom = `${ballX}px`
window.removeEventListener("keydown", jump);
clearInterval();
} else {
s.innerHTML = z;
z += 1;
}
}, 10);
window.onload = () => {
if (screen.availHeight > screen.availWidth) {
alert("This game is best played in landscape, so rotate your phone if you can :)");
}
}
#import url('https://fonts.googleapis.com/css2?family=Comic+Neue&display=swap');
* {
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
}
.screen {
margin: 20px;
width: 1000px;
height: 500px;
background: skyblue;
outline: solid 3px;
position: absolute;
overflow: hidden;
}
.score {
position: absolute;
top: 5px;
right: 5px;
width: 200px;
height: 50px;
display: flex;
align-items: flex-end;
font-size: 150%;
font-family: "Comic Neue";
font-weight: bold;
}
.s {
display: flex;
justify-content: center;
align-items: center;
line-height: 50px;
text-shadow: 3px 3px 0px white;
}
#s-title {
width: 40%;
}
#s {
width: 60%;
}
.ball {
position: absolute;
left: 30px;
bottom: 150px;
width: 80px;
height: 80px;
border-radius: 50%;
background: red;
transition: bottom 0.3s ease-in-out;
animation: spin 1s linear infinite;
}
#keyframes spin {
to {
transform: rotate(720deg);
}
}
.obstacle {
width: 100px;
height: 100px;
position: absolute;
right: -150px;
bottom: 150px;
background: red;
animation: attack 1.5s linear infinite;
animation-play-state: running;
}
#keyframes attack {
to {
right: 1150px;
}
}
.grass {
position: absolute;
bottom: 125px;
width: 100%;
height: 25px;
background: rgb(55, 141, 52);
}
.ground {
position: absolute;
bottom: 0;
width: 100%;
height: 125px;
background: rgb(82, 80, 69);
}
<!doctype html>
<html lang='en'>
<head>
<!-- Required meta tags -->
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>Rollin</title>
<link rel='stylesheet' href='rollin.css'>
<link rel='icon' href='assets/logo.ico'>
</head>
<body>
<div class="screen">
<div class="score">
<div id="s-title" class="s">Score:</div>
<div id="s" class="s">000000</div>
</div>
<div class="ball">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 1024 1024" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<style type="text/css">
.str0 {stroke:black;stroke-width:0.667;stroke-miterlimit:2.61313}
.fil1 {fill:black}
.fil2 {fill:#1A1A1A}
.fil0 {fill:#AE0000}
</style>
</defs>
<g id="Layer_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g>
<path class="fil0" d="M517.231 11.182c277.954,0 503.28,225.327 503.28,503.281 0,277.954 -225.326,503.281 -503.28,503.281 -277.954,0 -503.281,-225.327 -503.281,-503.281 0,-277.954 225.327,-503.281 503.281,-503.281z"/>
<path id="1" class="fil1" d="M517.231 11.182c277.954,0 503.28,225.327 503.28,503.281 0,277.954 -225.326,503.281 -503.28,503.281 -277.954,0 -503.281,-225.327 -503.281,-503.281 0,-277.954 225.327,-503.281 503.281,-503.281zm338.197 165.084c-86.55,-86.551 -206.121,-140.084 -338.197,-140.084 -132.076,0 -251.648,53.533 -338.198,140.084 -86.55,86.55 -140.083,206.121 -140.083,338.197 0,132.076 53.533,251.647 140.083,338.197 86.551,86.551 206.122,140.084 338.198,140.084 132.076,0 251.647,-53.533 338.197,-140.084 86.551,-86.55 140.083,-206.121 140.083,-338.197 0,-132.076 -53.532,-251.647 -140.083,-338.197z"/>
</g>
<g>
<path class="fil2" d="M517.231 246.225c149.688,0 271.035,121.346 271.035,271.035 0,149.688 -121.347,271.035 -271.035,271.035 -149.689,0 -271.035,-121.347 -271.035,-271.035 0,-149.689 121.346,-271.035 271.035,-271.035zm184.58 86.454c-47.237,-47.237 -112.497,-76.454 -184.58,-76.454 -72.084,0 -137.343,29.217 -184.581,76.454 -47.237,47.238 -76.455,112.497 -76.455,184.581 0,72.083 29.218,137.343 76.455,184.58 47.238,47.237 112.497,76.455 184.581,76.455 72.083,0 137.343,-29.218 184.58,-76.455 47.237,-47.237 76.455,-112.497 76.455,-184.58 0,-72.084 -29.218,-137.343 -76.455,-184.581z"/>
</g>
<circle class="fil1 str0" cx="517.231" cy="517.26" r="100.656"/>
<path class="fil1 str0" d="M213.398 645.876c-54.056,-27.029 -41.008,-69.901 -48.444,-92.065 -3.228,-9.623 -41.293,134.779 56.413,170.477 18.411,6.727 36.767,13.855 70.319,8.264 25.978,-4.33 61.512,-22.368 72.238,-53.613 12.934,-37.676 6.05,-56.363 -16.318,-82.46 -8.188,-9.553 -35.416,-37.28 -89.472,-20.504 -33.759,19.875 -36.584,43.161 -44.736,69.901z"/>
<path class="fil1 str0" d="M561.667 188.198c52.09,-14.249 81.039,-0.564 103.952,4.079 9.948,2.016 -96.076,-103.15 -175.843,-36.384 -15.032,12.581 -30.384,24.914 -42.317,56.766 -9.24,24.663 -11.386,64.455 10.311,89.367 26.161,30.039 45.787,33.421 79.571,27.098 12.367,-2.315 49.994,-12.031 62.493,-67.234 -0.333,-39.173 -19.087,-53.263 -38.167,-73.692z"/>
<path class="fil1 str0" d="M786.104 714.481c3.621,60.329 -40.031,70.465 -55.509,87.986 -6.72,7.607 137.369,-31.629 119.431,-134.093 -3.38,-19.308 -6.384,-38.769 -28.003,-65.031 -16.738,-20.332 -50.127,-42.086 -82.549,-35.753 -39.095,7.637 -51.837,22.942 -63.253,55.361 -4.179,11.868 -14.578,49.312 26.979,87.737 34.092,19.299 55.671,10.103 82.904,3.793z"/>
</g>
</svg>
</div>
<div class="obstacle">
<img src="assets/kakashi.png" alt="">
</div>
<div class="grass"></div>
<div class="ground"></div>
</div>
<script src='rollin.js' defer></script>
</body>
</html>
You can try to put the -listener- above the -jump- function and remove:
-- window.addEventListener("click", jump()); --

Using an SVG as a css background image on input range thumb

I am trying to use a custom background SVG for a range slider thumb in React using styled components. At the moment I am having trouble getting the SVG I have created to show. I have tested with other SVG's online and it works. Here is where I have got to thus far. It is in React with Typescript using styled-components
My compomnent styles:
const sliderThumbStyles = (props: any) => `
width: 40px;
height: 40px;
border: 0;
background: url("data:image/svg+xml, SVG to go in here");
border-radius: 20px;
border: 5px solid white;
cursor: pointer;
margin-top: -18px
`;
const SliderWrapper = styled.div`
.value {
flex: 1;
font-size: 2rem;
}
.slider {
-webkit-appearance: none;
appearance: none;
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
${(props) => sliderThumbStyles(props)}
}
&::-moz-range-thumb {
${(props) => sliderThumbStyles(props)}
}
&::-webkit-slider-runnable-track {
background: black;
height: 3px;
outline: none;
}
&::-moz-slider-track {
background: black;
height: 3px;
outline: none;
}
}
`;
And here is my SVG file that is in ./src/assets/images/slider/slider-thumb.svg
<svg width="63" height="63" viewBox="0 0 63 63" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="5.05025" y="31.2134" width="37" height="37" rx="18.5" transform="rotate(-45 5.05025 31.2134)" fill="#FF9675" stroke="white" stroke-width="7"/>
<path d="M31.3357 40.8633L31.3357 22.0019" stroke="#1D1D1D" stroke-width="2" stroke-miterlimit="10"/>
<path d="M38.67 26.334C32.3829 26.334 31.335 18.999 31.335 18.999C31.335 18.999 30.2871 26.334 24 26.334" stroke="#1D1D1D" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="bevel"/>
<path d="M38.67 35.5283C32.3829 35.5283 31.335 42.8633 31.335 42.8633C31.335 42.8633 30.2871 35.5283 24 35.5283" stroke="#1D1D1D" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="bevel"/>
</svg>
When I try and import the SVg and include it in the css property inside sliderThumbStyles method it doesn't work. I have tried dropping the SVG markup in directly too and this doesn't work. As I said, it works when I dropped in a random SVG from the web so I think my SVG markup is wrong or I am importing it incorrecty. Can anyone point me to as to where I am going wrong?
Thanks
I manage to use your svg as a thumbnail, but I had to
convert the double quotes to single quotes
remove all line breaks
encode the # with %23
ajust the width and heigth inside your svg tag to match the css
.slider {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 10px;
background: #d3d3d3;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 40px;
height: 40px;
border: 0;
background: url("data:image/svg+xml,<svg width='40' height='40' viewBox='0 0 63 63' fill='none' xmlns='http://www.w3.org/2000/svg'><rect x='5.05025' y='31.2134' width='37' height='37' rx='18.5' transform='rotate(-45 5.05025 31.2134)' fill='%23FF9675' stroke='white' stroke-width='7'/><path d='M31.3357 40.8633L31.3357 22.0019' stroke='%231D1D1D' stroke-width='2' stroke-miterlimit='10'/><path d='M38.67 26.334C32.3829 26.334 31.335 18.999 31.335 18.999C31.335 18.999 30.2871 26.334 24 26.334' stroke='%231D1D1D' stroke-width='2' stroke-miterlimit='10' stroke-linejoin='bevel'/><path d='M38.67 35.5283C32.3829 35.5283 31.335 42.8633 31.335 42.8633C31.335 42.8633 30.2871 35.5283 24 35.5283' stroke='%231D1D1D' stroke-width='2' stroke-miterlimit='10' stroke-linejoin='bevel'/></svg>");
cursor: pointer;
}
<input type="range" min="1" max="100" value="50" class="slider">

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>

seconds in a circle corner (like a clock)

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>

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