I felt like I needed to add a little pizazz to some of my components. I do this using CSS3 animations. The below is an example of how I did just that. Now, a quick disclaimer, a lot of this is hardcoded but I could refactor it to make it a bit more, well, shall I say, extensible. Given that though, I think the concepts are valuable all the same and don’t need to be predicated on extensibility.
With that out of the way, let me show you what you get with the code:
Keyframes, keyframes, keyframes:
Keyframes are the basis for building animations in CSS3. Essentially, we set brakepoints at certain intervals from completion 0-100% to animate components. Here’s an example of what that looks like:
/* This is the bounce in left animation */ .THIS .bounceInLeft{ animation: bounceInLeft 1s; } @keyframes bounceInLeft { 0% { opacity: 0; transform: translateX(-2000px); } 60% { opacity: 1; transform: translateX(30px); } 80% { transform: translateX(-10px); } 100% { transform: translateX(0); } }
At the 0 percent marker, we’re saying set the opacity of the content to 0 and push it 2000 pixels to the left, at 60% make it completely visible and set the x coordinates to 30px from it’s position, at 80% set it to the left of it’s final position by 10 pixels, and at 100% set it to stay in place. What this does is creates a bounce in from the left, a minor shift back to the left and then settles it in its final position.
For simplicity sake, I’ve only included the @keyframes annotation, but each browser has what’s called a webkit. This is used to make sure that the animations perform across different versions of browsers, @-webkit-keyframes for safari / earlier versions of chrome / earlier versions of opera, @-moz-keyframes for firefox, @-o-keyframes for opera. The webkits are for specific versions of browsers and for brevity sake, the code excludes them.
The class “bounceInLeft” is then used on the div and will operate for a duration of one second. I found any shorter than that was not pleasing to the eye and any longer meant that the content just simply took forever to load. Now, if I had more real content, I could probably get away with this by adding visually appealing stuff to make you scroll less quickly, but you get the idea.
Window.setInterval():
For this code, I had to be able to determine the distance from the top of the browser to my content. I only want to poll the distance every second, since less than that might be a bit much. I then set an attribute “image” with the static resources I used to store the images.
({ doInit: function(component, event, helper) { /** * Description: Set a polling event to check the scroll from the top * if within certain params, then change image and show content */ window.setInterval( function() { var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; console.log(scrollTop); var image = component.get("v.image"); if (scrollTop >= 0 && scrollTop < 400 && image != $A.get("$Resource.lake")) { helper.addAnimation(component, 1); } else if (scrollTop >= 400 && scrollTop < 800 && image != $A.get("$Resource.stars")) { helper.addAnimation(component, 2); } else if (scrollTop >= 800 && image != $A.get("$Resource.dock")) { helper.addAnimation(component, 3); } } ,1000); } })
This code essentially says, every second, tell us where we are at on the page. If we’re less greater than or equal to 0 and less than 400 pixels and the image isn’t currently the lake image, set it to be the lake image. The helper then will add and remove classes appropriately.
({ /** * Description: Sets the animation for each section. Adds hidden to the sections when not displaying & readds animation for continual actions (v once and done). */ addAnimation : function(component, section) { var section1 = component.find("section1Content"); var section2 = component.find("section2Content"); var section3 = component.find("section3Content"); if(section === 1){ $A.util.removeClass(section1, "slds-hidden"); $A.util.addClass(section1, "bounceInLeft"); $A.util.addClass(section2, "slds-hidden"); $A.util.removeClass(section2, "bounceInUp"); $A.util.addClass(section3, "slds-hidden"); $A.util.removeClass(section3, "bounceInDown"); component.set("v.image", $A.get("$Resource.lake")); } else if(section === 2){ $A.util.removeClass(section2, "slds-hidden"); $A.util.addClass(section2, "bounceInUp"); $A.util.addClass(section1, "slds-hidden"); $A.util.removeClass(section1, "bounceInLeft"); $A.util.addClass(section3, "slds-hidden"); $A.util.removeClass(section3, "bounceInDown"); component.set("v.image", $A.get("$Resource.stars")); } else if(section === 3){ $A.util.removeClass(section3, "slds-hidden"); $A.util.addClass(section3, "bounceInDown"); $A.util.addClass(section1, "slds-hidden"); $A.util.removeClass(section1, "bounceInLeft"); $A.util.addClass(section2, "slds-hidden"); $A.util.removeClass(section2, "bounceInUp"); component.set("v.image", $A.get("$Resource.dock")); } } })
The content looks like this for the sections:
<div aura:id="section3"> <div aura:id="section3Content" class="mainContentContainer slds-hidden"> <div class="slds-text-heading_large mainContent"> Extra new content! </div> <div class="slds-text-body_regular">The quick brown fox jumps over the lazy dog.</div> </div> </div> <!-- Image --> <div> <div id="img" style="{! 'background-image: url(\'' + v.image + '\')'}" /> </div>
There are three sections and one image div. Each section can have it’s own content and its own animation. Section 3 for example, based on the helper above will use bounceInDown, meaning it will come from the top to the bottom of the page.
Want to see the full code? I’ve made it available here on my github for brevity’s sake. Please note the static resources are large so I would only use this POC in a dev org. Happy animating!