Make animation happen when a section is in view using javascript - javascript

I am still learning web designing and I wanted to start an animation on a div which is in middle of the page. I searched for it but everywhere I found it using j-query. Is there any way it can be done using pure CSS and JavaScript.
<div>A lot of contents which take whole screen</div>
<div>Section where animation has to happen when come into view</div>
Please help if it can be done using javascript only and if not then what is the easiest way of doing it.

I just searched like you did and found a pure js answer
REFERENCE
var elements;
var windowHeight;
document.getElementById('content').innerText = "A lot of content to fill up the page. ".repeat(500)
function init() {
elements = document.querySelectorAll('.noanimfornow');
windowHeight = window.innerHeight;
}
function checkPosition() {
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var positionFromTop = elements[i].getBoundingClientRect().top;
//console.log(positionFromTop,windowHeight);
if (positionFromTop - windowHeight <= 0) {
element.classList.add('animateme');
element.classList.remove('noanimfornow');
}
if (positionFromTop - windowHeight > 0) {/*newly added:Edit2*/
element.classList.add('noanimfornow');
element.classList.remove('animateme');
}
}
}
window.addEventListener('scroll', checkPosition);
window.addEventListener('resize', init);
init();
checkPosition();
#keyframes myanim {
from {
opacity: 0;
transform: scale(.7, .7)
}
to {
opacity: 1;
}
}
.animateme {
animation: myanim 5s;
}
.noanimfornow {
opacity: 0;
}
<div id="content"></div>
<div class="noanimfornow">Section where animation has to happen when come into view</div>
First for the div you need animation only when you scroll, set class to noanimfornow ,
Next in js we check the position of scroll, and set the class to animateme when it reaches into view,
We also check any resizing event and start init function if needed in js
finally we put some animation for those in css

This answer does exactly the same as the other answer, but it uses IntersectionObserver
Thus "No need to enter JS on every scroll." - A Haworth's comment
This Code is referred from here
also i have used Tschallacka's edit to remove copy paste (using .repeat(500) in js)
var elements;
var windowHeight;
document.getElementById('content').innerText = "A lot of content to fill up the page. ".repeat(500)
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const square = entry.target.querySelector('.noanimfornow');
if (entry.isIntersecting) {
square.classList.add('animateme');
return; // if we added the class, exit the function
}
// We're not intersecting, so remove the class!
square.classList.remove('animateme');
});
});
observer.observe(document.querySelector('.animwrapper'));
#keyframes myanim {
from {
opacity: 0;
transform: scale(.7, .7)
}
to {
opacity: 1;
}
}
.animateme {
animation: myanim 5s;
}
.noanimfornow {
opacity: 0;
}
<div id="content"></div>
<div class="animwrapper">
<div class="noanimfornow">Section where animation has to happen when come into view</div>
</div>

Related

Simple appearing animation

Just make a very very simple animation:
p {
animation: appear 1s linear 1;
font-family:monospace;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any helps!</p>
It works, but I am just thinking is it possible to make the first letter appear first, after the first letter appearing, then the second, and finally the last letter (now the whole content of p will appear at the same time)
Does CSS have any selectors to select every characters in the element and is it possible to achieve the effect use CSS only?
I only know first-letter and last-letter selector in css, but not sure if css has selector for every letters
If not only with CSS, I would be also Ok with JS solution?
Appreciate for any helps provided~
Here's a vanilla JS approach. First we extract the text, empty the element, then rebuild it one character at a time.
let p = document.querySelector('.anim');
// store value
let t = p.innerText.split(''),
counter = 0
p.innerHTML = '';
let inter = setInterval(() => {
if (counter == t.length) clearInterval(inter)
else p.insertAdjacentHTML('beforeend', `<span>${t[counter++]}</span>`);
}, 100)
p.anim span {
animation: appear 1s linear 1;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p class='anim'>Thank you for any helps!</p>
If a JS-based solution is acceptable, then you can easily achieve this using a combination of DOM manipulation (wrapping each non-space character in a <span> element), and then sequentially toggling a class that runs the animation.
The sequential part can be achieved by simply looping through the generated <span> element, and then awaiting for a promise to be resolved.
In your question it is not clear how you want to be a delay to be calculated: I simply assume you want an arbitrary/customizable delay. In this case, a simple promise-based sleep function can get the job done:
function sleep(ms = 0) {
return new Promise(r => setTimeout(r, ms));
}
async function fadeInCharacters(el) {
// NOTE: Use a custom data- attribute to ensure we only target the <span> elements we generate
el.innerHTML = el.textContent.replace(/([^\s])/g, '<span data-animate>$1</span>');
for (const span of el.querySelectorAll('span[data-animate]')) {
span.classList.add('animate');
await sleep(100);
}
}
fadeInCharacters(document.querySelector('p'));
.animate {
animation: appear 1s ease-in-out 1 forwards;
}
span {
opacity: 0;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any help!</p>
If you want to wait for each character before fading in the next, then change the await function into something that hooks into the animationend event, although I am inclined to believe this is not what you intend. Just putting it out here for the sake of completeness:
function onAnimationEnd(el) {
return new Promise(r => el.addEventListener('animationend', () => r()));
}
async function fadeInCharacters(el) {
// NOTE: Use a custom data- attribute to ensure we only target the <span> elements we generate
el.innerHTML = el.textContent.replace(/([^\s])/g, '<span data-animate>$1</span>');
for (const span of el.querySelectorAll('span[data-animate]')) {
span.classList.add('animate');
await onAnimationEnd(span);
}
}
fadeInCharacters(document.querySelector('p'));
.animate {
animation: appear 1s ease-in-out 1 forwards;
}
span {
opacity: 0;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any help!</p>

Utilizing local variables of a function and setTimeout

I have hit a snag in a website I am building. I have a function which adds a specific class to different ids. The end result is to have images fade out one at a time on the home page. I used the local variable a so that I can easily code the different id's without having to write 3 separate functions. However I am having trouble using setTimeout. I cannot seem to get setTimeout to work at all. Any help would be greatly appreciated. The classes get added to the ids with no problem, I am just having trouble with the setTimeout function. I've used multiple methods I've seen on here but I don't really understand them. Any help would be greatly appreciated. Here is my code:
window.onload;
var fetchOne =document.getElementById('picOne');
console.log(fetchOne);
var fetchTwo =document.getElementById('picTwo');
var fetchThree =document.getElementById('picThree');
function AttachClass (a){
a.className ='opacity';
}
setTimeout(AttachClass.bind(null,a),8000);
AttachClass (fetchOne);
AttachClass (fetchTwo);
AttachClass (fetchThree);
#picOne{
opacity: 1;
transition: opacity 2s ease-in-out
}
#picOne.opacity{
opacity: 0;
}
#picTwo{
opacity: 1;
z-index: -1;
transition: opacity 2s ease-in-out
}
#picTwo.opacity{
opacity: 0;
}
#picThree{
opacity: 1;
z-index: -2;
transition: opacity 2s ease-in-out
}
#picThree.opacity{
opacity: 0;
}
<div class="landscape_pics">
<img id="picOne" src="media/Photographs/BK3U8791.JPG">
<img id="picTwo" src="media/Photographs/2014Feb%20-%20Vacation%20-%20Sao%20Paulo%20-%200071a.jpg">
<img id="picThree" src="media/Photographs/2014Aug06%20-%20Stadium%20-%20001.JPG">
</div>
You're only setting up 1 handler for 1 image with that timeout and a is never defined within the code you've pasted so the setTimeout won't work.
In order for them to fade out separately you'd need to create one setTimeout per image you want faded.
for (var i = 0; i < images.length; i++) {
setTimeout(AttachClass.bind(null, images[i]), 2000);
}
Secondly, that window.onload seems to be a remnant of something, you can safely remove that.
Thirdly, there's no need to call the function AttachClass after you've setup the setTimeout since it's now handled by setTimeout.
Now you'd just need to stick the images you've queried the DOM for in to an array called images and off you'd go.
It'd look something like this
var fetchOne = document.getElementById('picOne');
var fetchTwo = document.getElementById('picTwo');
var fetchThree = document.getElementById('picThree');
function AttachClass(a) {
a.classList.add('opacity'); // IE10+ support only
};
let images = [fetchOne, fetchTwo, fetchThree];
for (var i = 0; i < images.length; i++) {
setTimeout(AttachClass.bind(null, images[i]), 5000);
}
If you'd like to have it further refined, you could stick a class on all images. I chose the class name pic, query the DOM for that class name and then set the above timeouts.
var images = document.getElementsByClassName('pic');
function AttachClass(a) {
a.classList.add('opacity');
}
for (var i = 0; i < images.length; i++) {
setTimeout(AttachClass.bind(null, images[i]), 1000);
}
JSBIN here for you to play around with!
Happy hacking!
Probably you need something like this:
window.onload = function() {
AttachClass();
function AttachClass() {
setTimeout(function() {
var a = document.querySelector('.pics img:not(.opacity)');
if (a) {
a.className = 'opacity';
AttachClass();
}
}, 1200);
}
}
.pics img {
width: 100px;
opacity: 1;
transition: opacity 1s ease-in-out
}
img.opacity {
opacity: 0;
}
<div class="pics">
<img src="https://pbs.twimg.com/profile_images/843090368837042176/Nl-rCb9c_400x400.jpg">
<img src="https://pbs.twimg.com/profile_images/843090368837042176/Nl-rCb9c_400x400.jpg">
<img src="https://pbs.twimg.com/profile_images/843090368837042176/Nl-rCb9c_400x400.jpg">
</div>
Thanks everyone for your help. They helped me find my eventual answer. This is how I ended up solving my snag.
window.onload
var fetchOne =document.getElementById('picOne');
console.log(fetchOne);
var fetchTwo =document.getElementById('picTwo');
var fetchThree =document.getElementById('picThree');
setInterval(function() {
setTimeout(function AttachClass1 (){ fetchOne.className ='opacity';},1000);
setTimeout(function AttachClass2 (){ fetchTwo.className ='opacity';},8000);
setTimeout(function AttachClass3 (){ fetchThree.className ='opacity';},14000);
setTimeout(function RemoveClass2 () { fetchTwo.classList.remove("opacity");},14000)
setTimeout(function RemoveClass1 () { fetchOne.classList.remove("opacity");},18000)
setTimeout(function RemoveClass3 () { fetchThree.classList.remove("opacity");},18000)
},24000);
The images flip through one at a time just as I wanted. Then they return with the classlist.remove property. I can also control all the intervals as I like. I'm not familiar at all with JQuery yet so some of the answers above confused me a little. A like to stick to mainly just javascript and this is what I am most comfortable with at the moment. Thanks again for all your help!
Here you go
var imgs = document.querySelectorAll('.fade');
var delay = 500;
for (var i = 0; i < imgs.length; i++) {
(function(i){
setTimeout(function () {
imgs[i].classList.add('opacity');
}, delay * (i + 1))
})(i)
}
.fade {
opacity:.2;
transition: 1s all ease-in-out;
}
.fade.opacity {
opacity:1
}
<div>
<img src="http://lorempixel.com/200/150/people/" alt="" class="fade" id="i1">
</div>
<div>
<img src="http://lorempixel.com/200/150/food/" alt="" class="fade" id="i2">
</div>
<div>
<img src="http://lorempixel.com/200/150/sports/" alt="" class="fade" id="i3">
</div>

Make a Header disappear and reappear after waiting 2 seconds

I ANSWERED DOWN BELOW
I want to make the header of my document once scrolled play an animation to fade and then use style.display to make it unusable. Here is the code:
<script type="text/javascript">
function Scroll()
{
var head = document.getElementById('header')
if (window.pageYOffset > 1)
{
head.style.display = "none";
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
}
}
window.addEventListener("scroll",Scroll);
</script>
I don't know how to make this though so that it will wait two seconds before running the document.getElementById('header').style.display = "none".
I have in the <style> tag to do the fade out animation with the .opacity, it is just the .display that I want to make wait the 2 seconds of animation.
Also I have no idea how to use JQuery or other documents' code so I need it to be purely in HTML, JavaScript or CSS. Thanks. (I'm a noob)
If what you need is to run after 2 seconds
For that you can make use of setInterval or setTimeout depending on your needs.
setTimeout(function(){ alert("This is after 3 sec"); }, 3000);
setInterval : Runs after every specified time interval.
setTimeout : Runs only once after waiting specified time.
W3CSchool Doc
If what you need is to wait till the DOM is loaded.
For that you will have to check the event DOMContentLoaded.
whatever the code you have it should be within this.
document.addEventListener("DOMContentLoaded", function (event) {
function Scroll()
{
var head = document.getElementById('header')
if (window.pageYOffset > 1)
{
head.style.display = "none";
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
}
}
window.addEventListener("scroll",Scroll);
}
I hope this solved your problem.
The best way to approach this for performance and maintainability is to let css handle the animation and only use javascript to capture the event and add a class.
Also, you need a way to make sure the function bound to the scroll event only fires once.
Finally, if you want to destroy the element, you can use javascript to listen for the 'animationend' event (the name is browser-dependent), which is captured by the browser, and fire your function to destroy the element at that time (instead of having a fixed 2 second delay as you are trying to do).
Here's a codepen demo of the implementation I just described: http://codepen.io/anon/pen/NdWGqO
Here's the relevant js and css:
//js
var head = document.getElementById('header');
var hasScrolled = false;
function onScroll() { // a function should only start with a capital letter if it is a constructor
if (!hasScrolled) {
head.className += " fade-out";
window.removeEventListener("scroll",onScroll); // remove this so it only fires once
}
hasScrolled = true; // this prevents the class from being added multiple times
}
function destroyElement() {
head.className += " hidden"; // the 'hidden' class will give the element 'display : none'
}
function captureEvents () {
window.addEventListener("scroll",onScroll);
// capture the animation end event, there are better ways to do this to support all browsers FYI
head.addEventListener("animationend", destroyElement);
head.addEventListener("webkitAnimationEnd", destroyElement);
head.addEventListener("oanimationend", destroyElement);
head.addEventListener("MSAnimationEnd", destroyElement);
}
document.addEventListener("DOMContentLoaded", captureEvents);
//css
.hidden {
display: none;
}
.fade-out {
animation-name: fadeOut;
animation-duration: 4s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
-webkit-animation-name: fadeOut;
-webkit-animation-duration: 4s;
-webkit-animation-iteration-count: 1;
-webkit-animation-fill-mode: forwards;
}
#keyframes fadeOut {
from {opacity: 1; }
to {opacity: 0; }
}
#-webkit-keyframes fadeOut {
from {opacity: 1; }
to {opacity: 0; }
}
I figured it out... here is the code for js:
<script type="text/javascript">
function Scroll()
{
var head = document.getElementById('header');
var timeOut;
var displayNone = 'head.style.display = "none"'
if (window.pageYOffset < 1)
{
clearTimeout(timeOut);
}
if (window.pageYOffset > 1)
{
timeOut = setTimeout(function(){displayNone}, 2000);
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
stopcount();
}
}
window.addEventListener("scroll",Scroll);
Then in css:
#header
{
height: 100px;
width: 100%;
position: fixed;
background:radial-gradient(circle,#777,#666);
transition:all 0.2s;
}
And finally in HTML:
<header id="header"></header> /* header info goes in here */
idk why but when I set the display none part to a variable it fixed it.

Triggering CSS3 Keyframes with javascript multiple times

I am triggering CSS3 Keyframes with javascript but its working for with first call after that any call to that function doesn't animate my div.
Here the Javascript code
function animateShare (imgSrc){
var share = document.getElementById("shareTools");
share.style.animation = "testAnimate 1s ease-in-out 0s"
//shareTools.style.animationPlayState = "running";
}
Sample of the issue (Click red box to preview)
var box = document.getElementById("box");
function animateBox(){
box.style.animation = "box 1s ease-in-out 0s";
}
#box{
background:red;
width:100px;
height:100px;
}
#keyframes box {
50%{width:300px;}
}
<div id='box' onclick='animateBox()'><div>
JSFIDDLE
I want it to animate everytime i call this function.
You can use well known hack: destroy and create element to reset animation.
var box = document.getElementById("box");
function animateBox(){
//destroy and create hack
document.body.removeChild(box);
document.body.appendChild(box);
box.style.animation = "box 1s ease-in-out 0s";
}
#box{
background:red;
width:100px;
height:100px;
}
#keyframes box {
50%{width:300px;}
}
<div id='box' onclick='animateBox()'><div>
In case someone is still interested by this there is another trick that works:
Changing the dataset value to something it has never been, and then use a css matching on that data.
I used this technique when animating the collapsing/uncollapsing of a menu, which of course can happen multiple times. Here's how I did it:
#menu[data-closed^="1"]{
animation:menu_closing;
}
#menu[data-closed^="0"]{
animation:menu_opening;
}
So the animation is based on the first character of the dataset (1 or 0).
Then in the click event that wants to close/open the menu:
var closed = menu_ele.dataset.closed // closed right now
? parseInt( menu_ele.dataset.closed.substr(0,1) )
: 0; // assuming menu is initialized open
var counter = menu_ele.dataset.closed // nb of times the menu was closed or open
? parseInt( menu_ele.dataset.closed.substr(1) )
: 0;
menu_ele.dataset.closed = ''+(1-closed)+(counter+1);
This way the "closed" dataset variable changes like this at every click:
11 (closing for the first time)
02 (reopening for the first time)
13
04
15
06
...
The first digit indicates whether it is currently closed, while all the rest is a counter to make the value new every time.
Think about your code - after first call it does nothing, becouse it already changed animation property of that element.
According to this CSS-Tricks article:
function animateShare (imgSrc){
var share = document.getElementById("shareTools");
share.style.animation = "testAnimate 1s ease-in-out 0s";
shareTools.style.animationPlayState = "paused";
shareTools.style.animationPlayState = "running";
}
add this code in the body section after the element for which the animation is being played-
<script>
document.getElementById('shareTools').addEventListener("animationend", function () {
this.removeAttribute("style");
})
</script>
or if you dont want to remove style attribute ,because you have other css than animation then create a class and add class dynimacally and remove it as above code.
You can reset the animation by just removing the animation property from the styles of the element after the animation has complete - removing the element is unnecessary. In my example, I set the duration in JS, but you could just as easily add an animationend hook to keep it simpler.
JSFiddle
var duration = 1000;
document.getElementById('box').addEventListener('click', function onClick(ev) {
var el = this;
el.style.animation = 'box ' + (duration / 1000 + 's') + ' ease-in-out';
setTimeout(function() {
el.style.animation = ''
}, duration);
});
#box {
background: red;
width: 100px;
height: 100px;
}
#keyframes box {
50% {
width: 300px;
}
}
<div id='box'><div>

Fading in and out with ES6 and CSS3

So I have a function that I'm trying to create the loops through an array to update a div's innerHTML with JavaScript. I was hoping to set the opacity to 0 and then 1 between setting the new data each time, without using jQuery's fadeIn() and fadeOut().
Here is what I have so far. I think I'm very close, but not sure what I'm doing that's slightly off.
Thanks!
slide(index, tweets, element) {
let self = this;
element.innerHTML = data[index].text;
element.style.opacity = 1;
setTimeout(() => {
index++;
element.style.opacity = 0;
setTimeout(self.slide(index, data, element), 2000);
}, 5000);
}
EDIT
I forgot to mention I'm banking on CSS3 for animation by adding a class to my div that changes with this:
transition: opacity 2s ease-in-out;
I don't know how the code you provided relates to the problem at hand, but here's a simple demo on how to fade out, change the text and then fade back in.
You should be able to expand on this for your needs.
var d = document.querySelector("div");
window.addEventListener("load", function() {
d.classList.add("hidden");
});
var i = 0;
d.addEventListener("transitionend", function() {
if (this.classList.contains("hidden")) {
i++;
this.innerHTML = "SUCCESS! ---> " + i;
}
this.classList.toggle("hidden");
});
div {
opacity: 1;
transition: opacity 2s;
}
div.hidden {
opacity: 0;
}
<div>LOADING...</div>
It just adds the hidden class to trigger the fade out and it binds a transitionend handler to change the text and remove the class for the fade in.

Categories