Top Ad unit 728 × 90

News Ticker

random

Content slider with Zoom Effect for a predefined area in each slide

Content slider with Zoom Effect

Zoom Slider

Today’s Blueprint is a simple content slider with depth-like zoom functionality. Each slide has a predefined zoom area that will be used to calculate the appropriate scale value for a fullscreen fill. Once the icon for zooming is clicked, the zoom area as well as the page get scaled, creating the illusion that the viewer is approaching the item. Once the whole page is covered, we show some more details.

Navigating the slider will animate the inner parts of the slide, allowing for an independent control of the image area and the title. We are using CSS transitions and dymanic.js for moving the slide elements. Dymanic.js by Michaël Villar is a JavaScript library to create physics-based animations.


Please note that we are using a couple of modern CSS properties, so only contemporary browsers are supported.

Content slider with Zoom Effect

The HTML

<!-- Main container -->
<div class="container">
 <!-- Blueprint header -->
 <header class="bp-header cf">
  <!-- Page title etc. -->
 </header>
 <!-- Grid -->
 <section class="slider">
  <div class="slide slide--current" data-content="content-1">
   <div class="slide__mover">
    <div class="zoomer flex-center">
     <img class="zoomer__image" src="images/iphone.png" alt="iPhone" />
     <div class="preview">
      <img src="images/iphone-content-preview.png" alt="iPhone app preview" />
      <div class="zoomer__area zoomer__area--size-2"></div>
     </div>
    </div>
   </div>
   <h2 class="slide__title"><span>The Classy</span> iPhone 6</h2>
  </div>
  <div class="slide" data-content="content-2">
   <!-- ... -->
  </div>
  <!-- ... -->
  <nav class="slider__nav">
   <button class="button button--nav-prev">
    <i class="icon icon--arrow-left"></i>
    <span class="text-hidden">Previous product</span>
   </button>
   <button class="button button--zoom">
    <i class="icon icon--zoom"></i>
    <span class="text-hidden">View details</span>
   </button>
   <button class="button button--nav-next">
    <i class="icon icon--arrow-right"></i>
    <span class="text-hidden">Next product</span>
   </button>
  </nav>
 </section>
 <!-- /slider-->
 <!-- content -->
 <section class="content">
  <div class="content__item" id="content-1">
   <img class="content__item-img rounded-right" src="images/iphone-content.png" alt="Apple Watch Content" />
   <div class="content__item-inner">
    <h2>The iPhone 6</h2>
    <h3>Incredible performance for powerful apps</h3>
    <p>...</p>
   </div>
  </div>
  <div class="content__item" id="content-2">
   <!-- ... -->
  </div>
  <!-- ... -->
  <button class="button button--close">
   <i class="icon icon--circle-cross"></i>
   <span class="text-hidden">Close content</span>
  </button>
 </section>
 <!-- /content -->
</div>

The CSS

/* Helper classes */
html,
body {
 overflow: hidden;
 height: 100%;
}

.container {
 position: relative;
 overflow: hidden;
 overflow-y: scroll;
 width: 100%;
 height: 100%;
 -webkit-overflow-scrolling: touch;
}

.noscroll .container {
 overflow-y: hidden;
}

.slider {
 position: relative;
 z-index: 200;
 width: 100%;
 margin: 0 auto;
 padding: 0 0 7em;
 text-align: center;
 -webkit-user-select: none;
 -moz-user-select: none;
 -ms-user-select: none;
 user-select: none;
 -webkit-touch-callout: none;
 -khtml-user-select: none;
}

.slide {
 position: absolute;
 top: 0;
 visibility: hidden;
 width: 100%;
 opacity: 0;
}

.slide--current {
 position: relative;
 z-index: 100;
 visibility: visible;
 opacity: 1;
}

.slide__mover {
 position: relative;
 z-index: 100;
}

.slide__title {
 font-size: 1.75em;
 font-weight: normal;
 margin: 0 auto;
 padding: 1em 0 0 0;
}

.slide__title span {
 font-size: 55%;
 font-weight: bold;
 display: block;
 letter-spacing: 2px;
 text-transform: uppercase;
 color: #35303d;
}

.slider__nav {
 position: absolute;
 bottom: 2em;
 width: 100%;
 text-align: center;
}

.button {
 font-size: 1.31em;
 position: relative;
 display: inline-block;
 overflow: hidden;
 margin: 0 25px;
 padding: 0;
 cursor: pointer;
 color: #5c5edc;
 border: none;
 background: none;
}

.button:focus {
 outline: none;
}

.button:hover {
 color: #fff;
}

.text-hidden {
 position: absolute;
 top: 200%;
}

.button--close {
 font-size: 1.55em;
 position: absolute;
 top: 30px;
 right: 30px;
 margin: 0;
 opacity: 0;
 color: #50505a;
 -webkit-transition: opacity 0.3s;
 transition: opacity 0.3s;
}

.content--open .button--close {
 opacity: 1;
}

/* Zoomer */
.zoomer {
 position: relative;
 height: 360px; /* this is needed for IE10 so that vertical flexbox centering works */
}

.flex-center {
 display: -webkit-flex;
 display: -ms-flexbox;
 display: flex;
 -webkit-align-items: center;
 -ms-flex-align: center;
 align-items: center;
 -webkit-justify-content: center;
 -ms-flex-pack: center;
 justify-content: center;
}

.zoomer__image {
 display: block;
 margin: 0;
 -webkit-flex: none;
 -ms-flex: none;
 flex: none;
}

.zoomer__area,
.preview {
 position: absolute;
 top: 50%;
 left: 50%;
 -webkit-transform: translate3d(-50%,-50%,0);
 transform: translate3d(-50%,-50%,0);
}

.zoomer__area:focus {
 outline: none;
}

.zoomer__area--size-1 {
 /* Apple Watch */
 width: 96px;
 height: 118px;
}

.zoomer__area--size-2 {
 /* iPhone */
 width: 112px;
 height: 198px;
}

.zoomer__area--size-3 {
 /* MacBook */
 width: 315px;
 height: 200px;
}

.zoomer__area--size-4 {
 /* iPad */
 width: 150px;
 height: 200px;
}

.zoomer__area--size-5 {
 /* iMac */
 width: 315px;
 height: 189px;
}

.preview {
 overflow: hidden;
 background: #18191b;
}

.preview img {
 display: block;
 border-radius: inherit;
 -webkit-transform: translate3d(0,0,0);
 transform: translate3d(0,0,0);
}

.zoomer--active .preview img {
 -webkit-transform: translate3d(100%,0,0);
 transform: translate3d(100%,0,0);
}

.rounded {
 border-radius: 15px;
}

.rounded-right {
 border-radius: 0 15px 15px 0;
}

.preview__content {
 position: absolute;
 top: 0;
 left: 100%;
 width: 100%;
 height: 100%;
 border-radius: inherit;
}
/* Content */
.content {
 position: fixed;
 z-index: 1000;
 top: 0;
 left: -100%;
 overflow: hidden;
 overflow-y: scroll;
 width: 100%;
 height: 100vh;
 background: #18191b;
 -webkit-overflow-scrolling: touch;
}

.content--open {
 left: 0;
}

.content__item {
 position: absolute;
 top: 0;
 display: -webkit-flex;
 display: -ms-flexbox;
 display: flex;
 overflow: hidden;
 height: 0;
 min-height: 100%;
 margin: 0 auto;
 padding: 2em 0;
 pointer-events: none;
 opacity: 0;
 color: #fff;
 -webkit-align-items: center;
 -ms-flex-align: center;
 align-items: center;
}

.content__item--current {
 pointer-events: auto;
 opacity: 1;
}

.content__item--reset {
 height: auto;
}

.content h2 {
 font-size: 3.5em;
 font-weight: normal;
 margin: 0;
}

.content h3 {
 font-size: 1.95em;
 font-weight: normal;
 margin: 0.25em 0 0.5em;
 color: #685884;
}

.content p {
 font-size: 1.25em;
 line-height: 1.5;
}

.content__item-img {
 display: block;
 max-width: 40vw;
 max-height: 80vh;
 -webkit-transform: translate3d(-120%,0,0);
 transform: translate3d(-120%,0,0);
 -webkit-flex: none;
 -ms-flex: none;
 flex: none;
}

.content__item--current .content__item-img {
 -webkit-transform: translate3d(-10px,0,0);
 transform: translate3d(-10px,0,0);
}

.content__item-inner {
 padding: 0 10vw 0;
 opacity: 0;
 -webkit-transform: translate3d(0,50px,0);
 transform: translate3d(0,50px,0);
}

.content__item--current .content__item-inner {
 opacity: 1;
 -webkit-transform: translate3d(0,0,0);
 transform: translate3d(0,0,0);
}


/**************************/
/* All synced transitions */
/**************************/

.zoomer {
 -webkit-transition: -webkit-transform 0.5s;
 transition: transform 0.5s;
 -webkit-transition-timing-function: cubic-bezier(0.7,0,0.3,1);
 transition-timing-function: cubic-bezier(0.7,0,0.3,1);
}

.zoomer.zoomer--notrans {
 -webkit-transition: none;
 transition: none;
}

.zoomer__image {
 -webkit-transition: opacity 0.3s 0.3s;
 transition: opacity 0.3s 0.3s;
}

.zoomer--active .zoomer__image {
 opacity: 0;
 -webkit-transition-delay: 0s;
 transition-delay: 0s;
}

.preview img {
 -webkit-transition: -webkit-transform 0.6s 0.3s;
 transition: transform 0.6s 0.3s;
 -webkit-transition-timing-function: cubic-bezier(0.2,1,0.3,1);
 transition-timing-function: cubic-bezier(0.2,1,0.3,1);
}

.zoomer--active .preview img {
 -webkit-transition: -webkit-transform 0.3s;
 transition: transform 0.3s;
}

.content {
 -webkit-transition: left 0s;
 transition: left 0s;
}

.content__item {
 -webkit-transition: opacity 0s;
 transition: opacity 0s;
}

.content,
.content__item {
 /* delay for content to disappear and zoomer to start transitioning back to 0 */
 -webkit-transition-delay: 0.3s;
 transition-delay: 0.3s;
}

.content--open,
.content__item--current {
 -webkit-transition: none;
 transition: none;
}

.content__item-img {
 -webkit-transition: -webkit-transform 0.4s;
 transition: transform 0.4s;
 -webkit-transition-timing-function: cubic-bezier(0.7,1,0.8,1);
 transition-timing-function: cubic-bezier(0.7,1,0.8,1);
}

.content__item--current .content__item-img {
 -webkit-transition-timing-function: cubic-bezier(0.2,1,0.3,1);
 transition-timing-function: cubic-bezier(0.2,1,0.3,1);
 -webkit-transition-duration: 1s;
 transition-duration: 1s;
}

.content__item-inner {
 -webkit-transition: -webkit-transform 0.6s, opacity 0.3s;
 transition: transform 0.6s, opacity 0.3s;
 -webkit-transition-timing-function: cubic-bezier(0.7,1,0.8,1), ease;
 transition-timing-function: cubic-bezier(0.7,1,0.8,1), ease;
}

.content__item--current .content__item-inner {
 -webkit-transition-timing-function: cubic-bezier(0.2,1,0.3,1), ease;
 transition-timing-function: cubic-bezier(0.2,1,0.3,1), ease;
 -webkit-transition-duration: 1.7s;
 transition-duration: 1.7s;
}

/* Media Queries */
@media screen and (max-width: 50em) {
 .content__item {
  display: block;
 }
 .content__item-img {
  max-width: calc(100% - 80px);
  max-height: 70vh;
 }
 .content h2 {
  font-size: 3em;
 }
 .content__item-inner {
  font-size: 82%;
  padding: 4em 3em 2em;
 }
}

The JavaScript

<script src="js/classie.js"></script>
<script src="js/dynamics.min.js"></script>
<script src="js/main.js"></script>

/**
 * main.js
 * http://www.teezhelp.blogspot.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 * 
 * Copyright 2015, Codrops
 * http://www.teezhelp.blogspot.com
 */
;(function(window) {

 'use strict';

 var bodyEl = document.body, 
  docElem = window.document.documentElement,
  support = { transitions: Modernizr.csstransitions },
  // transition end event name
  transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd', 'transition': 'transitionend' },
  transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
  onEndTransition = function( el, callback ) {
   var onEndCallbackFn = function( ev ) {
    if( support.transitions ) {
     if( ev.target != this ) return;
     this.removeEventListener( transEndEventName, onEndCallbackFn );
    }
    if( callback && typeof callback === 'function' ) { callback.call(this); }
   };
   if( support.transitions ) {
    el.addEventListener( transEndEventName, onEndCallbackFn );
   }
   else {
    onEndCallbackFn();
   }
  },
  // window sizes
  win = {width: window.innerWidth, height: window.innerHeight},
  // some helper vars to disallow scrolling
  lockScroll = false, xscroll, yscroll,
  scrollContainer = document.querySelector('.container'),
  // the main slider and its items
  sliderEl = document.querySelector('.slider'),
  items = [].slice.call(sliderEl.querySelectorAll('.slide')),
  // total number of items
  itemsTotal = items.length,
  // navigation controls/arrows
  navRightCtrl = sliderEl.querySelector('.button--nav-next'),
  navLeftCtrl = sliderEl.querySelector('.button--nav-prev'),
  zoomCtrl = sliderEl.querySelector('.button--zoom'),
  // the main content element
  contentEl = document.querySelector('.content'),
  // close content control
  closeContentCtrl = contentEl.querySelector('button.button--close'),
  // index of current item
  current = 0,
  // check if an item is "open"
  isOpen = false,
  isFirefox = typeof InstallTrigger !== 'undefined',
  // scale body when zooming into the items, if not Firefox (the performance in Firefox is not very good)
  bodyScale = isFirefox ? false : 3;

 // some helper functions:
 function scrollX() { return window.pageXOffset || docElem.scrollLeft; }
 function scrollY() { return window.pageYOffset || docElem.scrollTop; }
 // from http://www.sberry.me/articles/javascript-event-throttling-debouncing
 function throttle(fn, delay) {
  var allowSample = true;

  return function(e) {
   if (allowSample) {
    allowSample = false;
    setTimeout(function() { allowSample = true; }, delay);
    fn(e);
   }
  };
 }

 function init() {
  initEvents();
 }

 // event binding
 function initEvents() {
  // open items
  zoomCtrl.addEventListener('click', function() {
   openItem(items[current]);
  });

  // close content
  closeContentCtrl.addEventListener('click', closeContent);

  // navigation
  navRightCtrl.addEventListener('click', function() { navigate('right'); });
  navLeftCtrl.addEventListener('click', function() { navigate('left'); });

  // window resize
  window.addEventListener('resize', throttle(function(ev) {
   // reset window sizes
   win = {width: window.innerWidth, height: window.innerHeight};

   // reset transforms for the items (slider items)
   items.forEach(function(item, pos) {
    if( pos === current ) return;
    var el = item.querySelector('.slide__mover');
    dynamics.css(el, { translateX: el.offsetWidth });
   });
  }, 10));

  // keyboard navigation events
  document.addEventListener( 'keydown', function( ev ) {
   if( isOpen ) return; 
   var keyCode = ev.keyCode || ev.which;
   switch (keyCode) {
    case 37:
     navigate('left');
     break;
    case 39:
     navigate('right');
     break;
   }
  } );
 }

 // opens one item
 function openItem(item) {
  if( isOpen ) return;
  isOpen = true;

  // the element that will be transformed
  var zoomer = item.querySelector('.zoomer');
  // slide screen preview
  classie.add(zoomer, 'zoomer--active');
  // disallow scroll
  scrollContainer.addEventListener('scroll', noscroll);
  // apply transforms
  applyTransforms(zoomer);
  // also scale the body so it looks the camera moves to the item.
  if( bodyScale ) {
   dynamics.animate(bodyEl, { scale: bodyScale }, { type: dynamics.easeInOut, duration: 500 });
  }
  // after the transition is finished:
  onEndTransition(zoomer, function() {
   // reset body transform
   if( bodyScale ) {
    dynamics.stop(bodyEl);
    dynamics.css(bodyEl, { scale: 1 });
    
    // fix for safari (allowing fixed children to keep position)
    bodyEl.style.WebkitTransform = 'none';
    bodyEl.style.transform = 'none';
   }
   // no scrolling
   classie.add(bodyEl, 'noscroll');
   classie.add(contentEl, 'content--open');
   var contentItem = document.getElementById(item.getAttribute('data-content'))
   classie.add(contentItem, 'content__item--current');
   classie.add(contentItem, 'content__item--reset');


   // reset zoomer transform - back to its original position/transform without a transition
   classie.add(zoomer, 'zoomer--notrans');
   zoomer.style.WebkitTransform = 'translate3d(0,0,0) scale3d(1,1,1)';
   zoomer.style.transform = 'translate3d(0,0,0) scale3d(1,1,1)';
  });
 }

 // closes the item/content
 function closeContent() {
  var contentItem = contentEl.querySelector('.content__item--current'),
   zoomer = items[current].querySelector('.zoomer');

  classie.remove(contentEl, 'content--open');
  classie.remove(contentItem, 'content__item--current');
  classie.remove(bodyEl, 'noscroll');
    
  if( bodyScale ) {
   // reset fix for safari (allowing fixed children to keep position)
   bodyEl.style.WebkitTransform = '';
   bodyEl.style.transform = '';
  }

  /* fix for safari flickering */
  var nobodyscale = true;
  applyTransforms(zoomer, nobodyscale);
  /* fix for safari flickering */

  // wait for the inner content to finish the transition
  onEndTransition(contentItem, function(ev) {
   classie.remove(this, 'content__item--reset');
   
   // reset scrolling permission
   lockScroll = false;
   scrollContainer.removeEventListener('scroll', noscroll);

   /* fix for safari flickering */
   zoomer.style.WebkitTransform = 'translate3d(0,0,0) scale3d(1,1,1)';
   zoomer.style.transform = 'translate3d(0,0,0) scale3d(1,1,1)';
   /* fix for safari flickering */
   
   // scale up - behind the scenes - the item again (without transition)
   applyTransforms(zoomer);
   
   // animate/scale down the item
   setTimeout(function() { 
    classie.remove(zoomer, 'zoomer--notrans');
    classie.remove(zoomer, 'zoomer--active');
    zoomer.style.WebkitTransform = 'translate3d(0,0,0) scale3d(1,1,1)';
    zoomer.style.transform = 'translate3d(0,0,0) scale3d(1,1,1)';
   }, 25);

   if( bodyScale ) {
    dynamics.css(bodyEl, { scale: bodyScale });
    dynamics.animate(bodyEl, { scale: 1 }, {
     type: dynamics.easeInOut,
     duration: 500
    });
   }

   isOpen = false;
  });
 }

 // applies the necessary transform value to scale the item up
 function applyTransforms(el, nobodyscale) {
  // zoomer area and scale value
  var zoomerArea = el.querySelector('.zoomer__area'), 
   zoomerAreaSize = {width: zoomerArea.offsetWidth, height: zoomerArea.offsetHeight},
   zoomerOffset = zoomerArea.getBoundingClientRect(),
   scaleVal = zoomerAreaSize.width/zoomerAreaSize.height < win.width/win.height ? win.width/zoomerAreaSize.width : win.height/zoomerAreaSize.height;

  if( bodyScale && !nobodyscale ) {
   scaleVal /= bodyScale; 
  }

  // apply transform
  el.style.WebkitTransform = 'translate3d(' + Number(win.width/2 - (zoomerOffset.left+zoomerAreaSize.width/2)) + 'px,' + Number(win.height/2 - (zoomerOffset.top+zoomerAreaSize.height/2)) + 'px,0) scale3d(' + scaleVal + ',' + scaleVal + ',1)';
  el.style.transform = 'translate3d(' + Number(win.width/2 - (zoomerOffset.left+zoomerAreaSize.width/2)) + 'px,' + Number(win.height/2 - (zoomerOffset.top+zoomerAreaSize.height/2)) + 'px,0) scale3d(' + scaleVal + ',' + scaleVal + ',1)';
 }

 // navigate the slider
 function navigate(dir) {
  var itemCurrent = items[current],
   currentEl = itemCurrent.querySelector('.slide__mover'),
   currentTitleEl = itemCurrent.querySelector('.slide__title');

  // update new current value
  if( dir === 'right' ) {
   current = current < itemsTotal-1 ? current + 1 : 0;
  }
  else {
   current = current > 0 ? current - 1 : itemsTotal-1;
  }

  var itemNext = items[current],
   nextEl = itemNext.querySelector('.slide__mover'),
   nextTitleEl = itemNext.querySelector('.slide__title');
  
  // animate the current element out
  dynamics.animate(currentEl, { opacity: 0, translateX: dir === 'right' ? -1*currentEl.offsetWidth/2 : currentEl.offsetWidth/2, rotateZ: dir === 'right' ? -10 : 10 }, {
   type: dynamics.spring,
   duration: 2000,
   friction: 600,
   complete: function() {
    dynamics.css(itemCurrent, { opacity: 0, visibility: 'hidden' });
   }
  });

  // animate the current title out
  dynamics.animate(currentTitleEl, { translateX: dir === 'right' ? -250 : 250, opacity: 0 }, {
   type: dynamics.bezier,
   points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
   duration: 450
  });

  // set the right properties for the next element to come in
  dynamics.css(itemNext, { opacity: 1, visibility: 'visible' });
  dynamics.css(nextEl, { opacity: 0, translateX: dir === 'right' ? nextEl.offsetWidth/2 : -1*nextEl.offsetWidth/2, rotateZ: dir === 'right' ? 10 : -10 });

  // animate the next element in
  dynamics.animate(nextEl, { opacity: 1, translateX: 0 }, {
   type: dynamics.spring,
   duration: 2000,
   friction: 600,
   complete: function() {
    items.forEach(function(item) { classie.remove(item, 'slide--current'); });
    classie.add(itemNext, 'slide--current');
   }
  });

  // set the right properties for the next title to come in
  dynamics.css(nextTitleEl, { translateX: dir === 'right' ? 250 : -250, opacity: 0 });
  // animate the next title in
  dynamics.animate(nextTitleEl, { translateX: 0, opacity: 1 }, {
   type: dynamics.bezier,
   points: [{"x":0,"y":0,"cp":[{"x":0.2,"y":1}]},{"x":1,"y":1,"cp":[{"x":0.3,"y":1}]}],
   duration: 650
  });
 }

 // disallow scrolling (on the scrollContainer)
 function noscroll() {
  if(!lockScroll) {
   lockScroll = true;
   xscroll = scrollContainer.scrollLeft;
   yscroll = scrollContainer.scrollTop;
  }
  scrollContainer.scrollTop = yscroll;
  scrollContainer.scrollLeft = xscroll;
 }

 init();

})(window);

Find this project on Github








Content slider with Zoom Effect for a predefined area in each slide Reviewed by Sanaulllah Rais on 13:34 Rating: 5

No comments:

Contact Form

Name

Email *

Message *

Theme images by Jason Morrow. Powered by Blogger.