Lightbox and Animation Effects
Many portfolio websites today employ a lightbox of some kind for their portfolio images. In this tutorial, we will apply the same to our website. The image cover will zoom-in when the users click on it, along with the other images that are in the content, so the users will be able to see each image therein more closely.Here are the tools we need to accomplish this:
Magnific Popup
We're going to rely on a jQuery Lightbox plugin called Magnific Popup by Dmitry Semenov. It's lightweight in size, fast, and responsive — just the way we want it.Animate.css
We will also incorporate CSS3 animation to help our website come alive. We'll be adopting a few snippets from Animate.css, which provides a tremendous collection of CSS3 animation through a number of drop-in classes to apply the animation immediately.Integrating Magnific Popup
Let's start by adding the Magnific Popup stylesheet to thehead
tag.1 2 3 4 5 | < link rel = "stylesheet" href = "//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.min.css" > < link rel = "stylesheet" href = "//cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/0.9.9/magnific-popup.css" > < link rel = "stylesheet" href = "//cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css" > < link href = 'http://fonts.googleapis.com/css?family=Cantata+One|Open+Sans:300,600' rel = 'stylesheet' type = 'text/css' > < link rel = "stylesheet" href = "css/style.css" > |
1 2 3 | < script src = "//cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js" ></ script > < script src = "//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/handlebars.min.js" ></ script > < script src = "//cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/0.9.9/jquery.magnific-popup.min.js" ></ script > |
data
attribute, data-project-id="{{this.id}}"
to the figure
element that wraps the portfolio cover image, as follows.01 02 03 04 05 06 07 08 09 10 11 12 13 | ... < figure class = "portfolio-cover" title = "{{this.name}}" data-project-id = "{{this.id}}" > {{#if this.covers.[404]}} < img class = "portfolio-image" src = "{{this.covers.[404]}}" alt = "" > {{else}} {{#if this.covers.[230]}} < img class = "portfolio-image" src = "{{this.covers.[230]}}" alt = "" > {{else}} < img class = "portfolio-image" src = "{{this.covers.[202]}}" alt = "" > {{/if}} {{/if}} </ figure > ... |
data-project-id
contains the ID number of the portfolio, as seen below.We will use the
data
attribute to retrieve the content of the selected portfolio with that ID assigned later on.After that, we will also need to change the cursor appearance to
zoom-in
, as follows:1 2 3 4 5 6 7 8 9 | ... .portfolio-cover { cursor : pointer ; cursor : -webkit-zoom-in; cursor : -moz-zoom-in; cursor : zoom-in; width : 100% ; } ... |
zoom-in
cursor will suggest that the image is zoom-able; the user should expect that they can click the image. However, the zoom-in
value is not yet supported in any version of Internet Explorer, according to MDN. So that's why we've also specified the cursor to pointer
prior to cursor: zoom-in
as the fallback for Internet Explorer as well as the other browsers that may not support it.Making it Work
Now, we will add the script to initialize Magnific Popup. Since the plan is not only to show the portfolio cover image, but also bring the other images in the content, the script might look a bit hefty. So here we will add the script sequentially. The first thing we will write is the jQuery.on('click')
method. We will only execute Magnific Popup when the user clicks on the cover image.1 2 3 | $( '#portfolio' ).on( 'click' , '.portfolio-cover' , function () { //the rest of the script goes here... } |
$this
, the this variable refers to the object bound to the.on()
method.projectID
will contain$this.data('project-id')
which grabs that ID number from thedata-project-id
attribute. We will use this ID to retrieve the content through the Behance API.beProjectContentAPI
will contain the Behance API endpoints to retrieve the Behance project content.keyName
, this variable forms the key name that we will be using for storing the data retrieved from Behance in localStorage. The name format will bebehanceProjectImages-
then followed by the project ID number. Contrary to what we have done previously, we now use localStorage to store the data instead of using sessionStorage. The reason being that we assume that Behance users would rarely update the content once it has been published. So, in this case, we are better to use localStorage, as it will store the data persistently; the data will remain in the browser as long as we do not intentionally delete it.
1 2 3 4 5 6 | $( '#portfolio' ).on( 'click' , '.portfolio-cover' , function () { var $ this = $( this ), projectID = $ this .data( 'project-id' ), beProjectContentAPI = 'http://www.behance.net/v2/projects/' + projectID + '?callback=?&api_key=' + apiKey, keyName = 'behanceProjectImages-' + projectID; } |
showGallery()
. We will also apply the following options in Magnific Popup:items
; theitems
are very important here. This will contain the list of images that will be displayed in the Lightbox.gallery
; when we enable gallery, Magnific Popup will adds arrows to navigate through each image therein.type
; we will use image for the only content type allowed in the Lightbox.
.magnificPopup('open')
so it opens the Lightbox immediately after initialization.01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | $( '#portfolio' ).on( 'click' , '.portfolio-cover' , function () { var $ this = $( this ), projectID = $ this .data( 'project-id' ), beProjectContentAPI = 'http://www.behance.net/v2/projects/' + projectID + '?callback=?&api_key=' + apiKey, keyName = 'behanceProjectImages-' + projectID; function showGallery(dataSource) { $ this .magnificPopup({ items: dataSource, gallery: { enabled: true }, type: 'image' }).magnificPopup( 'open' ); }; } |
showGallery()
under certain conditions; if the data for the selected portfolio is available in localStorage, go get it and execute the showGallery()
, otherwise get the data from the API with $.getJSON()
first, then execute showGallery()
and store the data in localStorage for use in the future. As we did previously, we have to use JSON.stringify()
to convert the data into a string so it can be saved in localStorage, then we will use JSON.parse()
to format the data back to JSON.Checking for Images
The thing that we have to note here is that the content retrieved from the API could be video, embedded video, or text, which are not allowed; we only accept the image content type. Thus, before posting the data to localStorage, we need to add the following piece of code to filter the content.1 2 3 4 5 6 | var src = []; $.each(projectContent.project.modules, function (index, mod) { if (mod.src != undefined) { src.push({ src: mod.src }); } }); |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | $( '#portfolio' ).on( 'click' , '.portfolio-cover' , function () { var $ this = $( this ), projectID = $ this .data( 'project-id' ), beProjectContentAPI = 'http://www.behance.net/v2/projects/' + projectID + '?callback=?&api_key=' + apiKey, keyName = 'behanceProjectImages-' + projectID; function showGallery(dataSource) { $ this .magnificPopup({ items: dataSource, gallery: { enabled: true }, type: 'image' }).magnificPopup( 'open' ); }; if (localStorage.getItem(keyName)) { var srcItems = JSON.parse(localStorage.getItem(keyName)); showGallery(srcItems); } else { $.getJSON(beProjectContentAPI, function (projectContent) { var src = []; $.each(projectContent.project.modules, function (index, mod) { if (mod.src != undefined) { src.push({ src: mod.src }); } }); showGallery(src); var data = JSON.stringify(src); localStorage.setItem(keyName, data); }); }; }); |
If you inspect the website with Chrome DevTools, you should now find the content is stored in localStorage.
Furthermore, you can navigate through all images in the content using the arrows. But the transition currently feels pretty awkward (right?); it jumps from one image to another instantaneously. So let's make it smoother and more communicative with some animation, shall we?
Integrating Animate.css
First of all, we will need to addmainClass: 'animated'
and removalDelay: 350
to our magnificPopup function.01 02 03 04 05 06 07 08 09 10 11 12 13 | ... function showGallery(dataSource) { $ this .magnificPopup({ items: dataSource, gallery: { enabled: true }, type: 'image' , mainClass: 'animated' , removalDelay: 350 }).magnificPopup( 'open' ); }; ... |
animated
to the Lightbox. The animated
class is the class used in Animate.css to designate animation for an element. This class would also be useful to enable or disable the animation as you like; if you want to disable it simply remove the mainClass: 'animated',
line.We also added
removalDelay
, which specifies the duration before the Lightbox is removed from the DOM completely. The delay herein will give the animation some time of visibility.Adapting Keyframe Styles
Next, we will adopt a few CSS Keyframes, Transforms, and Transitions that form the animation effect in Animate.css. We will convert them into LESS format using LESSHat.Let's start with the Keyframes.
1 2 3 4 5 6 7 8 | .keyframes(~ 'fadeInRight, 0% { transform: translateX(20px); opacity: 0; } 100% { transform: translateX(0); opacity: 1; }' ); .keyframes(~ 'fadeInLeft, 0% { transform: translateX(-20px); opacity: 0; } 100% { transform: translateX(0); opacity: 1; }' ); .keyframes(~ 'fadeOutRight, 0% { transform: translateX(0); opacity: 1; } 100% { transform: translateX(20px); opacity: 0; }' ); .keyframes(~ 'fadeOutLeft, 0% { transform: translateX(0); opacity: 1; } 100% { transform: translateX(-20px); opacity: 0; }' ); .keyframes(~ 'fadeInDown, 0% { transform: translateY(-20px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; }' ); .keyframes(~ 'fadeOutDown, 0% { transform: translateY(0); opacity: 1; } 100% { transform: translateY(20px); opacity: 0; }' ); |
fadeInRight
, fadeInLeft
, fadeOutRight
, fadeOutLeft
, fadeInDown
, and fadeOutDown
which have been translated into LESS format with LESSHat .keyframes()
Mixins.There are several parts within the Lightbox that we will animate, namely: the background overlay that covers the entire viewport, the Lightbox content or image, and the navigation arrows.
The overlay background animation is fairly simple. It won't need those Keyframes above, at all, it will simply fade-in when the Lightbox shows up and fade-out when it disappears. Here are all the style rules to achieve that animation.
01 02 03 04 05 06 07 08 09 10 | .mfp-bg.animated { opacity: 0 ; .transition(opacity 350 ms ease-out); } .mfp-bg.mfp-ready.animated { opacity: 0.8 ; } .mfp-bg.mfp-removing.animated { opacity: 0 ; } |
mfp-bg
. In this code, we set its opacity to 0
so it will initially be invisible, and also set the Transition duration for the opacity
property.Furthermore, Magnific Popup will produce a set of new classes for targeting different states; for instance, when the Lightbox is fully shown, it will add the
mfp-ready
class. In this state, we've set the opacity to 0.8. As we've set the Transition, it will give us the animation effect; the opacity will transmit from 0 to 0.8 in 350ms.Then when the Lightbox disappears, Magnific Popup will output the
mfp-removing
class. In this state, we set its opacity back to 0
, making the overlay background invisible again.Below is the style rules that animate the Lightbox content.
1 2 3 4 5 6 7 8 9 | .mfp-wrap.animated .mfp-content { .animation-duration( 350 ms); } .mfp-wrap.animated .mfp-content { .animation-name(fadeInDown); } .mfp-wrap.mfp-removing.animated .mfp-content { .animation-name(fadeOutDown); } |
350ms
. We also apply the Keyframes with .animation-name()
Mixins. Here we've set the content to fade-in and at the same time slide down when it appears, then slide down and fade-out when it disappears.Animating the Navigation Arrows
Lastly, we will add the animation for our Lightbox arrows.01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | .mfp-wrap.animated .mfp-arrow { .animation-duration( 350 ms); } .mfp-wrap.animated .mfp-arrow- left { .animation-name(fadeInRight); } .mfp-wrap.mfp-removing.animated .mfp-arrow- left { .animation-name(fadeOutLeft); } .mfp-wrap.animated .mfp-arrow- right { .animation-name(fadeInLeft); } .mfp-wrap.mfp-removing.animated .mfp-arrow- right { .animation-name(fadeOutRight); } |
Conclusion
It's been a very long tutorial series! We succesfully built a functioning personal portfolio website from the ground with the Behance API as the data source. To build our website, we've also utilized a number of modern tools such as LESS, HandlebarsJS, and Animate.css. It's pretty easy to deploy the website, since it is only a static HTML - in fact, our demo is hosted as a GitHub static page. Alternatively, you can upload it using FTP to a web server.If you want to take this project further, you can add, for example, a "filter" that will sort the portfolio according to its creative field. You could also add nice hover effects. In any case, I hope you enjoyed the series, and learned a couple of tricks that you can adopt in your own website.
No comments:
Post a Comment