I don't understand why there's having a problem with .toggle() when toggling class names with animations. Consider this experiment I made:
var query = document.querySelector.bind(document);
query('button').addEventListener('click', function() {
[].forEach.call(query('.container').children, function(box, i) {
setInterval(function() {
box.classList.toggle('popIn');
}, 300 * i);
})
})
.container > .box {
width: 100px;
height: 100px;
background: cyan;
display: inline-block;
transform: scale(0);
transition: all 0.3s ease-in-out;
}
.container > .box.popIn {
transform: scale(1);
}
<button>Click</button>
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
When I click the button, it toggles the class names of boxes indefinitely as if it's not sure if it adds or removes them. Is it because the .toggle() method is inside a forEach loop?
You're issue is in the setInterval function. You are saying, perform this event every 300ms. What you want is setTimeout, which is telling the event to stop after 300ms. See the below snippet where I have made the change.
See more information on setTimeout
See more information on setInterval
var query = document.querySelector.bind(document);
query('button').addEventListener('click', function() {
[].forEach.call(query('.container').children, function(box, i) {
setTimeout(function() {
box.classList.toggle('popIn');
}, 300 * i);
})
})
.container > .box {
width: 100px;
height: 100px;
background: cyan;
display: inline-block;
transform: scale(0);
transition: all 0.3s ease-in-out;
}
.container > .box.popIn {
transform: scale(1);
}
<button>Click</button>
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
Related
How to animate the each box when scrolling down the box will be animate like fade-in,
if scroll to the .box pageYOffset 20 that box will be fade-in, I tried with AOS third-party library and its worked fine but i want to know how to do scroll down animation without any third-party library
myCode
#HostListener('window:scroll', ['$event'])
onWindowScroll(e) {
const box = document.querySelector('.box');
if (window.pageYOffset < box.clientHeight ) {
box.classList.add('colorChange');
} else {
box.classList.remove('colorChange');
}
}
.container{
text-align: center;
margin: auto;
padding-top: 768px;
}
.box{
background: #007aff;
width: 200px;
height: 200px;
display: block;
word-spacing: 30px;
line-height: 30px;
margin: 30px auto;
}
.colorChange{
background: #fcad2e;
animation: fade-in 1s ease-in-out;
}
#keyframes fade-in {
from{
opacity: 0;
transform: translateY(50px);
}
to{
opacity: 1;
transform: translateY(0);
}
}
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
This works by specifically adding a window scroll event listener.
import { Component, VERSION, HostListener } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
name = "Angular " + VERSION.major;
constructor(){
window.addEventListener("scroll", (event)=>{
debugger;
const box = document.querySelector('.box');
if (window.pageYOffset < box.clientHeight ) {
box.classList.add('colorChange');
} else {
box.classList.remove('colorChange');
}
});
}
}
Here's link to working example
I want to create an animation for an element with following properties:
it animates the element when it enters the viewport
if the element left the viewport and then enters it again it should be animated again
depending on the scroll direction/intersected side (from top or bottom) the animation should be different
For this purpose I use an IntersectionObserver and I came close to the desired outcome.
The only problem I am facing is, when I translate the element in the scroll direction (which is in this case transform: translateY) during the animation. This will cause the IntersectionObserver to trigger multiple or even infinite times.
function isIntersectingFromTop(entry){
return entry.boundingClientRect.bottom != entry.intersectionRect.bottom;
}
function isIntersectingFromBottom(entry){
return entry.boundingClientRect.top != entry.intersectionRect.top;
}
var count = 0;
function incrementCounter(entry){
document.querySelector(".counter").textContent += "intersectionRation (" + count + "): " + entry.intersectionRatio + "\n";
count++;
}
let observer = new IntersectionObserver(
function (entries, observer) {
entries.forEach(function(entry){
incrementCounter(entry)
if (entry.isIntersecting) {
if(isIntersectingFromTop(entry)){
entry.target.classList.add("animated--up-in");
} else if(isIntersectingFromBottom(entry)) {
entry.target.classList.add("animated--down-in")
}
} else {
/** element is not in viewport anymore
* this will be triggered right after the animation starts
* since the translate is moving the elment out of the view
* which is causing a new intersection (isIntersecting = false)
*/
entry.target.classList.remove("animated--up-in");
entry.target.classList.remove("animated--down-in");
}
});
});
observer.observe(document.querySelector(".to-animate"));
.container {
height: 1000px;
width: 100%;
}
.box{
position: static;
width: 100px;
height: 100px;
background: red;
margin-top: 10px;
}
.to-animate{
background: blue;
opacity: 0;
}
.animated--up-in {
animation: animateUpIn 1.5s forwards ease;
}
.animated--down-in {
animation: animateDownIn 1.5s forwards ease;
}
#keyframes animateUpIn {
from {
transform: translateY(100px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
#keyframes animateDownIn {
from {
transform: translateY(-100px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.counter {
position: fixed;
top: 10%;
left: 30%;
color: black;
height: 80%;
width: 50%;
overflow-y: auto;
padding: 10px;
}
<div class="container">
<pre class="counter"></pre>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box to-animate"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
Question
How can I "tell" the IntersectionObserver to ignore translated position and just use the initial/original position to calculate intersections (with no extra element)? Is this even possible?
I think that in this case you need to track the position on the screen of the stationary containers in which the animated elements will be nested.
I have a similar problem with translateX on mobile device
Animation has translateX(-100% or 100%);
threshold: 0.1
Block has width 100%, but it should work with a visibility of 10% (threshold 0.1), therefore it cannot work, the animation switches back and forth, endlessly.
If it's 90%, then it will work
I am trying to have a animation play when an element is selected, and the reverse animation play when the element is deselected. My CSS looks like this:
#keyframes scale-effect {
from {
transform: scale(1);
}
to {
transform: scale(0.75);
}
}
.card.active {
animation: 1s ease-in-out reverse scale-effect;
}
.card.inactive {
animation: 1s ease-in-out forwards scale-effect;
transform: scale(0.75);
}
This plays the correct animations on page load. However, if I try to change the selected class in javascript:
newActiveObject.classList.remove('inactive');
oldActiveObject.classList.remove('active');
oldActiveObject.classList.add('inactive');
newActiveObject.classList.add('active');
Now, the classes get added properly, and I can see the size changes. However, no animation plays.
Things I have tried:
Using setTimeout(..., 0), between removing and adding classes does not have any effect. Same for 1.
Using setTimeout(..., 10), works, but very clear ugly fash of post-animation style before animation starts
Putting the animation in .card, instead of .inactive. This seems to just disable all animation even on page load.
Using getComputedStyle to force DOM redraw in between removing and adding classes. Has no effect.
Does anyone know how to properly replace a animation after removing a class?
Why don't you use transition instead? With animation, it's hard to control the flow. You need either to use a 2nd animation (one for active, one for inactive) or to trigger a reflow in JavaScript (see similar question). Here an example with transition:
const $cards = Array.from(document.getElementsByClassName("card"))
function handleClick(evt) {
$cards.forEach($card => $card.classList.remove("active"))
evt.target.classList.add("active")
}
$cards.forEach($card => $card.addEventListener("click", handleClick))
.card {
display: inline-block;
margin: 10px;
width: 100px;
height: 100px;
border: 1px solid gray;
cursor: pointer;
transition: transform 1s ease-in-out;
transform: scale(0.75);
}
.card.active {
transform: scale(1);
}
<div class="card">CARD 1</div>
<div class="card">CARD 2</div>
<div class="card">CARD 3</div>
<div class="card">CARD 4</div>
If your really need animation, here a working example with 2 animations:
const $cards = Array.from(document.getElementsByClassName("card"))
function handleClick(evt) {
$cards.forEach($card => {
$card.classList.remove("active")
$card.classList.add("inactive")
})
evt.target.classList.remove("inactive")
evt.target.classList.add("active")
}
$cards.forEach($card => $card.addEventListener("click", handleClick))
#keyframes scale-effect-out {
from { transform: scale(1); }
to { transform: scale(0.75); }
}
#keyframes scale-effect-in {
from { transform: scale(0.75); }
to { transform: scale(1); }
}
.card {
display: inline-block;
margin: 10px;
width: 100px;
height: 100px;
border: 1px solid gray;
cursor: pointer;
}
.card.inactive {
animation: 1s forwards ease-in-out scale-effect-out;
}
.card.active {
animation: 1s forwards ease-in-out scale-effect-in;
}
<div class="card inactive">CARD 1</div>
<div class="card inactive">CARD 2</div>
<div class="card inactive">CARD 3</div>
<div class="card inactive">CARD 4</div>
I have faced this before, you need to reset the animation for the target element, before adding another animation by javascript.
var el = document.getElementById('target_element_id');
el.style.animation = 'none'; // clear animation
el.offsetHeight; /* trigger reflow */
and then you can add another animation/class.
If needed try css '!important' while changing animation.
Assuming the active card should start on .75, then get bigger.
.card.active {
transform: scale(0.75); /* moved this to here */
animation: 1s ease-in-out reverse scale-effect;
}
.card.inactive {
animation: 1s ease-in-out forwards scale-effect;
}
When the page is loaded , in a big div, there are six different elements and there are six different functions for the elements.I want to make the functions execute by each other after a sure time for example 1000ms.But the six functions are not bind to one elements ,they are binded to six different elements.
For example, when the page is loaded,I want addClass "line1" to element "#linear1", after 1000ms, removeClass ".line1" from element "#linear1",
and immediately,
addClass "line2" to element "#linear2", after 1000ms, removeClass ".line2" from element "#linear2",
and immediately,
addClass "line3" to element "#linear3", after 1000ms, removeClass ".line3" from element "#linear3"
...
addClass "line6" to element "#linear6", after 1000ms, removeClass ".line6" from element "#linear6"
then back to "#linear1"..."linear6"...loop
jquery(".cspaceintro is a parent div for middlecolumn"):
$(document).ready(function(){
$(".cspaceintro").load(function(){
$("#linear1").addClass("line1");
//here,I don't know what to do next..
})
})
html:
<div class="middlecolumn">
<div class="left1">
<div id="linear1" ></div>
</div>
<div class="left2">
<div id="linear2" ></div>
</div>
<div class="left3">
<div id="linear3" ></div>
</div>
<div class="right1">
<div id="linear4" ></div>
</div>
<div class="right2">
<div id="linear5" ></div>
<!-- <div class="point"></div> -->
</div>
<div class="right3">
<div id="linear6" ></div>
</div>
part of css
.line1{
float: right;
width: 0%;
height: 3px;
background-color: #2e9edd;
background: -webkit-gradient(linear, 0 0, 0 100%, from(#2e9edd), to(#2e9edd));
-webkit-animation:aaa 1s linear 1;
-webkit-animation-fill-mode:both;
}
.line2{
float: right;
position: relative;
top:30px;
width: 0%;
height: 3px;
background-color: #2e9edd;
background: -webkit-gradient(linear, 0 0, 0 100%, from(#2e9edd), to(#2e9edd));
-webkit-animation:aaa 1s linear 1;
-webkit-animation-fill-mode:both;
}
.line3{
float: right;
position: relative;
top:50px;
width: 0%;
height: 3px;
background-color: #2e9edd;
background: -webkit-gradient(linear, 0 0, 0 100%, from(#2e9edd), to(#2e9edd));
-webkit-animation:aaa 1s linear 1;
-webkit-animation-fill-mode:both;
}
#keyframes aaa{
0% {width:0%; }
30% {width:30%; }
60% {width:60%; }
100%{width:95%; }
}
You can use .queue() to queue a function to be called for each element in an array or jQuery object. Set .className at element, attach animationend event to element using .one(), at animation end handler called when css animations complete for the element, remove .className, call next function in queue.
Chain .promise(), .then() to .dequeue() to call function when all functions in queue have been called and jQuery returns promise object.
At .then() function set width of #linearN element to "0%", call original function again, repeatedly, at .then() chained to .promise() when queue is empty of functions, to satisfy "loop" requirement of scheduling same function to be called following asynchronous function calls.
The Question describes six #linearN elements
... addClass "line6" to element "#linear6", after 1000ms, removeClass
".line6" from element "#linear6" then back to
"#linear1"..."linear6"...loop
though there are three .lineN declarations at css, not six. Only first three #linearN elements are passed to function at stacksnippets. When six .lineN declarations are defined at css, remove .slice(0, 3) chained to $("[id^=linear]", middlecolumn).
$(function() {
var middlecolumn = $(".middlecolumn");
var linearLines = $("[id^=linear]", middlecolumn).slice(0, 3);
function animateLines(column, lines) {
return column.queue("lines", $.map(lines, function(el, index) {
return function(next) {
$(el).addClass("line" + (index + 1))
.one("animationend", function() {
$(this).removeClass("line" + (index + 1));
setTimeout(next, 1000);
})
}
})).dequeue("lines").promise("lines")
.then(function() {
console.log("lines queue complete"
, "\n`animateLines` call scheduled at next line");
return animateLines(column, lines.css("width", "0%"));
})
}
animateLines(middlecolumn, linearLines);
})
.line1 {
float: right;
width: 0%;
height: 3px;
background-color: #2e9edd;
background: -webkit-linear-gradient(0 0, 0 100%, from(#2e9edd), to(#2e9edd));
-webkit-animation: aaa 1s linear 1;
-webkit-animation-fill-mode: both;
}
.line2 {
float: right;
position: relative;
top: 30px;
width: 0%;
height: 3px;
background-color: #2e9edd;
background: -webkit-linear-gradient(0 0, 0 100%, from(#2e9edd), to(#2e9edd));
-webkit-animation: aaa 1s linear 1;
-webkit-animation-fill-mode: both;
}
.line3 {
float: right;
position: relative;
top: 50px;
width: 0%;
height: 3px;
background-color: #2e9edd;
background: -webkit-linear-gradient(0 0, 0 100%, from(#2e9edd), to(#2e9edd));
-webkit-animation: aaa 1s linear 1;
-webkit-animation-fill-mode: both;
}
#keyframes aaa {
0% {
width: 0%;
}
30% {
width: 30%;
}
60% {
width: 60%;
}
100% {
width: 95%;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div class="middlecolumn">
<div class="left1">
<div id="linear1"></div>
</div>
<div class="left2">
<div id="linear2"></div>
</div>
<div class="left3">
<div id="linear3"></div>
</div>
<div class="right1">
<div id="linear4"></div>
</div>
<div class="right2">
<div id="linear5"></div>
<!-- <div class="point"></div> -->
</div>
<div class="right3">
<div id="linear6"></div>
</div>
</div>
This is my first post here. I'm looking for some help.
I have multiple div contents which should fade in out and out dynamically. I have got this jsfiddle and made it work for 2 divs, but need it to work for multiple, for example 5 different DIVs.
Is this jsfiddle the best way to go or is there a better option. Here is the code. Here is the jquery.
var fadeinBox = $("#box2");
var fadeoutBox = $("#box1");
function fade() {
fadeinBox.stop(true, true).fadeIn(2000);
fadeoutBox.stop(true, true).fadeOut(2000, function() {
// swap in/out
var temp = fadeinBox;
fadeinBox = fadeoutBox;
fadeoutBox = temp;
// start over again
setTimeout(fade, 1000);
});
}
// start the process
fade();
Here is the html
<div id="wrapper">
<div id="box1" class="box"></div>
<div id="box2" class="box"></div>
</div>
and the CSS
.box {
position: absolute;
height: 100px;
width: 100px;
}
#wrapper {position: relative;}
#box1 {background-color: #F00;}
#box2 {background-color: #00F; display: none;}
Thank you in advance, and the link is below.
Dynamically changing DIV jsfiddle
You could use the same code but make it dynamic instead of two static elements:
var $boxes = $(".box").hide();
var current = 0;
function fade() {
$boxes.eq(current).stop(true, true).fadeOut(2000);
current = (current + 1) % $boxes.length;
$boxes.eq(current).stop(true, true).fadeIn(2000, function(){
setTimeout(fade, 1000);
});
}
fade();
http://jsfiddle.net/3XwZv/637/
What about doing it with CSS3 animations? Depending on your target browsers, this should work well:
Simplify your markup to a single div like this:
<div id="box"></div>
and then the CSS handles all the animation:
#keyframes colorCycle
{
0% {background-color:red;}
20% {background-color:orange;}
40% {background-color:yellow;}
60% {background-color:green;}
80% {background-color:blue;}
100% {background-color:purple;}
}
#-webkit-keyframes colorCycle /* Safari and Chrome */
{
0% {background-color:red;}
20% {background-color:orange;}
40% {background-color:yellow;}
60% {background-color:green;}
80% {background-color:blue;}
100% {background-color:purple;}
}
#box {
height: 100px;
width: 100px;
animation: colorCycle 10s 0s infinite;
-webkit-animation: colorCycle 10s 0s infinite;
}
Here's a Fiddle You can play with the key stops, timings, and colors to get the effect you need.
Depending on your specific application, you could toggle the fade for all boxes and use jQuery's promise() to determine when all boxes have finished fading.
HTML:
<div class="wrapper">
<div class="box box1"></div>
<div class="box box2"></div>
</div>
<div class="wrapper">
<div class="box box1"></div>
<div class="box box2"></div>
</div>
<div class="wrapper">
<div class="box box1"></div>
<div class="box box2"></div>
</div>
CSS:
.box {
position: absolute;
height: 100px;
width: 100px;
}
.wrapper {
position: relative;
width:100px;
height:100px;
margin-bottom:10px;
}
.box1 {
background-color: #F00;
}
.box2 {
background-color: #00F;
display: none;
}
JS:
function fade() {
jQuery('.box').stop(true, true).fadeToggle(2000);
jQuery('.box').promise().done(function(){fade();});
}
fade();
http://jsfiddle.net/3XwZv/638/