I think this will be a problem really difficult to solve...
I created a speedometer that shows number of earthquakes occured in my city.
I want to animate this speedometer in two way:
background-color
(green when there aren't quakes and red when there are 3000 quakes) and width of this colored div (the div where i animate background-color).
So the width will be 0 when there aren't quakes and will be 100% when there are 3000 quakes.
The animation is 2 seconds, so for example if i have 1500 quakes: Add the class for animate speedometer
$('#first').addClass('first-start');
And using setTimeout
i add a class to stop the animation after 1 second
setTimeout(function() {
$('#first').addClass('first-pause');
}, 1000);
This code almost always works great.
Now i add a snippet:
$('#first').addClass('first-start');
setTimeout(function() {
$('#first').addClass('first-pause');
}, 1000);
#page {
margin-top: 50px;
width: 300px;
height: 300px;
background-color: #000;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
z-index: 4;
overflow: hidden;
}
#box-first{
width: 200px;
height: 100px;
background-color: #fff;
border-radius: 200px 200px 0 0;
margin-top: 10px;
margin-bottom: 10px;
position: relative;
display: flex;
justify-content: flex-end;
align-items: flex-start;
z-index: 3;
overflow: hidden;
}
#first{
border-radius: 200px 200px 0 0;
margin: 0;
background: red;
width: 200px;
height: 100px;
transform: rotate(180deg);
transform-origin: 50% 100%;
position: absolute;
top: 0px;
right: 0px;
border: 0;
z-index: 1;
}
#n1{
font-size: 20px;
color: #fff;
font-weight: bold;
position: absolute;
left: 50px;
right: 0;
text-align: center;
top: 50px;
bottom: 0;
display: flex;
align-items: flex-end;
justify-content: center;
width: 100px;
height: 50px;
background: #000;
border-radius: 100px 100px 0 0;
z-Index: 1;
overflow: hidden;
}
@keyframes first {
0% {
background-color: green;
transform: rotate(180deg);
}
33% {
background-color: yellow;
transform: rotate(240deg);
}
66% {
background-color: orange;
transform: rotate(300deg);
}
100% {
background-color: red;
transform: rotate(360deg);
}
}
.first-start {
animation: first 2s linear;
}
.first-pause {
animation-play-state: paused;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="page">
<div id="box-first">
<div id="first">
</div>
<div id="n1">
1500
</div>
</div>
</div>
https://jsfiddle.net/hoymds97/
The problem is that i use this code in a big file (4000 lines) with a lot of events and in the same function there are 8 speedometers.
I noticed that sometimes (when there are more events) setTimeout
not start immediately after added class for animate speedometer.
As a result, the animation will stop after ... In our case, for example, it is as if it blocked after 1700 milliseconds and not 1000 seconds. Sometimes it stops even after 2 seconds.
I think the problem is the many events in the queue
.
So how can i solve this problem ?
Is possible to solve using always setTimeout
or without it?
I hope you can help me and sorry for my english.
Here is a complete new idea that relies on transition instead of animation and where you can easily adjust the state without synchronization issue.
The main trick is to use a gradient for the background coloration and adjust its position in order to have the needed color.
Here is a simple code to illustrate the coloration:
.box {
background: linear-gradient(to right, green, yellow, orange, red);
background-size: 2000% 100%;
transition:1s;
background-repeat: no-repeat;
background-position: 0 0;
height: 200px;
}
.box:hover {
background-position: 100% 0;
}
<div class="box">
</div>
As you can see, I defined a gradient with the 4 colors and we simply need to adjust the background-size
in order to have the coloration (0%
for green and 100%
for red). This won't be exactly the same visually because we will not have a solid color like with animation and for this reason I made the background-size
big enough to create the illusion of a solid color.
Now, we simply need to find the values of the background-position
and the degree
which is pretty easy. The backround-position
is a value between 0%
and 100%
and the degree is a value between 180deg
and 360deg
. For the state 50%
we will logically use 50%
for the background-position and 270deg
for the transformation and for an x%
state we will use respectively x%
and x%*(360deg - 180deg) + 180deg = x%*180deg + 180deg = 180deg(x% + 1)
Here is an example with 50%
(hover to see)
#page {
margin-top: 50px;
width: 300px;
height: 300px;
background-color: #000;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
z-index: 4;
overflow: hidden;
}
#box-first{
width: 200px;
height: 100px;
background-color: #fff;
border-radius: 200px 200px 0 0;
margin-top: 10px;
margin-bottom: 10px;
position: relative;
display: flex;
justify-content: flex-end;
align-items: flex-start;
z-index: 3;
overflow: hidden;
}
#first{
border-radius: 200px 200px 0 0;
margin: 0;
background: linear-gradient(to right, green, yellow, orange, red);
background-size: 2000% 100%;
background-repeat:no-repeat;
background-position:0% 0%;
transition:1s;
width: 200px;
height: 100px;
transform: rotate(180deg);
transform-origin: 50% 100%;
position: absolute;
top: 0px;
right: 0px;
border: 0;
z-index: 1;
}
#box-first:hover #first{
transform: rotate(270deg);
background-position:50% 0%;
}
#n1{
font-size: 20px;
color: #fff;
font-weight: bold;
position: absolute;
left: 50px;
right: 0;
text-align: center;
top: 50px;
bottom: 0;
display: flex;
align-items: flex-end;
justify-content: center;
width: 100px;
height: 50px;
background: #000;
border-radius: 100px 100px 0 0;
z-Index: 1;
overflow: hidden;
}
<div id="page">
<div id="box-first">
<div id="first">
</div>
<div id="n1">
1500
</div>
</div>
</div>
In order to make this dynamic, we need to adjust the values using JS and the transition will do the job. For this we can define a data-attribute
for the state that we convert to the needed value.
Here is an example where I also simplified the html and used pseudo element and CSS variables
setTimeout(function() {
$('.box').each(function() {
var d = $(this).data('state');
$(this).attr("style", "--s:" + d);
});
}, 1000);
body {
margin: 0;
background: #000;
}
.box {
width: 200px;
height: 100px;
background-color: #fff;
border-radius: 200px 200px 0 0;
margin: 10px;
position: relative;
display: inline-flex;
z-index: 0;
overflow: hidden;
}
.box:before {
content: "";
position: absolute;
z-index: -1;
border-radius: 200px 200px 0 0;
background: linear-gradient(to right, green, yellow, orange, red);
background-size: 2000% 100%;
background-repeat: no-repeat;
background-position: calc(var(--s, 0) * 1%) 0%;
transition:2s linear;
width: 200px;
height: 100px;
transform: rotate(calc((var(--s, 0)/100 + 1)*180deg));
transform-origin: 50% 100%;
top: 0px;
right: 0px;
}
.box:after {
content: attr(data-number);
font-size: 20px;
color: #fff;
font-weight: bold;
text-align: center;
margin: auto auto 0;
width: 100px;
height: 50px;
line-height: 50px;
background: #000;
border-radius: 100px 100px 0 0;
z-Index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box" data-number="1500" data-state="50"></div>
<div class="box" data-number="1000" data-state="20"></div>
<div class="box" data-number="3000" data-state="80"></div>
<div class="box" data-number="6000" data-state="100"></div>
You may notice that all will have the same duration since the transition is the same for all. In case you want a different duration and keep the same speed, simply use the CSS variable within the transition also.
setTimeout(function() {
$('.box').each(function() {
var d = $(this).data('state');
$(this).attr("style", "--s:" + d);
});
}, 1000);
body {
margin: 0;
background: #000;
}
.box {
width: 200px;
height: 100px;
background-color: #fff;
border-radius: 200px 200px 0 0;
margin: 10px;
position: relative;
display: inline-flex;
z-index: 0;
overflow: hidden;
}
.box:before {
content: "";
position: absolute;
z-index: -1;
border-radius: 200px 200px 0 0;
background: linear-gradient(to right, green, yellow, orange, red);
background-size: 2000% 100%;
background-repeat: no-repeat;
background-position: calc(var(--s, 0) * 1%) 0%;
transition: calc(2s * var(--s, 0)/100) linear;
width: 200px;
height: 100px;
transform: rotate(calc((var(--s, 0)/100 + 1)*180deg));
transform-origin: 50% 100%;
top: 0px;
right: 0px;
}
.box:after {
content: attr(data-number);
font-size: 20px;
color: #fff;
font-weight: bold;
text-align: center;
margin: auto auto 0;
width: 100px;
height: 50px;
line-height: 50px;
background: #000;
border-radius: 100px 100px 0 0;
z-Index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box" data-number="1500" data-state="50"></div>
<div class="box" data-number="1000" data-state="20"></div>
<div class="box" data-number="3000" data-state="80"></div>
<div class="box" data-number="6000" data-state="100"></div>
来源:https://stackoverflow.com/questions/52213872/how-stop-keyframe-animation-exactly-after-1-second-without-to-use-settimeout