Why is the loop property not working on my second animation? - javascript

I'm using the anime JS library, I've added two animations to a div, the second animation starts when the first animation has finished.
But the problem is that I want just the second animation to keep looping, I've set the property "loop" to "true" on the second animation, but it still won't loop, every property works fine except the "loop" property, how can I make just the second animation loop over and over again?
HTML code:
<body>
<div></div>
</body>
CSS code:
div{
height: 100px;
width: 100px;
background-color: rgb(161, 6, 6);
}
JavaScript code:
"use strict";
let square = document.querySelector("div")
anime.timeline()
.add({
targets: square,
backgroundColor: "#3dbf7a",
duration: 2000,
})
.add({
targets: square,
translateX: [0,100],
translateY: [0,100],
duration: 1000,
loop: true,
})

As mentioned in the comments, this is an existing issue in the library. In this workaround, I needed to put the anim.restart() call inside a setTimeout to ensure the javascript executed in a new event loop.
let square = document.querySelector("div");
anime.timeline()
.add({
targets: square,
backgroundColor: "#3dbf7a",
duration: 2000,
}).add({
targets: square,
translateX: [0, 100],
translateY: [0, 100],
duration: 1000,
changeComplete: anim => setTimeout(() => anim.restart())
});
div {
height: 100px;
width: 100px;
background-color: rgb(161, 6, 6);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
<div></div>
jsFiddle

Related

How do you animate an external SVG file with anime.js?

I tried including the svg file as an img element, which didn't work - anime.js didn't recognize the svg. Then put it in an object element, which also didn't work.
So how do you set up a framework to recognize the svg and the elements within it?
You do need to use an object element, but you can't view the html file directly - you need to serve it from a server, e.g. with Python - at a console do
python -m http.server 8001
then view it on http://localhost:8001
Here's an example -
<html>
<!-- note: must run this from a server for svg to load properly -->
<!-- eg `python -m http.server 8001` -->
<!-- see https://stackoverflow.com/questions/11434916/javascript-accessing-inner-dom-of-svg -->
<head>
<script src="anime.min.js"></script>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background: #222;
}
object {
opacity: 0;
background: black;
border: 1px solid #aaa;
}
</style>
</head>
<body>
<script>
function loaded() {
// get references to svg and subelements
const svg = document.querySelector("#svg")
const title = [
svg.contentDocument.querySelector("#path30"),
svg.contentDocument.querySelector("#path34"),
]
const subtitle = [
svg.contentDocument.querySelector("#text54"),
]
const swooshes = [
svg.contentDocument.querySelector("#path42"),
svg.contentDocument.querySelector("#path38"),
svg.contentDocument.querySelector("#path50"),
svg.contentDocument.querySelector("#path46"),
]
// set some initial values
svg.style.opacity = 1
for (const element of title) {
element.style.opacity = 0
}
for (const element of subtitle) {
element.style.opacity = 0
}
for (const element of swooshes) {
element.style.opacity = 0
}
// animate elements
anime({
targets: svg,
delay: 0,
duration: 2000,
background: '#ffffff',
easing: 'linear'
})
anime({
targets: title,
opacity: 1,
delay: 0,
duration: 2000,
easing: 'linear',
})
anime({
targets: subtitle,
opacity: 0.9,
delay: 2000,
duration: 2000,
easing: 'linear',
})
let startTime = 3000
for (let i = 0; i < 4; i++) {
window.setTimeout(() => {
anime({
targets: swooshes[i],
opacity: 1,
duration: 2000,
easing: 'linear',
direction: 'alternate',
loop: true,
})
}, startTime)
startTime += 500
}
}
</script>
<!-- must put svg in an object element -->
<!-- https://stackoverflow.com/questions/28652648/how-to-use-external-svg-in-html -->
<object data="Emergent_Logo.svg" type="image/svg+xml" id="svg" onload="loaded()"></object>
</body>
</html>
animejs requires the raw svg code instead of accessing it via iframe/img tag.
To get the raw code, simply open the .svg file in one of the editors and copy the code from it to directly in your html file.
It should now recognize the code and animate/morph the svg however you want.

How can I change the speed (duration) of an anime.js event using a slider?

I am using anime.js to animate an element that is being bounced back in forth from the edges of its container. I want to be able to adjust the speed of this animation using a range slider that I have elsewhere on the page.
My problem is that while the duration is adjusted, it appears that the slider instantiates completely and does not continue animating to where it originally was suppposed to. I want it to go from the far left to the far right, but when I resize it the animation will only go from the place where it was resized to the end of the container.
These have been my attempts when calling the onchange method of my slider.
function adjustSpeed() {
alternate.duration = 300;
}
and
function adjustSpeed() {
var alternate = anime({
targets: '#alternate .el',
translateX: width,
direction: 'alternate',
loop: true,
duration: 300,
easing: 'linear',
});
}
Any help would be very much appreciated.
I just came across this issue and I've found a better yet not perfect solution. AnimeJS has another static speed property that is by default set to 1. If you change this speed, the animation speed changes, though the animation "jumps" and it doesn't look smooth.
For example, if you want the speed to be 0.5x the original speed, set anime.speed = 0.5.
I'll update this answer if I come up with a better solution.
When you changed speed or duration, you have to stop and remove current animation. After that, you have to start new animation with new duration value.
Here is example of bouncing from left to right element.
var duration = 500
const animateBLS = () => {
const el = document.getElementById('dot')
anime.remove(el)
animation = anime({
targets: [el],
left: '100%',
direction: 'alternate',
loop: true,
easing: 'linear',
duration: duration
});
}
And there is the code for running new animations, called when durations is changed. It's finish current animation with new speed value, and start our main animation functions "animateBLS"
const el = document.getElementById('dot')
if(animation.reversed) {
anime.remove(el)
animation = anime({
targets: [el],
left: '0%',
direction: 'normal',
loop: false,
easing: 'linear',
duration: duration,
complete: () => {
animateBLS()
}
});
} else {
anime.remove(el)
animation = anime({
targets: [el],
left: '100%',
direction: 'normal',
loop: false,
easing: 'linear',
duration: duration,
complete: () => {
animation = anime({
targets: [el],
left: '0%',
direction: 'normal',
loop: false,
easing: 'linear',
duration: duration,
complete: () => {
animateBLS()
}
});
}
});
}
A dirty solution:
Manage frame manually by anime.tick combining with requestAnimationFrame, here're the demo:
https://codesandbox.io/s/anime-js-speed-adjustment-lm0ui?file=/src/index.js
The code is basically self-explained, if you have any further question, let me know.

Trigger anime.js animation when element enters viewport

I'm trying to run an anime.js when an image or element enters the viewport, but i cant seem to get it working. Im trying it with waypoints.js
This is what I have so far, its the 'this' part im having troubles with i think.
$('img').waypoint(function() {
var CSStransforms = anime({
targets: this,
translateX: 250,
scale: 2,
rotate: '1turn'
});
}, {
offset: '100%'
});
You need to target the elements with this.element instead, Here's a working example:
CodePen Demo
Per your question, you would modify it to the following:
jQuery(document).ready(function(){
$('img').waypoint(function() {
var CSStransforms = anime({
targets: this.element,
translateX: 250,
scale: 2,
rotate: '1turn'
});
}, {
offset: '100%'
});
});
You need to change the
targets : this to targets: this.element

Velocity.js clearing default values after animation

I have elements on a page that I want to animate in to view, but after they've animated in, I want to defer further animation on them to CSS (by changing classes)... I am finding that Velocity leaves all my animated properties in the style= tag and makes CSS transitions impossible.
I have a solution below, but resetting the CSS on complete seems iffy, I was wondering if there's a better way to do it?
// first use CSS to hide the element and squish it
$el.css({
width: 0,
left: "-10px",
opacity: 0,
marginRight: 0,
transition: "none"
})
// now animate the width and margin (do this first
// so there's a little gap between existing elements
// before we fade in new element.
.velocity({
width: width,
marginRight: margin
}, {
queue: false,
duration: 230
})
// then fade and move in the new element,
// but this is the iffy bit when it completes
// I have to unset all the styles I've animated?
.velocity({
left: 0,
opacity: 1
}, {
queue: false,
duration: 100,
delay: 130,
complete: function(els) {
$(els).css({
width: "",
left: "",
opacity: "",
marginRight: "",
transition: ""
});
}
});
Typically, you want animation engines to leave styles inline; otherwise final values will pop as they get overwritten by stylesheets upon removal.
You can also do $(els).attr("style", ""); to just clear all styles.

fadeOut() and slideUp() at the same time?

I have found jQuery: FadeOut then SlideUp and it's good, but it's not the one.
How can I fadeOut() and slideUp() at the same time? I tried two separate setTimeout() calls with the same delay but the slideUp() happened as soon as the page loaded.
Has anyone done this?
You can do something like this, this is a full toggle version:
$("#mySelector").animate({ height: 'toggle', opacity: 'toggle' }, 'slow');
For strictly a fadeout:
$("#mySelector").animate({ height: 0, opacity: 0 }, 'slow');
Directly animating height results in a jerky motion on some web pages. However, combining a CSS transition with jQuery's slideUp() makes for a smooth disappearing act.
const slideFade = (elem) => {
const fade = { opacity: 0, transition: 'opacity 400ms' };
elem.css(fade).slideUp();
};
slideFade($('#mySelector'));
Fiddle with the code:
https://jsfiddle.net/00Lodcqf/435
In some situations, a very quick 100 millisecond pause to allow more fading creates a slightly smoother experience:
elem.css(fade).delay(100).slideUp();
This is the solution I used in the dna.js project where you can view the code (github.com/dnajs/dna.js) for the dna.ui.slideFade() function to see additional support for toggling and callbacks.
The accepted answer by "Nick Craver" is definitely the way to go. The only thing I'd add is that his answer doesn't actually "hide" it, meaning the DOM still sees it as a viable element to display.
This can be a problem if you have margin's or padding's on the 'slid' element... they will still show. So I just added a callback to the animate() function to actually hide it after animation is complete:
$("#mySelector").animate({
height: 0,
opacity: 0,
margin: 0,
padding: 0
}, 'slow', function(){
$(this).hide();
});
It's possible to do this with the slideUp and fadeOut methods themselves like so:
$('#mydiv').slideUp(300, function(){
console.log('Done!');
}).fadeOut({
duration: 300,
queue: false
});
I had a similar problem and fixed it like this.
$('#mydiv').animate({
height: 0,
}, {
duration: 1000,
complete: function(){$('#mydiv').css('display', 'none');}
});
$('#mydiv').animate({
opacity: 0,
}, {
duration: 1000,
queue: false
});
the queue property tells it whether to queue the animation or just play it right away
Throwing one more refinement in there based on #CodeKoalas. It accounts for vertical margin and padding but not horizontal.
$('.selector').animate({
opacity: 0,
height: 0,
marginTop: 0,
marginBottom: 0,
paddingTop: 0,
paddingBottom: 0
}, 'slow', function() {
$(this).hide();
});

Categories