How to save object position after animation in JavaScript? - javascript

I am trying to animate an object for a game in which the ball is supposed to move up, right, left and down on pressing user button. I am trying to achieve this by using .animate() function on the ball. But after every animation event, the ball resets to its initial position and does not continue moving from its "new" position. Is there any way to accomplish this?
Following is my condensed code snippet for simplicity:
/* Animation */
var item = document.getElementById('item');
var anim;
function myMoveLeft(){
anim=item.animate([
// keyframes
{ transform: 'translateX(0px)' },
{ transform: 'translateX(-60px)' }
], {
duration: 1000,
iterations: 1
});
}
function myMoveDown(){
anim=item.animate([
// keyframes
{ transform: 'translateY(0px)' },
{ transform: 'translateY(60px)' }
], {
duration: 1000,
iterations: 1
});
// anim.onfinish(()=>{console.log("onfinish ran")})
}
item.addEventListener('animationend', () => {
console.log('Animation ended');
});
button{
display:inline-block;
height:40px;
width:80px;
}
#myContainer {
width: 400px;
height: 400px;
position: relative;
background: lightgray ;
}
#item {
background: orange;
position: absolute;
right:30px;
top:30px;
height: 30px;
width: 30px;
border-radius:50%;
}
<p>
<button onclick="myMoveLeft()">Left</button>
<button onclick="myMoveDown()">Down</button>
</p>
<div id ="myContainer">
<div id="item"></div>
</div>
As seen, I have already tried using .onfinish() and Event Listener 'animationend' hoping I could update the new 'right' and 'top' position but it does not work. Not sure if that would be the right approach.
Could someone please suggest on how to save the element to a new position and animate it further from that new position?
PS: I am also open to suggestions/techniques if you feel there are other better ways to do this.
Thanks a lot in Advance!!

You can make the ball get the final transform value using the fill option, which is the same as animation-fill-mode in css animation.
For not override the transform when you do the next animation, you can save the x and y value as variables, and do any animation according to the current x and y state. (from x to x-60, instead from 0 to -60, etc.)
Example:
/* Animation */
var item = document.getElementById('item');
var anim;
var x=0, y=0;
function myMoveLeft(){
anim=item.animate([
// keyframes
{ transform: `translate(${x}px, ${y}px)` },
{ transform: `translate(${x-60}px, ${y}px)` }
], {
duration: 1000,
iterations: 1,
fill: 'forwards'
});
x -= 60;
}
function myMoveDown(){
anim=item.animate([
// keyframes
{ transform: `translate(${x}px, ${y}px)` },
{ transform: `translate(${x}px, ${y+60}px)` }
], {
duration: 1000,
iterations: 1,
fill: 'forwards'
});
y += 60;
// anim.onfinish(()=>{console.log("onfinish ran")})
}
item.addEventListener('animationend', () => {
console.log('Animation ended');
});
button{
display:inline-block;
height:40px;
width:80px;
}
#myContainer {
width: 400px;
height: 400px;
position: relative;
background: lightgray ;
}
#item {
background: orange;
position: absolute;
right:30px;
top:30px;
height: 30px;
width: 30px;
border-radius:50%;
}
<p>
<button onclick="myMoveLeft()">Left</button>
<button onclick="myMoveDown()">Down</button>
</p>
<div id ="myContainer">
<div id="item"></div>
</div>

Related

How to update an image with a new image after restarting the animation cycle Javascript/JQuery

I recently created my first Javascript program. I'm extremely new to this (about 3 days) and would really love some help as I am completely stumped on what to do next. As of right now I intend to have the program function as follows:
purpose: A streamlabs widget acting as a bit/donation goal
A bunny slowly animating towards a finish at the other end of the screen, a few inches per donation. Upon reaching the finish line, it starts from the beginning again, levels up into a different color or image, repeats, keeps leveling up and so on.
I have finished all animations, movement per donation, and resetting back into the original bunny and repeat, but I am having trouble with changing the image or color from block (which is the bunny) to gold (a golden bunny) and changing for every level up(after hitting the end of the screen and restarting).
I have an event listener already set up and working, for this sake I included a run button to interact with moving the bunny.
bonus
I would later down the line like to have the bunny have dialog after each animation. Somewhere along the lines of "20% through!", or "almost there!"
Could I get any help with some Javascript experts? How would I go about doing this? Thank you
$("#go").click(function() {
var dest = parseInt($("#block").css("margin-left").replace("px", "")) + 100;
if (dest < 700) {
$("#block").animate({
marginLeft: dest + "px"
}, 700);
} else {
$("#block").animate({
marginLeft: "10px"
}, 100);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<style>
#block {
position: relative;
left: 50px;
width: 120px;
}
#go {
position: fixed;
}
#gold {
position: relative;
left: 50px;
-webkit-filter: sepia(100%);
width: 120px;
}
</style>
<img id="block" src="https://media.giphy.com/media/26AHz1avXH7VGg6nm/giphy.gif" />
<div id="go">» Run</div>
<gold id="gold" src="https://media.giphy.com/media/26AHz1avXH7VGg6nm/giphy.gif" />
Link to the program (click the run text to move the image)
http://jsfiddle.net/svr1nmd3/4/
If you want use the css class like you set in your code, you must add this code at the end of else condition:
document.getElementById('block').classList.add("gold")
and replace in css #gold with .gold.
Else you want to change image when whe bunny return at the start point add this code in se else condition.
document.getElementById('block').src = "https://media.giphy.com/media/2yzGhPePXTXpJe9pMm/giphy.gif"
But you must have the gif of golden bunny.
let counter = -1;
$("#go").click(function() {
const maxMarginRight = 300; // just to reduse the width of block for fast try
var dest = parseInt($("#block").css("margin-left").replace("px", "")) + 100;
const colorIndex = ['gold', 'red', 'blue', 'green', 'black']
if (dest < maxMarginRight) {
$("#block").animate({
marginLeft: dest + "px"
}, maxMarginRight);
} else {
counter++;
$("#block").animate({
marginLeft: "10px"
}, 100);
if (colorIndex.length > counter) {
const nextColor = colorIndex[counter];
const prevColor = colorIndex[counter - 1];
// Here I add the class gold at bunny
if (nextColor) document.getElementById('block').classList.add(nextColor);
if (prevColor) document.getElementById('block').classList.remove(prevColor);
}
}
});
#block {
position: relative;
left: 50px;
width: 120px;
}
#go {
position: fixed;
}
/* #gold use the id of elemement to set the style, .gold is a class and you can add/remove it from element */
.gold {
position: relative;
left: 50px;
-webkit-filter: sepia(100%);
width: 120px;
}
.red {
-webkit-filter: invert(40%) grayscale(100%) brightness(40%) sepia(100%) hue-rotate(-50deg) saturate(400%) contrast(2);
filter: grayscale(100%) brightness(40%) sepia(100%) hue-rotate(-50deg) saturate(600%) contrast(0.8);
}
.blue {
-webkit-filter: grayscale(100%) brightness(30%) sepia(100%) hue-rotate(-180deg) saturate(700%) contrast(0.8);
filter: grayscale(100%) brightness(30%) sepia(100%) hue-rotate(-180deg) saturate(700%) contrast(0.8);
}
.green {
-webkit-filter: grayscale(100%) brightness(40%) sepia(100%) hue-rotate(50deg) saturate(1000%) contrast(0.8);
filter: grayscale(100%) brightness(40%) sepia(100%) hue-rotate(50deg) saturate(1000%) contrast(0.8);
}
.black {
-webkit-filter: invert(30%) grayscale(100%) brightness(70%) contrast(4);
filter: invert(30%) grayscale(100%) brightness(70%) contrast(4);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<img id="block" src="https://media.giphy.com/media/26AHz1avXH7VGg6nm/giphy.gif" />
<div id="go">» Run</div>

How to animate a progress bar with negatives using Element.animate()

I'm attempting to mimic the following widget with HTML/CSS/JavaScript:
https://gyazo.com/76bee875d35b571bd08edbe73ead12cb
The way that I have it set up is the following:
I have a bar with a background color that has a gradient from red to green which is static.
I then have two blinders that is supposed to represent the negative space to give the illusion that the colored bars are animating (in reality, the blinders are simply sliding away)
I did it this way because I figured it might be easier instead of trying to animate the bar going in both directions, but now I'm not so sure lol. One requirement that I'm trying to keep is that the animation only deals with transform or opacity to take advantage of optimizations the browser can do (as described here: https://hacks.mozilla.org/2016/08/animating-like-you-just-dont-care-with-element-animate/)
The example has a few buttons to help test various things. The "Random positive" works great, and is exactly what I want. I haven't quite hooked up the negative yet tho because I'm not sure how to approach the problem of transitioning from positive to negative and vice-versa.
Ideally, when going from a positive to a negative, the right blinder will finish at the middle, and the left blinder will pick up the animation and finish off where it needs to go.
So for example, if the values is initially set to 40%, and the then set to -30%, the right blinder should animate transform: translateX(40%) -> transform: translateX(0%) and then the left blinder should animate from transform: translateX(0%) -> transform: translateX(-30%) to expose the red.
Also, the easing should be seamless.
I'm not sure if this is possible with the setup (specifically keeping the easing seamless, since the easing would be per-element, I think, and can't "carry over" to another element?)
Looking for guidance on how I can salvage this to produce the expected results, or if there's a better way to deal with this.
Note: I'm using jquery simply for ease with click events and whatnot, but this will eventually be in an application that's not jquery aware.
Here's my current attempt: https://codepen.io/blitzmann/pen/vYLrqEW
let currentPercentageState = 0;
function animate(percentage) {
var animation = [{
transform: `translateX(${currentPercentageState}%)`,
easing: "ease-out"
},
{
transform: `translateX(${percentage}%)`
}
];
var timing = {
fill: "forwards",
duration: 1000
};
$(".blind.right")[0].animate(animation, timing);
// save the new value so that the next iteration has a proper from keyframe
currentPercentageState = percentage;
}
$(document).ready(function() {
$(".apply").click(function() {
animate($("#amount").val());
});
$(".reset").click(function() {
animate(0);
});
$(".random").click(function() {
var val = (Math.random() * 2 - 1) * 100;
$("#amount").val(val);
animate(val);
});
$(".randomPos").click(function() {
var val = Math.random() * 100;
$("#amount").val(val);
animate(val);
});
$(".randomNeg").click(function() {
var val = Math.random() * -100;
$("#amount").val(val);
animate(val);
});
$(".toggleBlinds").click(function() {
$(".blind").toggle();
});
$(".toggleLeft").click(function() {
$(".blind.left").toggle();
});
$(".toggleRight").click(function() {
$(".blind.right").toggle();
});
});
$(document).ready(function() {});
.wrapper {
margin: 10px;
height: 10px;
width: 800px;
background: linear-gradient(to right, red 50%, green 50%);
border: 1px solid black;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.blind {
height: 100%;
position: absolute;
top: 0;
background-color: rgb(51, 51, 51);
min-width: 50%;
}
.blind.right {
left: 50%;
border-left: 1px solid white;
transform-origin: left top;
}
.blind.left {
border-right: 1px solid white;
transform-origin: left top;
}
<div class="wrapper">
<div class='blind right'></div>
<div class='blind left'></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script>
<input id="amount" type="number" placeholder="Enter percentage..." value='40' />
<button class="apply">Apply</button>
<button class="random">Random</button>
<button class="randomPos">Random Positive</button>
<button class="randomNeg">Random Negative</button>
<button class="toggleBlinds">Toggle Blinds</button>
<button class="toggleLeft">Toggle L Blind</button>
<button class="toggleRight">Toggle R Blind</button>
<button class="reset" href="#">Reset</button>
I've modified your code. Have a look at the code.
let currentPercentageState = 0;
function animate(percentage) {
var animation = [{
transform: `translateX(${currentPercentageState}%)`,
easing: "ease-out"
},
{
transform: `translateX(${percentage}%)`
}
];
var timing = {
fill: "forwards",
duration: 1000
};
if (percentage < 0) {
$(".blind.right")[0].animate(
[{
transform: `translateX(0%)`,
easing: "ease-out"
},
{
transform: `translateX(0%)`
}
], timing);
$(".blind.left")[0].animate(animation, timing);
} else {
$(".blind.left")[0].animate(
[{
transform: `translateX(0%)`,
easing: "ease-out"
},
{
transform: `translateX(0%)`
}
], timing);
$(".blind.right")[0].animate(animation, timing);
}
// save the new value so that the next iteration has a proper from keyframe
//currentPercentageState = percentage;
}
$(document).ready(function() {
$(".apply").click(function() {
animate($("#amount").val());
});
$(".reset").click(function() {
animate(0);
});
$(".random").click(function() {
var val = (Math.random() * 2 - 1) * 100;
$("#amount").val(val);
animate(val);
});
$(".randomPos").click(function() {
var val = Math.random() * 100;
$("#amount").val(val);
animate(val);
});
$(".randomNeg").click(function() {
var val = Math.random() * -100;
$("#amount").val(val);
animate(val);
});
$(".toggleBlinds").click(function() {
$(".blind").toggle();
});
$(".toggleLeft").click(function() {
$(".blind.left").toggle();
});
$(".toggleRight").click(function() {
$(".blind.right").toggle();
});
});
$(document).ready(function() {});
.wrapper {
margin: 10px;
height: 10px;
width: 800px;
background: linear-gradient(to right, red 50%, green 50%);
border: 1px solid black;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.blind {
height: 100%;
position: absolute;
top: 0;
background-color: rgb(51, 51, 51);
min-width: 50%;
}
.blind.right {
left: 50%;
border-left: 1px solid white;
transform-origin: left top;
}
.blind.left {
border-right: 1px solid white;
transform-origin: left top;
}
<div class="wrapper">
<div class='blind right'></div>
<div class='blind left'></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script>
<input id="amount" type="number" placeholder="Enter percentage..." value='40' />
<button class="apply">Apply</button>
<button class="random">Random</button>
<button class="randomPos">Random Positive</button>
<button class="randomNeg">Random Negative</button>
<button class="toggleBlinds">Toggle Blinds</button>
<button class="toggleLeft">Toggle L Blind</button>
<button class="toggleRight">Toggle R Blind</button>
<button class="reset" href="#">Reset</button>
You need to animate the things in two steps. The first step is to reset the previous state to initial state(which should be set to 0) and in the second step, you need to run the other animation which will actually move it to the destination state.
In order to achive this you can do,
let currentPercentageState = 0;
const animationTiming = 300;
function animate(percentage) {
let defaultTranformVal = [{
transform: `translateX(${currentPercentageState}%)`,
easing: "ease-out"
}, {transform: `translateX(0%)`}];
var animation = [{
transform: `translateX(0%)`,
easing: "ease-out"
},{
transform: `translateX(${percentage}%)`,
easing: "ease-out"
}];
var timing = {
fill: "forwards",
duration: animationTiming
};
if (percentage < 0) {
if(currentPercentageState > 0) {
$(".blind.right")[0].animate(defaultTranformVal, timing);
setTimeout(() => {
$(".blind.left")[0].animate(animation, timing);
}, animationTiming);
} else {
$(".blind.left")[0].animate(animation, timing);
}
}
if(percentage > 0) {
if(currentPercentageState < 0) {
$(".blind.left")[0].animate(defaultTranformVal, timing);
setTimeout(() => {
$(".blind.right")[0].animate(animation, timing);
}, animationTiming);
} else {
$(".blind.right")[0].animate(animation, timing);
}
}
// save the new value so that the next iteration has a proper from keyframe
currentPercentageState = percentage;
}
Here, you will see we have two transformations. The first one defaultTranformVal will move the currentPercentageState to zero and then the other one which will move from 0 to percentage.
You need to handle a couple of conditions here. The first one is if you are running it the first time(means there is no currentPercentageState), you don't need to run defaultTranformVal. If you have currentPercentageState then you need to run defaultTranformVal and then run the second animation.
Note:- You also need to clear the timeout in order to prevent the memory leak. This can be handle by storing the setTimout return value and then when next time it's running clear the previous one with the help of clearTimeout.
Here is the updated codepen example:-
https://codepen.io/gauravsoni119/pen/yLeZBmb?editors=0011
EDIT: I actually did manage to solve this!
let easing = "cubic-bezier(0.5, 1, 0.89, 1)";
let duration = 1000;
let easeReversal = y => 1 - Math.sqrt((y-1)/-1)
https://codepen.io/blitzmann/pen/WNrBWpG
I gave it my own cubic-bezier function of which I know the reversal for. The post below and my explanation was based on an easing function using sin() which isn't easily reversible. Not only that, but the built in easing function for ease-out doesn't match the sin() one that I had a reference for (I'm not really sure what the build in one is based on). But I realized I could give it my own function that I knew the reversal for, and boom, works like a charm!
This has been a very informative experience for me, I'm glad that I've got a solution that works. I still think I'll dip my toes in the other ideas that I had to see which pans out better in the long term.
Historical post:
So, after a few nights of banging my head around on this, I've come to the conclusion that this either isn't possible the way I was thinking about doing it, or if it is possible then the solution is so contrived that it's probably not worth it and I'd be better off developing a new solution (of which I've thought of one or tow things that I'd like to try).
Please see this jsfiddle for my final "solution" and a post-mortem
https://jsfiddle.net/blitzmann/zc80p1n4/
let currentPercentageState = 0;
let easing = "linear";
let duration = 1000;
function animate(percentage) {
percentage = parseFloat(percentage);
// determine if we've crossed the 0 threshold, which would force us to do something else here
let threshold = currentPercentageState / percentage < 0;
console.log("Crosses 0: " + threshold);
if (!threshold && percentage != 0) {
// determine which blind we're animating
let blind = percentage < 0 ? "left" : "right";
$(`.blind.${blind}`)[0].animate(
[
{
transform: `translateX(${currentPercentageState}%)`,
easing: easing
},
{
transform: `translateX(${percentage}%)`
}
],
{
fill: "forwards",
duration: duration
}
);
} else {
// this happens when we cross the 0 boundry
// we'll have to create two animations - one for moving the currently offset blind back to 0, and then another to move the second blind
let firstBlind = percentage < 0 ? "right" : "left";
let secondBlind = percentage < 0 ? "left" : "right";
// get total travel distance
let delta = currentPercentageState - percentage;
// find the percentage of that travel that the first blind is responsible for
let firstTravel = currentPercentageState / delta;
let secondTravel = 1 - firstTravel;
console.log("delta; total values to travel: ", delta);
console.log(
"firstTravel; percentage of the total travel that should be done by the first blind: ",
firstTravel
);
console.log(
"secondTravel; percentage of the total travel that should be done by the second blind: ",
secondTravel
);
// animate the first blind.
$(`.blind.${firstBlind}`)[0].animate(
[
{
transform: `translateX(${currentPercentageState}%)`,
easing: easing
},
{
// we go towards the target value instead of 0 since we'll cut the animation short
transform: `translateX(${percentage}%)`
}
],
{
fill: "forwards",
duration: duration,
// cut the animation short, this should run the animation to this x value of the easing function
iterations: firstTravel
}
);
// animate the second blind
$(`.blind.${secondBlind}`)[0].animate(
[
{
transform: `translateX(${currentPercentageState}%)`,
easing: easing
},
{
transform: `translateX(${percentage}%)`
}
],
{
fill: "forwards",
duration: duration,
// start the iteration where the first should have left off. This should put up where the easing function left off
iterationStart: firstTravel,
// we only need to carry this aniamtion the rest of the way
iterations: 1-firstTravel,
// delay this animation until the first "meets" it
delay: duration * firstTravel
}
);
}
// save the new value so that the next iteration has a proper from keyframe
currentPercentageState = percentage;
}
// the following are just binding set ups for the buttons
$(document).ready(function () {
$(".apply").click(function () {
animate($("#amount").val());
});
$(".reset").click(function () {
animate(0);
});
$(".random").click(function () {
var val = (Math.random() * 2 - 1) * 100;
$("#amount").val(val);
animate(val);
});
$(".randomPos").click(function () {
var val = Math.random() * 100;
$("#amount").val(val);
animate(val);
});
$(".randomNeg").click(function () {
var val = Math.random() * -100;
$("#amount").val(val);
animate(val);
});
$(".flipSign").click(function () {
animate(currentPercentageState * -1);
});
$(".toggleBlinds").click(function () {
$(".blind").toggle();
});
$(".toggleLeft").click(function () {
$(".blind.left").toggle();
});
$(".toggleRight").click(function () {
$(".blind.right").toggle();
});
});
animate(50);
//setTimeout(()=>animate(-100), 1050)
$(function () {
// Build "dynamic" rulers by adding items
$(".ruler[data-items]").each(function () {
var ruler = $(this).empty(),
len = Number(ruler.attr("data-items")) || 0,
item = $(document.createElement("li")),
i;
for (i = -11; i < len - 11; i++) {
ruler.append(item.clone().text(i + 1));
}
});
// Change the spacing programatically
function changeRulerSpacing(spacing) {
$(".ruler")
.css("padding-right", spacing)
.find("li")
.css("padding-left", spacing);
}
changeRulerSpacing("30px");
});
.wrapper {
margin: 10px auto 2px;
height: 10px;
width: 600px;
background: linear-gradient(to right, red 50%, green 50%);
border: 1px solid black;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.blind {
height: 100%;
position: absolute;
top: 0;
background-color: rgb(51, 51, 51);
min-width: 50%;
}
.blind.right {
left: 50%;
border-left: 1px solid white;
transform-origin: left top;
}
.blind.left {
border-right: 1px solid white;
transform-origin: left top;
}
#buttons {
text-align: center;
}
/* Ruler crap */
.ruler-container {
text-align: center;
}
.ruler, .ruler li {
margin: 0;
padding: 0;
list-style: none;
display: inline-block;
}
/* IE6-7 Fix */
.ruler, .ruler li {
*display: inline;
}
.ruler {
display:inline-block;
margin: 0 auto;https://jsfiddle.net/user/login/
background: lightYellow;
box-shadow: 0 -1px 1em hsl(60, 60%, 84%) inset;
border-radius: 2px;
border: 1px solid #ccc;
color: #ccc;
height: 3em;
padding-right: 1cm;
white-space: nowrap;
margin-left: 1px;
}
.ruler li {
padding-left: 1cm;
width: 2em;
margin: .64em -1em -.64em;
text-align: center;
position: relative;
text-shadow: 1px 1px hsl(60, 60%, 84%);
}
.ruler li:before {
content: '';
position: absolute;
border-left: 1px solid #ccc;
height: .64em;
top: -.64em;
right: 1em;
}
<div class="wrapper">
<div class='blind right'></div>
<div class='blind left'></div>
</div>
<div class="ruler-container">
<ul class="ruler" data-items="21"></ul>
</div>
<div id="buttons">
<input id="amount" type="number" placeholder="Enter percentage..." value='-80' />
<button class="apply">Apply</button>
<button class="random">Random</button>
<button class="randomPos">Random Positive</button>
<button class="randomNeg">Random Negative</button>
<button class="flipSign">Flip Sign</button>
<button class="toggleBlinds">Toggle Blinds</button>
<button class="toggleLeft">Toggle L Blind</button>
<button class="toggleRight">Toggle R Blind</button>
<button class="reset" href="#">Reset</button>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script>
<hr />
<p><strong>A note</strong> on the attempt made here:</p>
<p>
I was trying to animate a percentage bar that has both positive and negative values. But I set a challenge as well: I wanted to achieve this via animations utilizing only the compositor - which means animating opacity or transform <strong>only</strong> (no color, width, height, position, etc). The ideas presented here were based on the concept of blinds. I have a static element with a background gradient of red to green, then I have two elements that "blind" the user to the background. These blinds, being a simple element, simply slide into and out of place.
</p>
<p>The problem that I ran into was timing the two animations correctly when they switched signage. It's currently working (very well) for linear animation, but as soon as you introduce an easing function it gets wonky. The reason for this is due to the value that I'm using to set the first animation length (iteration, not duration), as well as the second animations start to pick up where the first left off. The value that I was using is the percentage of the total travel distance that each of the blinds will have to do.</p>
<p>So, for example, if you have a value of 50, and go to -80, that's a total travel distance of 130. The first blind travels <code>50 / 130 = ~0.3846</code> of the total distance, and the second blind will travel <code>1 - ~0.3846 = ~0.6154</code> of the total distance.</p>
<p>But, these are not the correct values for the <em>duration</em> of the animation. Instead, these are the percentages of the easing values (the y-axis). To get the duration for these, I would have to find the x value (given the known y value). eg, for an ease-out animation for a value going from 50 to -80, the animation crosses our 0 at ~0.03846, and we would have to solve for x given <code>0.03846 = sin((x * PI) / 2)</code>.</p>
<p>With the help of Wolfram Alpha, I was able to find a few test values this got me much closer to the actual animation, but the blinds always stopped slightly off the mark. I eventually chalked this up to one of two reasons: the fact that the valuess are always going to be approximate and the browser is never going to be 100% accurate, or / and 2) the browser is using a slightly different easing function than I was using for reference. Regardless, being so constrained by the fact that this "animation" relies on two different aniamtions lining up perfectly, I decided to leave this version be and go in a different direction.</p>
<p>
If anyone finds an actual solution to this, please post an answer here: https://stackoverflow.com/questions/62866844/how-to-animate-a-progress-bar-with-negatives-using-element-animate
</p>
Thanks to those that attempted this admittedly tricky problem

How to run the Callback function inside of the prototype in JavaScript?

According to this question and mdn.doc articles, I'm giving a Callback function inside of aprototype for managing the next code line after it's done.
But even if I create the Callback, the browser keeps ignoring it and running the next code line no matter the Callback is completed or not.
This is the code:
'use strict';
(function() {
function Box($el, $frame) {
// Reassign the Values
this.$el = $el;
this.$frame = $frame;
// Event Register Zone
this.$el.addEventListener('touchstart', (e) => this.start(e));
this.$el.addEventListener('touchmove', (e) => this.move(e));
this.$el.addEventListener('touchend', (e) => this.end(e));
}
Box.prototype = {
start: function(e) {
console.log('touchstart has been detected');
},
move: function(e) {
console.log('touchmove has been detected');
},
end: function(e) {
console.log('touchend has been detected');
this.getanAction(this.moveTop);
},
getanAction: function(callback) {
let bound = callback.bind(this);
bound();
this.$frame[1].classList.add('leftMover');
// Expectation: move the purple box first, and move the orange box next
},
moveTop: function() {
this.$frame[0].classList.add('topMover');
}
}
/***************************************************************/
// Declare & Assign the Original Values
let _elem = document.getElementById('box');
let _frame = _elem.querySelectorAll('.contents');
const proBox = new Box(_elem, _frame);
}());
* {
margin: 0;
padding: 0;
}
#box {
width: auto;
height: 800px;
border: 4px dotted black;
}
.contents {
position: absolute;
width: 200px;
height: 200px;
float: left;
top: 0;
left: 0;
transition: 800ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
}
.purple { background-color: purple; }
.orange { background-color: orange; }
.topMover { top: 600px; }
.leftMover { left: 600px; }
<div id="box">
<div class="contents purple">
</div>
<div class="contents orange">
</div>
</div>
My expectation is the .orange box moves after the .purple box moves done.
Did I miss or do something wrong from the code?
The problem is they are being called one after the other with no delay as JavaScript won't wait for the CSS transition to finish before moving to the next line.
I've fixed waiting for the first transition has finished before calling the bound callback. This way the purple box will move, wait for the transition to finish, then the orange box will move.
'use strict';
(function() {
function Box($el, $frame) {
// Reassign the Values
this.$el = $el;
this.$frame = $frame;
// Event Register Zone
this.$el.addEventListener('touchstart', (e) => this.start(e));
this.$el.addEventListener('touchmove', (e) => this.move(e));
// Added mouse up so it works on desktop
this.$el.addEventListener('mouseup', (e) => this.end(e));
this.$el.addEventListener('touchend', (e) => this.end(e));
}
Box.prototype = {
start: function(e) {
console.log('touchstart has been detected');
},
move: function(e) {
console.log('touchmove has been detected');
},
end: function(e) {
console.log('touchend has been detected');
this.getanAction(this.moveTop);
},
getanAction: function(callback) {
let bound = callback.bind(this);
// Listen for css transition end
this.$frame[0].addEventListener('transitionend', function() {
// Call callback to move orange box
bound()
});
// Move the purple box now
this.$frame[0].classList.add('topMover1')
},
moveTop: function() {
this.$frame[1].classList.add('topMover2');
}
}
/***************************************************************/
// Declare & Assign the Original Values
let _elem = document.getElementById('box');
let _frame = _elem.querySelectorAll('.contents');
const proBox = new Box(_elem, _frame);
}());
* {
margin: 0;
padding: 0;
}
#box {
width: auto;
height: 800px;
border: 4px dotted black;
}
.contents {
position: absolute;
width: 200px;
height: 200px;
float: left;
top: 0;
left: 0;
transition: 800ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
}
.purple { background-color: purple; }
.orange { background-color: orange; }
.topMover1 { top: 600px; }
.topMover2 { left: 600px; }
<div id="box">
<div class="contents purple">
</div>
<div class="contents orange">
</div>
</div>

Load an image (background-image CSS) dynamically using javascript

i'm trying to convert the background-image (css) into an object to change it's value but I couldn't find a solution.
At the moment there are 2 images in the script the first one is a circle loader script and the second one is a script that based of the value of the range it shows or hides part of the picture.
What i'm trying to do is to make load the picture inside the circle loader script.
For instance if the circle loader is 30/100 it should show only display 30% of the picture inside the circle vertically.
Here's the code:
Live version:
var bar = new ProgressBar.Circle(container, {
color: '#aaa',
// This has to be the same size as the maximum width to
// prevent clipping
strokeWidth: 4,
trailWidth: 1,
easing: 'easeInOut',
duration: 1400,
text: {
autoStyleContainer: false
},
from: { color: '#aaa', width: 1 },
to: { color: '#333', width: 4 },
// Set default step function for all animate calls
step: function(state, circle) {
circle.path.setAttribute('stroke', state.color);
circle.path.setAttribute('stroke-width', state.width);
var value = Math.round(circle.value() * 100);
if (value === 0) {
circle.setText('');
} else {
circle.setText(value);
}
}
});
bar.text.style.fontFamily = '"Raleway", Helvetica, sans-serif';
bar.text.style.fontSize = '2rem';
bar.animate(1.0); // Number from 0.0 to 1.0
var sldH = document.getElementById('slider-h');
var sldV = document.getElementById('slider-v');
var img = document.getElementById("image");
// attach change handlers to the sliders
sldH.addEventListener('change', changeHandler);
sldV.addEventListener('change', changeHandler);
function changeHandler(e) {
var isHorizontal = e.srcElement.id == 'slider-h';
// get the sliders values
var valH = sldH.value;
var valV = sldV.value;
// calculate the percentage to pass an absolute length value
// to the clip property and determine the static value
var leftVal = calcPerc(img.width, valH);
var topVal = -1 * calcPerc(img.height, valV);
var clipVal = getClipVal(topVal, leftVal);
// set the images' right offset clip accordingly
img.style.clip = clipVal;
}
function calcPerc(range, val) {
return range / 100 * val;
}
function getClipVal(top, left) {
return 'rect(' + top + 'px, auto, auto, ' + left + 'px)';
}
#container {
margin: 20px;
top: 80px;
width: 200px;
height: 200px;
position: relative;
background-image: url("http://pngimg.com/uploads/bitcoin/bitcoin_PNG47.png");
background-repeat: no-repeat;
background-size: 100% 100%;
}
.btc{
background-image: url("http://pngimg.com/uploads/bitcoin/bitcoin_PNG47.png");
background-repeat: no-repeat;
background-size: 100% 100%;
}
#slider-h,
#slider-v,
#image,
#underlay {
/* absolute positioning is mandatory for clipped elements (#image) */
position: absolute;
}
#slider-h,
#image,
#underlay {
width: 192px;
left: 100px;
}
#slider-h {
top: 50px;
left: 350px;
}
#slider-v {
top: 192px;
left: 200px;;
width: 207px;
-moz-transform: rotate(270deg);
-webkit-transform: rotate(270deg);
-o-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
}
#image,
#underlay {
top: 100px;
left: 350px;
height: 207px;
}
#image {
/* initial clip state */
}
#underlay {
/*background-color: #4C76A5;*/
}
<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>
<input type="range" min="0" max="100" id="slider-h" value="0" />
<input type="range" min="-100" max="0" id="slider-v" value="0" />
<div id="underlay"></div>
<img src="http://pngimg.com/uploads/bitcoin/bitcoin_PNG47.png" id="image" />
I hope somebody can help! Thanks

Jquery slider auto again after finish

I´m new in Jquery and I need to help with this. I have Jquery image slider on the web page with rotation of few different images with timer 3000 per each image - they still repeat. Under the slider I have simple loader bar - based on other color hidding div with animation from left to right with the same timer 3000. Simple. But problem is, that this animation run only once and images rotate constantly. So question is, how can I make my loader bar with "reload" animation after timer is up? Like this - http://www.designbash.com/wp-content/uploads/2010/01/animated-loading-bar.gif
My JQUERY
function playslider(){
var hidden = $('.colorSlide2');
hidden.animate({"left":"0px"}, 3000).addClass('visible');
}
playslider();
My CSS
.colorSlide2 {
position:absolute;
width:100%;
z-index:2;
left:-100%;
height: 5px;
background: #f8e508;
}
.colorSlide1 {
position: relative;
width: 100%;
height: 5px;
background: #254b8b;
}
Try
$.fx.interval = -10;
function playslider(){
var hidden = $(".colorSlide2")
, reset = hidden.css("left");
return hidden.animate({"left":"0%"}, 3000, "linear", function() {
$(this).css("left", reset);
return playslider()
}).addClass("visible");
}
playslider();
$.fx.interval = -10;
function playslider(){
var hidden = $(".colorSlide2")
, reset = hidden.css("left");
return hidden.animate({"left":"0%"}, 3000, "linear", function() {
$(this).css("left", reset);
return playslider()
}).addClass("visible");
}
playslider();
.colorSlide2 {
position:absolute;
width:100%;
z-index:2;
left:-100%;
height: 5px;
background: #f8e508;
}
.colorSlide1 {
position: relative;
width: 100%;
height: 5px;
background: #254b8b;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="colorSlide1">
<div class="colorSlide2"></div>
</div>

Categories