一款流畅的图片滑动焦点图特效

一款流畅的图片滑动特效

这是一款流畅的图片滑动特效,可以对当前图片预览放大说明,细节方面做得很到位,大家可以下载体验。

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?watermark/1/image/aHR0cDovL3d3dy56enl6YW4uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE2LzA3Lzk2ZDZmMmU3ZTFmNzA1YWI1ZTU5Yzg0YTZkYzAwOWIyLnBuZw==/dissolve/100/gravity/SouthEast/dx/10/dy/10" 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>
<script src="js/classie.js"></script>
<script src="js/dynamics.min.js"></script>
<script src="js/main.js"></script>

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;
    }
}

JavaScript

/**
 * main.js
 * 
 * Copyright 2016, 小乖乖
 * http://www.zzyzan.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);
打赏
下载 在线预览 积分 (-2) 吐槽 (1)
×
加载中,请稍候…

您需要 登录 后才能发表评论。

avatar

- 评论

  • 加载中,请稍候…
我要吐槽