How can I smoothly stretch and shrink a revolving line arc? - javascript

Google's Material Design spinners shring and stretch while rotating:
I have found the following SVG spinner which implements it pretty well here: https://codepen.io/svnt/pen/qraaRN.
HTML:
<svg class="spinner" viewBox="0 0 50 50"><circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle></svg>
CSS:
/* SVG spinner icon animation */
.spinner {
-webkit-animation: rotate 2s linear infinite;
animation: rotate 2s linear infinite;
z-index: 2;
position: absolute;
top: 50%;
left: 50%;
margin: -25px 0 0 -25px;
width: 50px;
height: 50px;
}
.spinner .path {
stroke: #cccccc;
stroke-linecap: round;
-webkit-animation: dash 1.5s ease-in-out infinite;
animation: dash 1.5s ease-in-out infinite;
}
#-webkit-keyframes rotate {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#keyframes rotate {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#-webkit-keyframes dash {
0% {
stroke-dasharray: 1, 150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -124;
}
}
#keyframes dash {
0% {
stroke-dasharray: 1, 150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -124;
}
}
The thing is, the #keyframes animation uses stroke-dasharray and stroke-dashoffset which seem to run on the main UI thread as if I do some tasks with JavaScript while the animation is running, the spinner looses its smoothness and becomes jerky.
The rotation (via transform) works pretty well, and I know it runs off the UI thread, so even if I perform some JS task the rotation will be smooth while animating.
Of course, I can just implement a rotating spinner without the stretch/shrink stuff, but I was wondering if someone of you knows how to make such animations always look smooth. Is there a way to stretch and shrink the spinner using transform while it rotates?
Hope I was clear. Thanks for the attention!

You can simulate this using different elements where the idea is to hide the first one using the other. The only drawback is the transparency.
Here is an example where you can adjust the different values to get the needed animation. Used CSS variable for simplicity but it's not mandatory.
.loading {
width:50px;
height:50px;
position:fixed;
top:calc(50% - 25px);
left:calc(50% - 25px);
border-radius:50%;
border:5px solid blue;
animation:load 2s linear infinite;
}
.loading:before,
.loading:after,
.loading span:before,
.loading span:after{
content:"";
position:absolute;
top:-6px;
left:-6px;
right:-6px;
bottom:-6px;
border-radius:50%;
border:7px solid transparent;
border-left-color:white;
animation:hide 1.2s infinite;
}
.loading span:before {
--r:90deg;
}
.loading span:after {
--r:180deg;
}
.loading:before {
--r:260deg; /* a little less than 270deg to keep some border visible */
}
#keyframes load {
to {
transform:rotate(360deg);
}
}
#keyframes hide {
50% {
transform:rotate(var(--r,0deg));
}
100% {
transform:rotate(360deg);
}
}
<div class="loading">
<span></span>
</div>
With transparency you can create the border using 4 elements that you rotate to make them above each other and shrink the overal shape. Basically the opposite logic of the first code (we change what was blue with transparent and what was white with blue)
The only drawback is that you cannot shrink less that the length of one side
.loading {
width:50px;
height:50px;
position:fixed;
top:calc(50% - 25px);
left:calc(50% - 25px);
animation:load 2s linear infinite;
}
.loading:before,
.loading:after,
.loading span:before,
.loading span:after{
content:"";
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
border-radius:50%;
border:5px solid transparent;
border-left-color:blue;
animation:hide 1.2s infinite;
}
.loading span:before {
--r:90deg;
}
.loading span:after {
--r:180deg;
}
.loading:before {
--r:200deg;
}
#keyframes load {
to {
transform:rotate(360deg);
}
}
#keyframes hide {
50% {
transform:rotate(var(--r,0deg));
}
100% {
transform:rotate(360deg);
}
}
body {
background:linear-gradient(to right,pink,orange);
}
<div class="loading">
<span></span>
</div>
To better understand what is happening in both codes, remove the main rotation and use different color for borders
.loading {
width:50px;
height:50px;
position:fixed;
top:calc(50% - 25px);
left:calc(50% - 25px);
/* animation:load 2s linear infinite;*/
}
.loading:before,
.loading:after,
.loading span:before,
.loading span:after{
content:"";
position:absolute;
top:0;
left:0;
right:0;
bottom:-0;
border-radius:50%;
border:5px solid transparent;
border-left-color:blue;
animation:hide 4s infinite;
}
.loading span:before {
--r:90deg;
border-left-color:red;
}
.loading span:after {
--r:180deg;
border-left-color:green;
}
.loading:before {
--r:260deg;
border-left-color:yellow;
}
#keyframes load {
to {
transform:rotate(360deg);
}
}
#keyframes hide {
50% {
transform:rotate(var(--r,0deg));
}
100% {
transform:rotate(360deg);
}
}
body {
background:linear-gradient(to right,pink,orange);
}
<div class="loading">
<span></span>
</div>

Related

Getting Top/Right values ​from a css-animated element

I'm trying to get the Top and Right values from an element being rotated by a CSS animation, for this I am using the following code:
HTML:
<div id="ball1"> </div>
CSS:
#keyframes spin {
0% {transform: rotate(0deg);}
100% {transform: rotate(360deg);}
}
#ball1 {
transform-origin: center right;
animation: spin 2.5s linear 0s infinite forwards;
position: relative;
background-color: #7883f7;
width: 10;
height: 10;
border-radius: 10px;
}
Javascript:
console.log(window.getComputedStyle(document.getElementById("ball1"), null).top);
console.log(window.getComputedStyle(document.getElementById("ball1"), null).right);
However it returns a value of 0px, I wanted to get the value from Right and Top as if I was manually setting them (and not by the transform animation).
If this is not possible, is there a way to simulate a "circle" rotation and return the right/top values without using the transform?
ie:
https://66.media.tumblr.com/fb22b61bcbca3785a515e86c2276451b/tumblr_inline_pmimnjEvbK1v6q8wn_1280.gif?fbclid=IwAR2zjgE0hfB8emWOg0f6TOcQb8DWGbEvu9IQOr92fMq4HmMKjiAQRQzLmI0
Use getBoundingClientRect():
const ball = document.getElementById("ball");
setInterval(() => {
const rect = ball.getBoundingClientRect();
console.log(rect.top, rect.right);
}, 300);
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#ball {
transform-origin: center right;
animation: spin 2.5s linear 0s infinite forwards;
position: relative;
background-color: #7883f7;
width: 10px;
height: 10px;
border-radius: 10px;
}
<div id="ball"></div>
Here is an approximation using top/left. The trick is to animate each property individually alternating the ease function to simulate the circular path:
.box {
width:100px;
height:100px;
position:relative;
}
#ball1 {
animation:
Atop 2.5s infinite,
Aleft 2.5s infinite;
position: absolute;
background-color: #7883f7;
width: 10px;
height: 10px;
border-radius: 10px;
}
#keyframes Atop {
0%,50%,100% {
top:50%;
animation-timing-function: ease-out;
}
25% {
top:0%;
animation-timing-function: ease-in;
}
75% {
top:100%;
animation-timing-function: ease-in;
}
}
#keyframes Aleft {
0%,100% {
left:0;
animation-timing-function: ease-in;
}
25%,75% {
left:50%;
animation-timing-function: ease-out;
}
50% {
left:100%;
animation-timing-function: ease-in;
}
}
<div class="box">
<div id="ball1"> </div>
</div>

I want to create a loader with still image in between the loader circle [duplicate]

This question already has answers here:
How to add a icon inside a spinner
(3 answers)
Closed 5 years ago.
I want to create a loader with still image in between the loader
circle. in this code the image is rotating along with loader i want
the image to be still not rotating.
<!DOCTYPE html>
<html>
<head>
<style>
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<h2>Loader with image</h2>
<p><strong>Note:</strong> The -webkit- and -ms- prefixes are for browsers that do not support animation and transform properties.</p>
<div class="loader">
<img id="drawerUserImage" class="img-circle" src="https://cdn2.iconfinder.com/data/icons/picol-vector/32/view-128.png"/>
</div>
</body>
</html>
You could change it something like this with using image in the wrapper elements background:
.wrapper {
width: 152px;
height: 152px;
}
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
.wrapper {
background: url('https://cdn2.iconfinder.com/data/icons/picol-vector/32/view-128.png') center no-repeat;
background-size:50%;
}
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
<div class="wrapper">
<div class="loader"></div>
</div>
You could try countering the rotating effect by creating identical animation for the img, just in opposite direction. However, a better option would be to change your DOM so that the image is not a child of animated parent.
.loader img {
-webkit-animation: counter-spin 2s linear infinite;
animation: counter-spin 2s linear infinite;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(-360deg); }
}

Grow line from center out on page load with CSS?

I am trying to accomplish the effect from this answer but without the text: Expand bottom border on hover
And know this can be accomplished by growing the entire div from the center as with here: http://jsfiddle.net/wNXLY/ but have no idea how to create this effect with line (i.e keeping the height static)
I have created my line here:
.line {
background: white;
width: 300px;
top: 10%;
height: 3.2px;
margin:auto;
position: relative;
}
And need to have the line grow from the center on page load. How can I do this?
You can use css animation with animation-fill-mode set to forwards, setting #keyframes width from 0% to n%, left from 50% to 5%
body {
width:100%;
}
div {
display:block;
position:absolute;
top:45%;
left:50%;
border-bottom:4px solid red;
width:0%;
text-align:center;
animation: line 2s linear forwards;
}
#keyframes line {
from {
left:50%;
width:0%;
}
to {
left:5%;
width:90%;
}
}
<div></div>
#keyframes line_animation {
from {
width: 0%;
}
to {
width:100%;
}
}
.line {
border-bottom: solid 3px #019fb6;
border-top-width: 0px;
animation-name: line_animation;
animation-duration: 4s;
animation-timing-function: linear;
}
Like this
<hr class="line" />
i have utilised display grid and SCSS to configure an authentic border animation
.top-border {
grid-area: tb;
// background: green;
border-bottom: $border-config;
width: 0%;
animation: horizontal-border-animation $animation-duration / 2 forwards;
animation-delay: $animation-delay;
}
.bottom-border {
grid-area: bb;
//to prevent being visible since it is going to be delayed
width: 0%;
// background: yellow;
border-top: $border-config;
animation: horizontal-border-animation $animation-duration / 2 forwards;
// because both right and bottom will start animating after top and left + the intitial delay
animation-delay: $animation-duration / 2 + $animation-delay;
}
#keyframes expand-border-width {
from {
width:0%;
}
to {
width:100%;
}
}
check my sample to gain an explicit clarification
https://codepen.io/ino0r/pen/eYEgvrZ
You don't need keyframes for this if you're just transitioning the effect.
<div class="line"></div>
.line {
width: 0%;
height: 1px;
position: absolute;
left: 50%;
background: #f00;
transition: all 1s;
}
.line:hover {
width: 100%;
left: 0%;
}

Ocillate function between two values javascript

I need to have a growing and decreasing circle in made in javascript.
My idea is to use a div with
border-radius : 50%
To get a circle. I need to make it scale from 0.2 to 1 every [x] seconds .
Like It grows from 0.2 to 1 in 5 seconds, then it decreases from 1 to 0.2 in5 seconds too. THen the movement starts again.
I think i have to use sin or cos functions but i don't know how to get this interval depending on time.
I need it to be coupled with a javascript timing function, so that when i satr a timer, the animation begins, and when I pause it it pauses the animation.
Thanks for advice !
One more example of CSS animation which probably better option then javascript:
.circle {
background: coral;
height: 100px;
width: 100px;
border-radius: 50%;
-webkit-animation: pulse 1s ease infinite alternate;
animation: pulse 1s ease infinite alternate;
}
#-webkit-keyframes pulse {
from {
-webkit-transform: scale(1);
}
to {
-webkit-transform: scale(.2);
}
}
<div class="circle"></div>
For Firefox you will need to add prefixless rule
#keyframes pulse {
from { transform: scale(1); }
to { transform: scale(.2); }
}
You could do this with CSS3 animations. Look at this example and change it until it does exactly how you want it.
#circle {
-webkit-animation: oscillate 10s infinite;
animation: oscillate 10s infinite;
border: 1px solid black;
height: 100px;
width: 100px;
}
#-webkit-keyframes oscillate {
0% { border-radius: 20%; }
50% { border-radius: 100%; }
100% { border-radius: 20%; }
}
#keyframes oscillate {
0% { border-radius: 20%; }
50% { border-radius: 100%; }
100% { border-radius: 20%; }
}
<div id="circle">Hi</div>
Use a CSS3 animation set on infinite. Check this fiddle.
#-webkit-keyframes mymove {
0%, 100% { -webkit-transform: scale(1); }
50% { -webkit-transform: scale(0.2); }
}
#keyframes mymove {
0%, 100% { transform: scale(1); }
50% { transform: scale(0.2); }
}
div {
width: 100px;
height: 100px;
background: red;
position: relative;
-webkit-animation: mymove 5s infinite; /* Chrome, Safari, Opera */
animation: mymove 5s infinite;
border-radius:60px;
}

Convert jquery animation to CSS3

HTML:
<div id="slick-slidetoggle">wxyz</div>
<div id="slickbox" >abcd</div>​
JS
// hides the slickbox as soon as the DOM is ready (a little sooner that page load)
var hoverVariable=false;
var hoverVariable2=false;
$('#slickbox').hide();
$('#slick-slidetoggle').mouseover(function() {
hoverVariable2=true;
$('#slickbox').slideToggle(600);
return false;
})
$('#slick-slidetoggle').mouseleave(function() {
hoverVariable2=false;
setTimeout(function (){
if(!hoverVariable && !hoverVariable2){
$('#slickbox').slideToggle(600);
return false;}
}, 1000);
})
$('#slickbox').mouseleave(function() {
hoverVariable=false;
setTimeout(function (){
if(!hoverVariable && !hoverVariable2){
$('#slickbox').slideToggle(600);
return false;}
return false;
}, 1000);
})
$('#slickbox').mouseover(function() {
hoverVariable2=false;
hoverVariable=true;
})​
CSS
#slickbox {
background: black;
width:100px;
height: 135px;
display: none;
cursor:pointer;
color:white;
}
#slick-slidetoggle{
background: yellow;
width:100px;
height: 135px;
cursor:pointer;
color:black;
}
​
Now the above functionality is what I want to achieve using purely CSS, which is when I hover over the "wxyz" button "abcd" button should come down and stay visible even is mouse is moved away from "wxyz" for 3 secs.
I tried transition delay with display property but apparently that doesn't work on display property, then I tried position:absolute & visibility & transition delay of visibility, but then the appearance of button got delayed by 3 secs not the hidnig.
I want the "abcd" button to hide after 3 secs of moving the button away from "wxyz" using only CSS or CSS3
Here is an Example (Code here)
(I have written only -webkit, but you could add the other prefixes)
#test2 {
position:absolute;
z-index:1;
background: black;
width:100px;
height: 135px;
opacity: 0;
cursor:pointer;
color:white;
opacity:0;
-webkit-animation-duration: 600ms;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-iteration-count: 1;
-webkit-animation-fill-mode: both;
}
#test {
position:absolute;
z-index:2;
background: yellow;
width:100px;
height: 135px;
cursor:pointer;
color:black;
}
.container {
position:relative;
}
.container:hover #test2 {
opacity:1;
-webkit-animation-name: slideDown;
}
.container:not(:hover) > #test2 {
-webkit-animation-delay:1000ms;
-webkit-animation-name: slideUp;
opacity:1;
}
#-webkit-keyframes slideDown {
0% {
-webkit-transform: translateY(0);
}
100% {
-webkit-transform: translateY(135px);
}
}
#-webkit-keyframes slideUp {
0% {
-webkit-transform: translateY(135px);
}
100% {
-webkit-transform: translateY(0);
}
}
Here is a cross browser solution:
Tested on OPERA-SAFARI-CHROME-MAXTHON-FIREFOX
HTML:
<div class="container">
<div id="test">wxyz</div>
<div id="test2" >abcd</div>
</div>
CSS:
#test {
width:100px;
height:100px;
background:yellow;
position:relative;
z-index:2;
}
#test2 {
top:-100px;
width:100px;
height:100px;
background:black;
color:white;
position:relative;
z-index:1;
}
.container:hover #test2 {
top:0px;
transition-property:top;
transition-duration:0.2s;
transition-timing-function: linear;
/* Firefox 4 */
-moz-transition-property:top;
-moz-transition-duration:0.2s;
-moz-transition-timing-function:linear;
/* Safari and Chrome */
-webkit-transition-property:top;
-webkit-transition-duration:0.2s;
-webkit-transition-timing-function:linear;
/* Opera */
-o-transition-property:top;
-o-transition-duration:0.2s;
-o-transition-timing-function:linear;
/* IE */
-ms-transition-property:top;
-ms-transition-duration:0.2s;
-ms-transition-timing-function:linear;
}
.container:not(:hover) #test2 {
top:-100px;
transition-property:top;
transition-duration:0.2s;
transition-timing-function: linear;
transition-delay: 3s;
/* Firefox 4 */
-moz-transition-property:top;
-moz-transition-duration:0.2s;
-moz-transition-timing-function:linear;
-moz-transition-delay:3s;
/* Safari and Chrome */
-webkit-transition-property:top;
-webkit-transition-duration:0.2s;
-webkit-transition-timing-function:linear;
-webkit-transition-delay:3s;
/* Opera */
-o-transition-property:top;
-o-transition-duration:0.2s;
-o-transition-timing-function:linear;
-o-transition-delay:3s;
/* IE */
-ms-transition-property:top;
-ms-transition-duration:0.2s;
-ms-transition-timing-function:linear;
-ms-transition-delay:3s;
}
Fiddle: http://jsfiddle.net/BerkerYuceer/2gVLX/
Use the transition to do it as below:
<head>
<style>
#outer {
width: 100px;
height: 50px;
background-color: green;
position: relative;
}
#innerOne {
width: 100px;
height: 50px;
background-color: blue;
}
#innerTwo {
width: 100px;
height: 50px;
background-color: red;
position: absolute;
top: -150px;
left: 100px;
}
#outer:hover #innerTwo {
top: 0px;
-webkit-transition: top 2s ease-out;
-moz-transition: top 2s ease-out;
-o-transition: top 2s ease-out;
transition: top 2s ease-out;
}
#innerTwo:not(hover) {
-webkit-transition: top 1s ease-in 3s;
-moz-transition: top 1s ease-in 3s;
-o-transition: top 1s ease-in 3s;
transition: top 1s ease-in 3s;
}
</style>
</head>
<body>
<div id="outer">
<div id="innerOne">wxyz</div>
<div id="innerTwo">abcd</div>
</div>
</body>
</html>

Categories