How is an HTML element animated as soon as it appears on screen?
I found an example on the stack overflow tour page, where info elements slide into the page as soon as you scroll down far enough. What is the trick behind that?
You need to use JavaScript to detect the location of the viewport and activate it when it becomes visible.
You can use JavaScript to detect and execute the transition and then CSS or JavaScript to do the animations.
There are many jquery based scripts available to accomplish this. Here is one example:
DEMO
1. Create a Html element you want to check if it's in the viewport.
<div class="demo"></div>
2. Load the jQuery javascript library and jQuery Viewport Checker plugin at the end of your document.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="viewportchecker.js"></script>
3. Call the plugin on this element and do something in the javascript.
<script>
$(document).ready(function(){
$('.demo').viewportChecker({
// Class to add to the elements when they are visible
classToAdd: 'visible',
// The offset of the elements (let them appear earlier or later)
offset: 100,
// Add the possibility to remove the class if the elements are not visible
repeat: false,
// Callback to do after a class was added to an element. Action will return "add" or "remove", depending if the class was added or removed
callbackFunction: function(elem, action){}
});
});
</script>
DOWNLOAD THIS SCRIPT
You can use the intersection Observer for this. The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.
const startAnimation = (entries, observer) => {
entries.forEach(entry => {
entry.target.classList.toggle("slide-in-from-right", entry.isIntersecting);
});
};
const observer = new IntersectionObserver(startAnimation);
const options = { root: null, rootMargin: '0px', threshold: 1 };
const elements = document.querySelectorAll('.card');
elements.forEach(el => {
observer.observe(el, options);
});
.card {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.slide-in-from-right {
animation: 1.5s ease-out 0s 1 slideInFromRight forwards;
}
#keyframes slideInFromRight {
0% {
transform: translateX(50%);
}
100% {
transform: translateX(0);
}
}
<div class="card">
<h2>Card one</h2>
</div>
<div class="card">
<h2>Card two</h2>
</div>
<div class="card">
<h2>Card three</h2>
</div>
Related
This is frustrating me to no end. Before I post the code, here's a summary:
The goal, in simple terms: when I double click X, I want it to fade out; when I click Y, I want X to fade in.
The method: I'm using CSS to create the actual fade-in and fade-out "animations." I'm using JavaScript to apply the classes when necessary using a little trickery.
The problem: the fade-in transition doesn't work -- the element just appears instantly. What is driving me insane is the fact that the fade-in, when instantly added back onto a faded-out object, works perfectly. I'll explain this better as a comment in the JS code.
(Yes, I've added opacity: 1 and transition: opacity onto the base elements. It had no effect at all.)
The code:
CSS
*.fade-out {
opacity: 0;
transition: opacity 400ms;
}
*.fade-in {
opacity: 1;
transition: opacity 400ms;
}
*.hide {
display: none;
visibility: hidden;
}
JavaScript
$( '#ArtistEmblem' ).on( 'dblclick', function() {
fadeOut($( '#ArtistEmblem' ));
fadeIn($( '#btnShowLogo' ));
});
$( '#btnShowLogo' ).on( 'click', function() {
fadeOut($( '#btnShowLogo' ));
fadeIn($( '#ArtistEmblem' ));
});
function fadeOut(element) {
element.addClass( 'fade-out' );
setTimeout( function () {
element.addClass( 'hide' );
/*
* I tried immediately adding the 'fade-in' class here
* and it worked -- as soon as the element faded out, it faded
* back in (using the CSS transition). However, outside of this,
* it REFUSES to work; everything appears instantly
*/
console.log('timer triggered');
}, 400);
}
function fadeIn(element) {
element.removeClass( 'hide' );
element.removeClass( 'fade-out' );
element.addClass( 'fade-in' );
}
Relevant HTML
<div id="ArtistEmblem">
<img src="img/logo_artist_2.png" />
</div>
<div id="PopMenu" class="collapse">
<article>
<header>
<b>Debug Menu</b>
</header>
<section>
<button id="btnOpenOverlay">Open Overlay</button>
<button id="btnShowLogo" class="hide">Show Logo</button>
<button id="btnClose">Close Menu</button>
</section>
</article>
</div>
I apologize if this is something obvious but I've wasted far too much time trying to solve it. I am also open to better, faster, or more efficient solutions if that would be the best answer. Thanks in advance!
The problem is that the initial opacity of "hidden" element is 1 by default. You just need to set it to 0. And also remove display: none –
*.hide {
opacity: 0;
}
Also I would do a little refactoring and remove setTimeout:
$('#ArtistEmblem').on('click', function() {
fade($('#btnShowLogo'), $(this));
});
$('#btnShowLogo').on('click', function() {
fade($('#ArtistEmblem'), $(this));
});
function fade(inElement, outElement) {
inElement.removeClass('hide');
inElement.addClass('fade-in');
outElement.removeClass('fade-in');
outElement.addClass('fade-out');
}
If you don't want the hidden element to occupy space and you want it to be displayed-none, then you need to set display: block before starting the fadeOut.
I know you're asking for a JS heavy answer, but I highly recommend toggling a class of "active", "open" or something similar and using CSS with the transition. Less is more here.
Here's an example fiddle of something I've transitions not only the opacity, but also the z-index. That's the key with these transitions if you intend on having any elements below such as buttons that require hovering, clicking, etc.
JS Fiddle
Key parts:
.container {
z-index: -1;
opacity: 0;
transition: z-index .01s 1s, opacity 1s;
}
.container.active {
transition: z-index 0s, opacity 1s;
z-index: 500;
opacity: 1;
}
EDIT
I was just messing around with this type of thing for my own project, and observing how beautiful Stripe handles their navigation bar. Something so simple changes everything, and that's pointer-events. If you're okay with its support, (notable no ie. 10) this is infinitely easier to integrate. Here's another fiddle of the simulation in a nav bar.
The key part is pointer-events: none, as it ignores click events if set to none, almost as if it wasn't there, yet visibly it is. I highly recommend this.
https://jsfiddle.net/joshmoxey/dd2sts7d/1/
Here is an example using Javascript Animate API. Animate API is not supported in IE/Edge though.
var element = document.getElementById("fade-in-out")
var button = document.getElementById("x")
button.addEventListener("click", function(event) {
element.animate([{opacity: 1, visibility: "visible"},{opacity: 0, visibility: "hidden"}], {duration: 2000})
setTimeout(function() { element.remove() }, 2000)
})
button.addEventListener("dblclick", function(event) {
element && element.animate([{opacity: 0}, {opacity: 1}], {duration: 2000})
})
<input id="x" type="button" value="Click here" />
<div id="fade-in-out"> FADE ME </div>
I'm trying to make animation effect for background, it should listen an event that will change images on click.
For instance, I click Sajo Hapyo it should change the background image.
Main issue is that all images will be having different background-images and I'm really stuck with this.
I used backgroundColor: green in my JS for test, since wanted to check, whether it works or not.
At the final version, the background images will be added and it should change on click with nice jquery UI (effect).
Here is screenshot
Please help me out
Here is my code
HTML
<section id="main-showcase">
<div class="showcase-wrapper">
<div class="left-main col-lg-3 col-md-3">
<div class="shadow-effect"><p class="ottogi">OTTOGI</p></div>
<div class="shadow-effect"><p class="sajo">Sajo Hapyo</p></div>
<div class="shadow-effect"><p class="natura">Natura Bogata</p></div>
<div class="shadow-effect"><p class="maloo">ТОО Малу</p></div>
<div class="shadow-effect"><p class="dongush">Dongsuh</p></div>
<div class="shadow-effect"><p class="may">ООО Май</p></div>
</div>
<div class="right-main col-lg-9 col-md-9">
<div class="inner-container">
<h1>Ottogi</h1>
<h2>Южно - Корейские продукты питания высочайшего качества!</h2>
</div>
<div class="box-container">
<div class="main-wrap">
<div id="main-slider" class="first-slider">
[[getImageList?
&tvname=`goods`
&tpl=`goodsSlider.tpl`
]]
</div>
</div>
</div>
</div>
</div>
</section>
JS
$('button.sajo').click(function () {
$('.right-main').animate({
backgroundColor: 'green',
}, 1500);
});
Actually instead of using the jQuery.animate(). You can simply toggle a class on the same element. It is a good practice to avoid animate() and using css3 animations instead.
codepen
Check the codepen sample here. It will explain how to use it. Instead of using keyframes and all. You can simply obtain it using trnasition.
span {
background-color: red;
padding: 10px;
transition: all 1.5s ease;
color: white;
}
.change-color {
background-color: blue;
}
First of all, I cannot see in the HTML the button where you apply the click event listener. However, I assume this button is located somewhere in your HTML code and what you want to do is change the background image on the main slider by clicking on it. To do so you simply have to do the following:
$('button.sajo').click(function () {
$('.right-main').animate({opacity: 0}, 'slow', function() {
$(this)
.css({'background-image': 'url(your_url)'}) //Change url to your image url
.animate({opacity: 1});
}
});
Note that since you do not specify the exact animation you want, I just provided an example with a fade in animation where opacity goes from 0 to 1. You can change this animation to a different one by changing the content of .animate() and leaving the .css() like i wrote there. Hope this helps!
You can use like that
var imageUrl = your image url
$('button.sajo').click(function () {
$('.right-main').css('background-image', 'url(' + imageUrl + ')');
});
.bgcolor{
animation: colorchange 50s; /* animation-name followed by duration in seconds */
/* you could also use milliseconds (ms) or something like 2.5s */
-webkit-animation: colorchange 50s; /* Chrome and Safari */
}
#keyframes colorchange
{
100% {background: green;}
}
#-webkit-keyframes colorchange /* Safari and Chrome - necessary duplicate */
{
100% {background: green;}
}`
Then add this class to the element you want to change the background of:
$('button.sajo').click(function () {
$('.right-main').addClass('bgcolor');
});
I'm implementing some animation by adding and removing classes to an element on mouseover and mouseout. I'm using this method as I found using CSS alone was not reliable; the animation would not complete if the mouse exited the element before the animation finished.
So I have the following code:
<div class="one flip-container">
<div class="flipper">
<div class="front">
<!-- front content -->
</div>
<div class="back">
<!-- back content -->
</div>
</div>
</div>
<script>
jQuery(".flip-container").hover(function () {
jQuery(this).addClass("hover");
},function () {
jQuery(this).delay(2000).queue(function(){
jQuery(this).removeClass("hover");
});
});
</script>
<style>
.flip-container.hover .flipper {
transform: rotateY(180deg);
}
.flipper {
transition: 0.6s;
transform-style: preserve-3d;
position: relative;
}
</style>
This works but sometimes the class 'hover' is not removed, it stays, leaving the element in its animated state. Any idea how to make this more reliable?
Try using mouseenter and then set a timeout function to remove the class that way you wont be adding and removing classes except once each time the mouse enters the area. Also you may want to check to see if the area already has the class to avoid the function from being executed too many times like so:
jQuery(".flip-container").mouseenter(function () {
var el = jQuery(this);
if(!el.hasClass("hover")){
el.addClass("hover");
setTimeout(function(){
el.removeClass("hover");
}, 2000);
}
});
Here is a working fiddle Fiddle Demo
I've got my hover working - but i'm interested in trying to make it more efficient as it does seems to 'lag' when it's finding the .overlay div. I also had the issue where I was animating all .overlay divs on a page, which I consider to be quite a noob mistake.
Anyway, let's learn how to make the below better!
jQuery:
// get aside feature
var aside_feature = $('aside .feature');
// on hover, fade it in
$( aside_feature ).hover(function() {
// get the overlay div
var feature_overlay = $(this).find('.overlay');
$(feature_overlay).stop().fadeIn();
// on hover out, fade it out
}, function() {
$(this).find('.overlay').stop().fadeOut();
});
Markup:
<aside>
<div class="feature">
<div class="overlay">
button
</div><!-- overlay -->
<div class="text">
<p>text</p>
</div><!-- .text-->
<div class="image">
<figure>
<img src="" alt="">
</figure>
</div><!-- .image -->
</div><!-- .feature -->
</aside><!-- aside -->
Fiddle: http://jsfiddle.net/9xRML/5/
Edit - Final Code
Thanks #Shomz, and #Afro.
Final code choices were to use tranisitons, and coupled with modernizr detection for transitions, I changed my hidden overlay div to opacity: 0; *display:none; and javascript as a fallback:
CSS
.overlay {
*display: none;
opacity: 0;
transition: 0.4s all linear;
}
.overlay:hover {
opacity: 1;
}
jQuery
$(function () {
/*=====================================
= Feature overlay =
=====================================*/
if (!Modernizr.csstransitions) {
// get aside feature
var aside_feature = $('aside .feature');
// on hover, fade it in
$( aside_feature ).hover(function() {
$(this).find('.overlay').stop(true, true).fadeIn();
// on hover out, fade it out
}, function() {
$(this).find('.overlay').stop(true, true).fadeOut();
});
}
});
With risking of having my answer out of scope here, if you want to really get performance, you should switch to CSS animations. It's totally possible with your example by setting the default opacity of the overlay to 0 (instead of display: none;) and making it show up on .feature:hover. The trick is to add the transition property like this:
// applies a 4ms transition to any possible property with no easing
transition: all .4s linear;
See the whole example here: http://jsfiddle.net/9xRML/6/
See a nice article about the performance difference (CSS vs. JS) here: http://css3.bradshawenterprises.com/blog/jquery-vs-css3-transitions/ (there are many more, of course)
I think I have solved your issue using the same HTML but changing the following:
JQuery
$('aside .feature').hover(function() {
$(this).find('.overlay').stop(true, true).fadeIn();
}, function() {
$(this).find('.overlay').stop(true, true).fadeOut();
});
CSS
.feature {
background: #ccc;
}
.overlay {
display: none;
}
This means the overlay will only display on hover.
Details on .stop() can be found here.
.stop(true, true)
We can create a nice fade effect without the common problem of multiple queued animations by adding .stop(true, true) to the chain.
DEMO
I am currently trying to do a CSS3 animation in Angular.js.
Before animating I try to set the initial css properties using Javascript.
So, is there a way to initialize an animation using Javascript and then continue the animation using CSS3?
My situation:
When the user clicks on a div, a dialog should appear.
The dialog should start out exactly over the original div (same size, same position), and then grow to a larger size.
I am able to animate the dialog from a predefined position and size:
CSS:
.dialog {
position: absolute;
z-index: 10;
width:600px;
height:400px;
margin-left: -300px;
left:50%;
margin-top: 50px;
}
.dialogHolder.ng-enter .dialog {
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1s;
width:0;
height:0;
margin-left: 0px;
}
.dialogHolder.ng-enter-active .dialog {
width:600px;
height:400px;
margin-left: -300px;
}
I would like to animate the dialog starting at the size of the clicked div.
So far my code (not working yet) looks like this:
HTML:
<div ng-repeat="course in data.courses" ng-click="showDialog($event)">
{{ course.cursus }}
</div>
<!-- Placeholder for blokDialogs -->
<div class="dialogHolder" ng-include="dialogTemplate.url">
DIALOG WILL BE LOADED HERE
</div>
Javascript:
app.controller('blockController', function($scope) {
$scope.showDialog = function(evt) {
// get position and size of the course block
$scope.dialogTemplate.clientRect = evt.target.getBoundingClientRect();
// load the html to show the dialog
$scope.dialogTemplate.url = 'partials/blokDialog.html';
// SHOULD I DO SOMETHING HERE?
};
});
// OR SHOULD I DO SOMETHING LIKE THIS?
app.animation('.dialogHolder', function(){
return {
// SOMEHOW SET THE WIDTH, HEIGHT, TOP, LEFT OF .dialog
};
});
I'd prefer to do this without jQuery to keep the page weight low.
Regards,
Hendrik Jan
You want to use ng-animate http://docs.angularjs.org/api/ngAnimate
If you are using ng-repeat, you can animate when elements enter, leave and move around your repeater. The magic is that you don't even have to put an extra directive in your html, just define your CSS animations accordingly.
So in your case something like this
.repeater.ng-enter, .repeater.ng-leave, .repeater.ng-move {
-webkit-transition:0.5s linear all;
transition:0.5s linear all;
}
.repeater.ng-enter { }
.repeater.ng-enter-active { }
.repeater.ng-leave { }
.repeater.ng-leave-active { }
.repeater.ng-move { }
.repeater.ng-move-active { }
and your HTML
<div ng-repeat="..." class="repeater"/>
In the end, I found the following solution:
HTML:
Create an onClick handler and a placeholder where the dialog is loaded.
<!-- Element on which the user clicks to initialize the dialog -->
<div ng-repeat="course in data.courses"
ng-click="showDialog($event, course)">
{{ course.name }}
</div>
<!-- Placeholder for blokDialogs -->
<div class="dialogHolder"
ng-include="dialogTemplate.url"
onload="showDialogLoaded()">
</div>
HTML Template:
partials/blokDialog.html sets it's style using ng-style.
<div ng-style="dialogTemplate.initialStyle">
...
</div>
Javascript:
The onClick handler sets the initial CSS before the animation starts.
$scope.showDialog = function(evt, course) {
// Load the dialog template
$scope.dialogTemplate.url = 'partials/blokDialog.html';
// set the css before the animation starts
// get position and size of the course block
var clientRect = evt.target.getBoundingClientRect();
$scope.dialogTemplate.initialStyle = {
left: clientRect.left + 'px',
top: clientRect.top + 'px',
width: clientRect.width + 'px',
height: clientRect.height + 'px',
backgroundColor: getComputedStyle(evt.target).backgroundColor
};
};
The style needs to be removed before the animation ends but after the animation started.
The animation starts at the end of the onLoad handler. If we remove the style in the onLoad handler (i.e. in showDialogLoaded), then we are to early.
We use setTimeout to make sure that the removal of the style is done after the animation was started.
$scope.showDialogLoaded = function() {
// remove the style that we set in showDialog
setTimeout(function(){
$scope.dialogTemplate.initialStyle = {};
// we need to $apply because this function is executed
// outside normal Angular handling, so Angular does not know
// that it needs to do a dirty check
$scope.$apply();
}, 0);
};
I hope this can be helpful for others.
Regards,
HJ