【Animejs】——Anime.js照片墙案例实现

目录

一、实现的效果:

二、实现js的具体步骤

1、需要实现的逻辑梳理

2、切换风格的逻辑

三、完整代码:


用js编写使用animejs实现图片复杂的切换效果

一、实现的效果:

点击——> <——箭头,实现不同动画效果的炫酷切换 。

代码可以通过我下面分享的码云链接进行下载,或者结尾我会附上完整的代码:

animejs_1照片墙: 使用animejs实现照片墙的炫酷切换效果

我们主要来实现js部分的编写,所以建议大家去我的码云把源码下载下来,方便学习。 

二、实现js的具体步骤

创建如下的目录:

【Animejs】——Anime.js照片墙案例实现_第1张图片

目录组成部分: 

【Animejs】——Anime.js照片墙案例实现_第2张图片

 现在的状态是,画面在第一页上,点击两个箭头页面是不动的:

我们可以看到在index.html文件中,这7个风格都在里面,每一个风格我们用类名slide--layout-n中的数字来做区分:

如slide--layout-4代表第4种风格。

【Animejs】——Anime.js照片墙案例实现_第3张图片

 这些风格都在页面中显示,为什么我们看不到?是因为透明度改成0。

我们需要做的是给按钮添加点击事件,使之能够切换风格: 

【Animejs】——Anime.js照片墙案例实现_第4张图片

1、需要实现的逻辑梳理

点击箭头,会有两个效果:一个显示的图片出去,将要显示的图片进来的效果。

每次切换,标题也是不同的动画效果

【Animejs】——Anime.js照片墙案例实现_第5张图片

【Animejs】——Anime.js照片墙案例实现_第6张图片

总结我们需要完成的业务逻辑就是:

1、 我们切换风格的逻辑是通过改变每个风格的透明度来实现。

2、实现两个动画风格:图片的和标题的。——图片又分为进入和出去两个动画风格;标题每个风格都一样,我们写一个就可以了。

【Animejs】——Anime.js照片墙案例实现_第7张图片

2、切换风格的逻辑

展示页面的逻辑:通过添加'slide--current'类名来实现透明度变为1,展示在页面上。

1、所有代码都是自执行函数,先实现两个方法,方便之后使用。

; (function (window) {
    // 严格模式下
	'use strict';
	// extend:把某一个对象的属性复制给另一个对象,把b的属性给a
	function extend(a, b) {
		for (var key in b) {
			if (b.hasOwnProperty(key)) {
				a[key] = b[key];
			}
		}
		return a;
	}
    // 防抖:我们不希望当快速点击的时候我们就一直在执行。想要间隔一定的时间在去触发 
	function debounce(func, wait) {
		var timeout;
		return function () {
			var context = this, args = arguments;
			var later = function () {
				timeout = null;
				func.apply(context, args);
			};
			clearTimeout(timeout);
			timeout = setTimeout(later, wait);
		};
	};
})(window)

2、实现风格切换的方法: 

function MLSlideshow(el, options) {
		// 1. 元素属性
        // 拿到元素,就把元素挂载到对象下
		this.el = el;
		// 2. 相关参数
		this.options = extend({}, this.options);
		extend(this.options, options);
		// 3. 所有照片的风格
        // 最外层盒子取到所有风格的类名
		this.slides = [].slice.call(this.el.querySelectorAll('.slide'));

		// 判断:如果当前照片风格少于2,不创建实例————也就是只有1种风格
		this.slidesTotal = this.slides.length;
		if (this.slidesTotal <= 1) {
			return;
		}
		// 4. 当前展示的风格的下标
		this.current = this.options.startIdx || 0;
		// 5. 当前窗口大小————因为图片的宽高是由窗口大小决定的
		this.dimentions = {
			width: this.el.offsetWidth,
			height: this.el.offsetHeight
		}
		// 6. 照片风格初始化
        // 需要一个_init方法————下面会写
		this._init();
	}

解释: 

this.slides = [].slice.call(this.el.querySelectorAll('.slide'));

如下图index.html中知道每个风格的类名都叫 slide ,this.el.querySelectorAll('.slide')返回的是一个伪数组数据,所以我们需要转化为正常的数组来方便之后处理,通过slice

【Animejs】——Anime.js照片墙案例实现_第8张图片


3、风格初始化——_init

// 照片墙初始化
	MLSlideshow.prototype._init = function () {
		var self = this, // self保存一下当前的this
			// 1. 当图片加载完成,展示当前第一个风格————由current决定是第几个风格
			// onPreload:图片加载完成要做的事情
			onPreload = function () {
				// 给当前的图片添加一个类名
				self.el.classList.add('slideshow--loaded');
				// 让第一个风格展示
				self.slides[self.current].classList.add('slide--current');
			};
		// 在图片加载完成的时候调用
		this._preload(onPreload);
		// 2. 添加事件:
                // 2.1 窗口大小事件
                // 2.2 键盘触发切换风格事件
		// _initEvents独立出一个方法
		this._initEvents();

	}

解释:

 当图片加载完成之后,才会展示当前第一个风格,那么我们会用到一个插件:

【Animejs】——Anime.js照片墙案例实现_第9张图片

这个插件里面通提供了一个方法imagesLoaded,来判断我们的图片是否加载完成。

// 判断图片是否加载完成
	MLSlideshow.prototype._preload = function (callback) {
		// imagesLoaded直接调用,因为我们已经引入了这个包
		imagesLoaded(this.el, { background: true }, function () {
			// 如果传入的回调是一个函数,我们就去调用它
			if (typeof callback === 'function') {
				callback();
			}
		});
	};

4、添加事件——_initEvents:

MLSlideshow.prototype._initEvents = function () {
		var self = this;
		// 2.1 窗口大小事件
		// 用到防抖debounce
		this.debounceResize = debounce(function (ev) {
			self.dimentions = {
				width: self.el.offsetWidth,
				height: self.el.offsetHeight
			};
		}, 10)
		// 添加事件,当窗口的大小发送改变: 
		window.addEventListener('resize', this.debounceResize);
		// 2.2 键盘触发切换风格事件
		// 按下左右键的键盘按键,触发切换的按钮
		this.keyboardFn = function (ev) {
			var keyCode = ev.keyCode || ev.which;
			switch (keyCode) {
				case 37:
					// _navigate('prev'):往前切换————一会完成前后切换的函数实现
					self._navigate('prev');
					break;
				case 39:
					// _navigate('next'):往后切换
					self._navigate('next');
					break;
			}
		};
		this.el.addEventListener('keydown', this.keyboardFn);
	}

5、前后切换——_navigate

// _navigate:参数接收prev和next
	MLSlideshow.prototype._navigate = function (direction) {
		// isAnimating:判断当前动画是否在运动
		if (this.isAnimating) {
			return false;
		}
		this.isAnimating = true;
		// 1. 获取当前展示风格的信息: 当前元素,唯一标识,相关标题
		var self = this,
			// 知道当前展示的下标是什么,当点击前后切换的时候,改变下标的值来完成
			currentSlide = this.slides[this.current],
			// 获取当前风格标识
			currentLayout = currentSlide.getAttribute('data-layout') || 'layout1',
			// 获取当前展示风格的标题
			currentTitle = currentSlide.querySelector('.slide__title');

		// 2. 获取下一个要展示风格的信息: 当前元素,唯一标识,相关标题
		if (direction === 'next') {
			this.current = this.current < this.slidesTotal - 1 ? this.current + 1 : 0;
		}
		else {
			this.current = this.current > 0 ? this.current - 1 : this.slidesTotal - 1;
		}
		// 获取元素信息
		var nextSlide = this.slides[this.current],
			nextLayout = nextSlide.getAttribute('data-layout'),
			nextTitle = nextSlide.querySelector('.slide__title');
        // 3. 针对要出去的元素的动画
		// currentSlide.querySelectorAll('.slide-imgwrap .slide__img-inner'):动画中要出去的所有的图片
		// currentSlide:当前要出去的元素
		var outItems = [].slice.call(currentSlide.querySelectorAll('.slide-imgwrap .slide__img-inner')),
			// 获取相关配置参数
			outconfig = this.options.layoutConfig[currentLayout] !== undefined ? this.options.layoutConfig[currentLayout].out : this.options.layoutConfig['layout1'].out,
			animeOutProps = {
				targets: outItems,
				duration: outconfig.duration,
				easing: outconfig.easing,
				// 延迟是一个方法
				delay: function (el, index) {
					return direction === 'next' ? index * outconfig.itemsDelay : (outItems.length - 1 - index) * outconfig.itemsDelay;
				},
				// 走的这个元素之前是添加了一个类名,现在走了要把类名拿掉
				complete: function () {
					currentSlide.classList.remove('slide--current');
				}
			};
		this._setAnimationProperties(animeOutProps, outconfig, direction)
		// 参数传入,直接进行动画
		anime(animeOutProps);
		// 标题
		this._anmateTitle(currentTitle, 'out');
		// 创建定时器:当前风格出去了,再让下一个风格进来
		clearTimeout(this.navtime);
		// animateIn:让动画进来  
		this.navtime = setTimeout(animateIn, this.options.layoutConfig[nextLayout] !== undefined && this.options.layoutConfig[nextLayout].in.delay !== undefined ? this.options.layoutConfig[nextLayout].in.delay : 150);
	}

针对要显示的风格创建方法:

// _navigate:参数接收prev和next
	MLSlideshow.prototype._navigate = function (direction) {
		// isAnimating:判断当前动画是否在运动
		if (this.isAnimating) {
			return false;
		}
		this.isAnimating = true;
		// 1. 获取当前展示风格的信息: 当前元素,唯一标识,相关标题
		var self = this,
			// 知道当前展示的下标是什么也就是要出去的元素,当点击前后切换的时候,改变下标的值来完成
			currentSlide = this.slides[this.current],
			// 获取当前风格标识
			currentLayout = currentSlide.getAttribute('data-layout') || 'layout1',
			// 获取当前展示风格的标题
			currentTitle = currentSlide.querySelector('.slide__title');

		// 2. 获取下一个要展示风格的信息: 当前元素,唯一标识,相关标题
		if (direction === 'next') {
			this.current = this.current < this.slidesTotal - 1 ? this.current + 1 : 0;
		}
		else {
			this.current = this.current > 0 ? this.current - 1 : this.slidesTotal - 1;
		}
		// 获取元素信息
		var nextSlide = this.slides[this.current],
			nextLayout = nextSlide.getAttribute('data-layout'),
			nextTitle = nextSlide.querySelector('.slide__title');
		// 4. 针对要显示的风格创建方法
		var animateIn = function () {

			// 开启动画之前把其他动画停掉
			clearTimeout(self.navtime);

			// 获取计算的要进入的风格的照片元素
			var inItems = [].slice.call(nextSlide.querySelectorAll('.slide-imgwrap .slide__img-inner')),
				// 设置计算的下一个的下标元素动画,如果不存在就设置第一个元素动画,标识区分
				// 获取即将进入的风格的动画参数
				inconfig = self.options.layoutConfig[nextLayout] !== undefined ? self.options.layoutConfig[nextLayout].in : self.options.layoutConfig['layout1'].in,
				// 进入时,初始化设置赋值给inresetconfig
				inresetconfig = inconfig.resetProps,
				// 设置动画参数
				animeInProps = {
					targets: inItems,
					duration: inconfig.duration,
					easing: inconfig.easing,
					delay: function (el, index) {
						return direction === 'next' ? index * inconfig.itemsDelay : (inItems.length - 1 - index) * inconfig.itemsDelay;
					},
					// 动画完成设置动画运动标识是false
					complete: function () {
						self.isAnimating = false;
					}
				};

			// Configure the animation in properties.
			// 将动画参数,当前动画元素相关信息,前后传入设置动画属性的方法中
			self._setAnimationProperties(animeInProps, inconfig, direction);
			// Reset before animating in:
			// 在动画之前重置即将进入的风格的图片
			// 需要重置的原因:因为图片的宽高是相对窗口的 ,但是图片的宽高又是根据浏览器窗口的,所以每一次我们都要重置一下图片的宽高,避免窗口发生改变之后,盒子小了图片还很大这种不好的效果
			inItems.forEach(function (item, pos) {
				var transformStr = '';
				// 将属性中的translateX、translateY、rotateZ、scale、opacity进行重置
				if (inresetconfig.translateX !== undefined) {
					var tx = typeof inresetconfig.translateX === 'object' ?
						function () {
							return typeof inresetconfig.translateX[direction] === 'function' ?
								self._getValuePercentage(inresetconfig.translateX[direction](item, pos), 'width') :
								self._getValuePercentage(inresetconfig.translateX[direction], 'width');
						} : self._getValuePercentage(inresetconfig.translateX, 'width');

					transformStr += ' translateX(' + (typeof tx === 'function' ? tx() : tx) + 'px)';
				}
				if (inresetconfig.translateY !== undefined) {
					var ty = typeof inresetconfig.translateY === 'object' ? function () {
						return typeof inresetconfig.translateY[direction] === 'function' ? self._getValuePercentage(inresetconfig.translateY[direction](item, pos), 'height') : self._getValuePercentage(inresetconfig.translateY[direction], 'height');
					} : self._getValuePercentage(inresetconfig.translateY, 'height');
					transformStr += ' translateY(' + (typeof ty === 'function' ? ty() : ty) + 'px)';
				}
				if (inresetconfig.rotateZ !== undefined) {
					var rot = typeof inresetconfig.rotateZ === 'object' ? function () {
						return typeof inresetconfig.rotateZ[direction] === 'function' ? inresetconfig.rotateZ[direction](item, pos) : inresetconfig.rotateZ[direction];
					} : inresetconfig.rotateZ;

					transformStr += ' rotateZ(' + (typeof rot === 'function' ? rot() : rot) + 'deg)';
				}
				if (inresetconfig.scale !== undefined) {
					var s = typeof inresetconfig.scale === 'object' ? function () {
						return typeof inresetconfig.scale[direction] === 'function' ? inresetconfig.scale[direction](item, pos) : inresetconfig.scale[direction];
					} : inresetconfig.scale;

					transformStr += ' scale(' + (typeof s === 'function' ? s() : s) + ')';
				}
				if (transformStr !== '') {
					item.style.transform = item.style.WebkitTransform = transformStr;
				}
				if (inresetconfig.opacity !== undefined) {
					item.style.opacity = inresetconfig.opacity;
				}
			});
			// 设置即将进入的风格的title是透明的
			// Reset next title.
			nextTitle.style.opacity = 0;
			// Switch current class.
			// 设置即将进入的元素类名是当前风格的
			nextSlide.classList.add('slide--current');
			// Animate next slide in.
			// 动画让其进入
			anime(animeInProps);
			// Animate next title in.
			// 让标题进入
			self._animateTitle(nextTitle, 'in');
		};

		// 3. 针对要出去的元素的动画
		// currentSlide.querySelectorAll('.slide-imgwrap .slide__img-inner'):动画中要出去的所有的图片
		// currentSlide:当前要出去的元素
		var outItems = [].slice.call(currentSlide.querySelectorAll('.slide-imgwrap .slide__img-inner')),
			// 获取相关配置参数
			outconfig = this.options.layoutConfig[currentLayout] !== undefined ? this.options.layoutConfig[currentLayout].out : this.options.layoutConfig['layout1'].out,
			animeOutProps = {
				targets: outItems,
				duration: outconfig.duration,
				easing: outconfig.easing,
				// 延迟是一个方法
				delay: function (el, index) {
					return direction === 'next' ? index * outconfig.itemsDelay : (outItems.length - 1 - index) * outconfig.itemsDelay;
				},
				// 走的这个元素之前是添加了一个类名,现在走了要把类名拿掉
				complete: function () {
					currentSlide.classList.remove('slide--current');
				}
			};
		this._setAnimationProperties(animeOutProps, outconfig, direction)
		// 参数传入,直接进行动画
		anime(animeOutProps);
		// 标题
		this._anmateTitle(currentTitle, 'out');
		// 创建定时器:当前风格出去了,再让下一个风格进来
		clearTimeout(this.navtime);
		// animateIn:让动画进来  
		this.navtime = setTimeout(animateIn, this.options.layoutConfig[nextLayout] !== undefined && this.options.layoutConfig[nextLayout].in.delay !== undefined ? this.options.layoutConfig[nextLayout].in.delay : 150);
	}

6、图片进入和出去相关的动画参数——options

这里有7个动画效果,我们只分析一个,其他7个是完全一样的,详细看源码。

	// 图片进入和出去相关的动画参数
	// 设计风格动画参数
	MLSlideshow.prototype.options = {
		// 起始的下标设置成0
		startIdx: 0,
		// layoutConfig:7个风格会放在一个对象下 
		layoutConfig: {
			layout1: {
				// 退出时
				out: {
					// 点击next还是点击prev,方向是不一样的
					translateX: {
						next: '-100%',
						prev: '100%'
					},
					rotateZ: {
						next: function (el, index) {
							return anime.random(-15, 0);
						},
						prev: function (el, index) {
							return anime.random(0, 15);
						}
					},
					opacity: 0,
					duration: 1200,
					easing: 'easeOutQuint',
					itemsDelay: 80
				},
				// 进入时
				in: {
					// resetProps:在进入时需要重置一下动画的参数
					resetProps: {
						translateX: {
							next: '100%',
							prev: '-100%'
						},
						rotateZ: {
							next: function (el, index) {
								return anime.random(0, 15);
							},
							prev: function (el, index) {
								return anime.random(-15, 0);
							}
						},
						opacity: 0,
					},
					translateX: '0%',
					rotateZ: 0,
					opacity: 1,
					duration: 700,
					easing: 'easeOutQuint',
					itemsDelay: 80
				}
			},
            layout12: {
            }
            ...
        }
    }

7、进入和出去两个动画之前都是处理一下参数,把config的参数传递给当前动画的参数,思路和初始化的思路是一样的。

// 处理参数
	MLSlideshow.prototype._setAnimationProperties = function (props, config, direction) {
		var self = this;
		if (config.translateX !== undefined) {
			props.translateX = typeof config.translateX === 'object' ?
				function (el, index) {
					return typeof config.translateX[direction] === 'function' ?
						self._getValuePercentage(config.translateX[direction](el, index), 'width')
						: self._getValuePercentage(config.translateX[direction], 'width');
				} : this._getValuePercentage(config.translateX, 'width');
		}
		if (config.translateY !== undefined) {
			props.translateY = typeof config.translateY === 'object' ? function (el, index) {
				return typeof config.translateY[direction] === 'function' ? self._getValuePercentage(config.translateY[direction](el, index), 'width') : self._getValuePercentage(config.translateY[direction], 'height');
			} : this._getValuePercentage(config.translateY, 'height');
		}
		if (config.rotateZ !== undefined) {
			props.rotateZ = typeof config.rotateZ === 'object' ? function (el, index) {
				return typeof config.rotateZ[direction] === 'function' ? config.rotateZ[direction](el, index) : config.rotateZ[direction];
			} : config.rotateZ;
		}
		if (config.scale !== undefined) {
			props.scale = typeof config.scale === 'object' ? function (el, index) {
				return typeof config.scale[direction] === 'function' ? config.scale[direction](el, index) : config.scale[direction];
			} : config.scale;
		}
		if (config.opacity !== undefined) {
			props.opacity = config.opacity;
		}
	};
    // _getValuePercentage :获取百分比,改为值
	MLSlideshow.prototype._getValuePercentage = function (str, axis) {
		return typeof str === 'string' && str.indexOf('%') !== -1 ? parseFloat(str) / 100 * this.dimentions[axis] : str;
	}

8、设置title动画

// 设置title动画
	MLSlideshow.prototype._anmateTitle = function (titleEl, dir) {
		anime({
			targets: titleEl,
			opacity: dir === 'out' ? 0 : 1,
			duration: dir === 'out' ? 200 : 500,
			easing: 'easeOutExpo'
		})
	}
	MLSlideshow.prototype.next = function () {
		this._navigate('next');
	}
	MLSlideshow.prototype.prev = function () {
		this._navigate('prev');
	}
	window.MLSlideshow = MLSlideshow;

代码就完成了,我们去index.html调用我们写的方法:





三、完整代码:

// ▷main.js

// 所有代码都是自执行函数
; (function (window) {
	// 严格模式下
	'use strict';
	// extend:把某一个对象的属性复制给另一个对象,把b的属性给a
	function extend(a, b) {
		for (var key in b) {
			if (b.hasOwnProperty(key)) {
				a[key] = b[key];
			}
		}
		return a;
	}
	// 防抖:我们不希望当快速点击的时候我们就一直在执行。想要间隔一定的时间在去触发 
	function debounce(func, wait) {
		var timeout;
		return function () {
			var context = this, args = arguments;
			var later = function () {
				timeout = null;
				func.apply(context, args);
			};
			clearTimeout(timeout);
			timeout = setTimeout(later, wait);
		};
	};

	function MLSlideshow(el, options) {
		// 1. 元素属性
		// 拿到元素,就把元素挂载到对象下
		this.el = el;
		// 2. 相关参数
		this.options = extend({}, this.options);
		extend(this.options, options);
		// 3. 所有照片的风格
		// 最外层盒子取到所有风格的类名
		this.slides = [].slice.call(this.el.querySelectorAll('.slide'));

		// 判断:如果当前照片风格少于2,不创建实例
		this.slidesTotal = this.slides.length;
		if (this.slidesTotal <= 1) {
			return;
		}
		// 4. 当前展示的风格的下标
		this.current = this.options.startIdx || 0;
		// 5. 当前窗口
		this.dimentions = {
			width: this.el.offsetWidth,
			height: this.el.offsetHeight
		}
		// 6. 照片风格初始化
		this._init();
	}

	// 照片墙初始化
	MLSlideshow.prototype._init = function () {
		var self = this, // self保存一下当前的this
			// 1. 当图片加载完成,展示当前第一个风格————由current决定是第几个风格
			// onPreload:图片加载完成要做的事情
			onPreload = function () {
				// 给当前的图片添加一个类名
				self.el.classList.add('slideshow--loaded');
				// 让第一个风格展示
				self.slides[self.current].classList.add('slide--current');
			};
		// 在图片加载完成的时候调用
		this._preload(onPreload);
		// 2. 添加事件:
		// _initEvents独立出一个方法
		this._initEvents();

	}

	// 判断图片是否加载完成
	MLSlideshow.prototype._preload = function (callback) {
		// imagesLoaded直接调用,因为我们已经引入了这个包
		imagesLoaded(this.el, { background: true }, function () {
			// 如果传入的回调是一个函数,我们就去调用它
			if (typeof callback === 'function') {
				callback();
			}
		});
	};

	MLSlideshow.prototype._initEvents = function () {
		var self = this;
		// 2.1 窗口大小事件
		// 用到防抖debounce
		this.debounceResize = debounce(function (ev) {
			self.dimentions = {
				width: self.el.offsetWidth,
				height: self.el.offsetHeight
			};
		}, 10)
		// 添加事件,当窗口的大小发送改变: 
		window.addEventListener('resize', this.debounceResize);
		// 2.2 键盘触发切换风格事件
		// 按下左右键的键盘按键,触发切换的按钮
		this.keyboardFn = function (ev) {
			var keyCode = ev.keyCode || ev.which;
			switch (keyCode) {
				case 37:
					// _navigate('prev'):往前切换
					self._navigate('prev');
					break;
				case 39:
					// _navigate('next'):往后切换
					self._navigate('next');
					break;
			}
		};
		this.el.addEventListener('keydown', this.keyboardFn);
	}
	
	// 图片进入和出去相关的动画参数
	// 设计风格动画参数
	MLSlideshow.prototype.options = {
		// 起始的下标设置成0
		startIdx: 0,
		// layoutConfig:7个风格会放在一个对象下 
		layoutConfig: {
			layout1: {
				// 退出时
				out: {
					// 点击next还是点击prev,方向是不一样的
					translateX: {
						next: '-100%',
						prev: '100%'
					},
					rotateZ: {
						next: function (el, index) {
							return anime.random(-15, 0);
						},
						prev: function (el, index) {
							return anime.random(0, 15);
						}
					},
					opacity: 0,
					duration: 1200,
					easing: 'easeOutQuint',
					itemsDelay: 80
				},
				// 进入时
				in: {
					// resetProps:在进入时需要重置一下动画的参数
					resetProps: {
						translateX: {
							next: '100%',
							prev: '-100%'
						},
						rotateZ: {
							next: function (el, index) {
								return anime.random(0, 15);
							},
							prev: function (el, index) {
								return anime.random(-15, 0);
							}
						},
						opacity: 0,
					},
					translateX: '0%',
					rotateZ: 0,
					opacity: 1,
					duration: 700,
					easing: 'easeOutQuint',
					itemsDelay: 80
				}
			},
			layout2: {
				out: {
					translateX: {
						next: function (el, index) {
							return anime.random(-50, 50) + '%';
						},
						prev: function (el, index) {
							return anime.random(-50, 50) + '%';
						}
					},
					translateY: {
						next: function (el, index) {
							return anime.random(-50, 50) + '%';
						},
						prev: function (el, index) {
							return anime.random(-50, 50) + '%';
						}
					},
					opacity: 0,
					duration: 1200,
					easing: 'easeOutQuint',
					itemsDelay: 10
				},
				in: {
					resetProps: {
						translateX: {
							next: '100%',
							prev: '-100%'
						},
						rotateZ: {
							next: function (el, index) {
								return anime.random(0, 90);
							},
							prev: function (el, index) {
								return anime.random(-90, 0);
							}
						},
						opacity: 0,
					},
					translateX: '0%',
					rotateZ: 0,
					opacity: 1,
					duration: 900,
					easing: 'easeOutExpo',
					itemsDelay: 30
				}
			},
			layout3: {
				out: {
					translateX: '-10%',
					rotateZ: 0,
					opacity: 0,
					duration: 500,
					easing: 'easeOutExpo',
					itemsDelay: 0
				},
				in: {
					resetProps: {
						translateX: '-10%',
						rotateZ: 0,
						opacity: 0
					},
					translateX: 0,
					opacity: 1,
					rotateZ: {
						next: function (el, index) {
							return index * 6;
						},
						prev: function (el, index) {
							return index * 6;
						}
					},
					duration: 1200,
					easing: 'easeOutElastic',
					itemsDelay: 0
				}
			},
			layout4: {
				out: {
					translateY: {
						next: '60%',
						prev: '-60%'
					},
					opacity: 0,
					duration: 700,
					easing: 'easeOutQuint',
					itemsDelay: 50
				},
				in: {
					resetProps: {
						translateY: {
							next: '-60%',
							prev: '60%'
						},
						opacity: 0,
					},
					translateY: '0%',
					opacity: 1,
					duration: 700,
					easing: 'easeOutQuint',
					itemsDelay: 50,
					delay: 250
				}
			},
			layout5: {
				out: {
					scale: 0.5,
					opacity: 0,
					duration: 500,
					easing: 'easeOutExpo',
					itemsDelay: 20
				},
				in: {
					resetProps: {
						scale: 0.5,
						opacity: 0
					},
					opacity: 1,
					scale: 1,
					duration: 500,
					easing: 'easeOutExpo',
					itemsDelay: 20,
					delay: 300
				}
			},
			layout6: {
				out: {
					scale: 0.5,
					opacity: 0,
					duration: 300,
					easing: 'easeInBack',
					itemsDelay: 20
				},
				in: {
					resetProps: {
						scale: 0.5,
						opacity: 0
					},
					opacity: 1,
					scale: 1,
					duration: 1000,
					easing: 'easeOutElastic',
					itemsDelay: 50,
					delay: 400
				}
			},
			layout7: {
				out: {
					translateX: {
						next: '-100%',
						prev: '100%'
					},
					opacity: 0,
					duration: 1200,
					easing: 'easeOutQuint',
					itemsDelay: 40
				},
				in: {
					resetProps: {
						translateX: {
							next: '100%',
							prev: '-100%'
						},
						rotateZ: {
							next: function (el, index) {
								return anime.random(0, 25);
							},
							prev: function (el, index) {
								return anime.random(-25, 0);
							}
						},
						opacity: 0,
					},
					translateX: '0%',
					rotateZ: 0,
					opacity: 1,
					duration: 700,
					easing: 'easeOutQuint',
					itemsDelay: 40,
					delay: 250
				}
			}
		}
	};

	// _navigate:参数接收prev和next
	MLSlideshow.prototype._navigate = function (direction) {
		// isAnimating:判断当前动画是否在运动
		if (this.isAnimating) {
			return false;
		}
		this.isAnimating = true;
		// 1. 获取当前展示风格的信息: 当前元素,唯一标识,相关标题
		var self = this,
			// 知道当前展示的下标是什么也就是要出去的元素,当点击前后切换的时候,改变下标的值来完成
			currentSlide = this.slides[this.current],
			// 获取当前风格标识
			currentLayout = currentSlide.getAttribute('data-layout') || 'layout1',
			// 获取当前展示风格的标题
			currentTitle = currentSlide.querySelector('.slide__title');

		// 2. 获取下一个要展示风格的信息: 当前元素,唯一标识,相关标题
		if (direction === 'next') {
			this.current = this.current < this.slidesTotal - 1 ? this.current + 1 : 0;
		}
		else {
			this.current = this.current > 0 ? this.current - 1 : this.slidesTotal - 1;
		}
		// 获取元素信息
		var nextSlide = this.slides[this.current],
			nextLayout = nextSlide.getAttribute('data-layout'),
			nextTitle = nextSlide.querySelector('.slide__title');
		// 4. 针对要显示的风格创建方法
		var animateIn = function () {

			// 开启动画之前把其他动画停掉
			clearTimeout(self.navtime);

			// 获取计算的要进入的风格的照片元素
			var inItems = [].slice.call(nextSlide.querySelectorAll('.slide-imgwrap .slide__img-inner')),
				// 设置计算的下一个的下标元素动画,如果不存在就设置第一个元素动画,标识区分
				// 获取即将进入的风格的动画参数
				inconfig = self.options.layoutConfig[nextLayout] !== undefined ? self.options.layoutConfig[nextLayout].in : self.options.layoutConfig['layout1'].in,
				// 进入时,初始化设置赋值给inresetconfig
				inresetconfig = inconfig.resetProps,
				// 设置动画参数
				animeInProps = {
					targets: inItems,
					duration: inconfig.duration,
					easing: inconfig.easing,
					delay: function (el, index) {
						return direction === 'next' ? index * inconfig.itemsDelay : (inItems.length - 1 - index) * inconfig.itemsDelay;
					},
					// 动画完成设置动画运动标识是false
					complete: function () {
						self.isAnimating = false;
					}
				};

			// Configure the animation in properties.
			// 将动画参数,当前动画元素相关信息,前后传入设置动画属性的方法中
			self._setAnimationProperties(animeInProps, inconfig, direction);
			// Reset before animating in:
			// 在动画之前重置即将进入的风格的图片
			// 需要重置的原因:因为图片的宽高是相对窗口的 ,但是图片的宽高又是根据浏览器窗口的,所以每一次我们都要重置一下图片的宽高,避免窗口发生改变之后,盒子小了图片还很大这种不好的效果
			inItems.forEach(function (item, pos) {
				var transformStr = '';
				// 将属性中的translateX、translateY、rotateZ、scale、opacity进行重置
				if (inresetconfig.translateX !== undefined) {
					var tx = typeof inresetconfig.translateX === 'object' ?
						function () {
							return typeof inresetconfig.translateX[direction] === 'function' ?
								self._getValuePercentage(inresetconfig.translateX[direction](item, pos), 'width') :
								self._getValuePercentage(inresetconfig.translateX[direction], 'width');
						} : self._getValuePercentage(inresetconfig.translateX, 'width');

					transformStr += ' translateX(' + (typeof tx === 'function' ? tx() : tx) + 'px)';
				}
				if (inresetconfig.translateY !== undefined) {
					var ty = typeof inresetconfig.translateY === 'object' ? function () {
						return typeof inresetconfig.translateY[direction] === 'function' ? self._getValuePercentage(inresetconfig.translateY[direction](item, pos), 'height') : self._getValuePercentage(inresetconfig.translateY[direction], 'height');
					} : self._getValuePercentage(inresetconfig.translateY, 'height');
					transformStr += ' translateY(' + (typeof ty === 'function' ? ty() : ty) + 'px)';
				}
				if (inresetconfig.rotateZ !== undefined) {
					var rot = typeof inresetconfig.rotateZ === 'object' ? function () {
						return typeof inresetconfig.rotateZ[direction] === 'function' ? inresetconfig.rotateZ[direction](item, pos) : inresetconfig.rotateZ[direction];
					} : inresetconfig.rotateZ;

					transformStr += ' rotateZ(' + (typeof rot === 'function' ? rot() : rot) + 'deg)';
				}
				if (inresetconfig.scale !== undefined) {
					var s = typeof inresetconfig.scale === 'object' ? function () {
						return typeof inresetconfig.scale[direction] === 'function' ? inresetconfig.scale[direction](item, pos) : inresetconfig.scale[direction];
					} : inresetconfig.scale;

					transformStr += ' scale(' + (typeof s === 'function' ? s() : s) + ')';
				}
				if (transformStr !== '') {
					item.style.transform = item.style.WebkitTransform = transformStr;
				}
				if (inresetconfig.opacity !== undefined) {
					item.style.opacity = inresetconfig.opacity;
				}
			});
			// 设置即将进入的风格的title是透明的
			// Reset next title.
			nextTitle.style.opacity = 0;
			// Switch current class.
			// 设置即将进入的元素类名是当前风格的
			nextSlide.classList.add('slide--current');
			// Animate next slide in.
			// 动画让其进入
			anime(animeInProps);
			// Animate next title in.
			// 让标题进入
			self._anmateTitle(nextTitle, 'in');
		};

		// 3. 针对要出去的元素的动画
		// currentSlide.querySelectorAll('.slide-imgwrap .slide__img-inner'):动画中要出去的所有的图片
		// currentSlide:当前要出去的元素
		var outItems = [].slice.call(currentSlide.querySelectorAll('.slide-imgwrap .slide__img-inner')),
			// 获取相关配置参数
			outconfig = this.options.layoutConfig[currentLayout] !== undefined ? this.options.layoutConfig[currentLayout].out : this.options.layoutConfig['layout1'].out,
			animeOutProps = {
				targets: outItems,
				duration: outconfig.duration,
				easing: outconfig.easing,
				// 延迟是一个方法
				delay: function (el, index) {
					return direction === 'next' ? index * outconfig.itemsDelay : (outItems.length - 1 - index) * outconfig.itemsDelay;
				},
				// 走的这个元素之前是添加了一个类名,现在走了要把类名拿掉
				complete: function () {
					currentSlide.classList.remove('slide--current');
				}
			};
		this._setAnimationProperties(animeOutProps, outconfig, direction)
		// 参数传入,直接进行动画
		anime(animeOutProps);
		// 标题
		this._anmateTitle(currentTitle, 'out');
		// 创建定时器:当前风格出去了,再让下一个风格进来
		clearTimeout(this.navtime);
		// animateIn:让动画进来  
		this.navtime = setTimeout(animateIn, this.options.layoutConfig[nextLayout] !== undefined && this.options.layoutConfig[nextLayout].in.delay !== undefined ? this.options.layoutConfig[nextLayout].in.delay : 150);
	}

	// 处理参数
	MLSlideshow.prototype._setAnimationProperties = function (props, config, direction) {
		var self = this;
		if (config.translateX !== undefined) {
			props.translateX = typeof config.translateX === 'object' ?
				function (el, index) {
					return typeof config.translateX[direction] === 'function' ?
						self._getValuePercentage(config.translateX[direction](el, index), 'width')
						: self._getValuePercentage(config.translateX[direction], 'width');
				} : this._getValuePercentage(config.translateX, 'width');
		}
		if (config.translateY !== undefined) {
			props.translateY = typeof config.translateY === 'object' ? function (el, index) {
				return typeof config.translateY[direction] === 'function' ? self._getValuePercentage(config.translateY[direction](el, index), 'width') : self._getValuePercentage(config.translateY[direction], 'height');
			} : this._getValuePercentage(config.translateY, 'height');
		}
		if (config.rotateZ !== undefined) {
			props.rotateZ = typeof config.rotateZ === 'object' ? function (el, index) {
				return typeof config.rotateZ[direction] === 'function' ? config.rotateZ[direction](el, index) : config.rotateZ[direction];
			} : config.rotateZ;
		}
		if (config.scale !== undefined) {
			props.scale = typeof config.scale === 'object' ? function (el, index) {
				return typeof config.scale[direction] === 'function' ? config.scale[direction](el, index) : config.scale[direction];
			} : config.scale;
		}
		if (config.opacity !== undefined) {
			props.opacity = config.opacity;
		}
	};

	
	MLSlideshow.prototype._getValuePercentage = function (str, axis) {
		return typeof str === 'string' && str.indexOf('%') !== -1 ? parseFloat(str) / 100 * this.dimentions[axis] : str;
	}

	// 设置title动画
	MLSlideshow.prototype._anmateTitle = function (titleEl, dir) {
		anime({
			targets: titleEl,
			opacity: dir === 'out' ? 0 : 1,
			duration: dir === 'out' ? 200 : 500,
			easing: 'easeOutExpo'
		})
	}
	MLSlideshow.prototype.next = function () {
		this._navigate('next');
	}
	MLSlideshow.prototype.prev = function () {
		this._navigate('prev');
	}
	window.MLSlideshow = MLSlideshow;
})(window)
// ▷index.html





    
    
    
    照片墙
    
    
    
    
    
    



    
    

Now or Never

Our battered suitcases were piled on the sidewalk again; we had longer ways to go. But no matter, the road is life. Read more

Today is someday

Crazy Breed

There's those thinking more or less less is more. But if less is more how you're keeping score?

Safe Harbor

Twenty years from now you will be more disappointed by the things you didn’t do than by the ones you did do.

Our Freedom

For to be free is not merely to cast off one's chains, but to live in a way that respects and enhances the freedom of others.

Stopping Time

Emancipate yourselves from mental slavery, none but ourselves can free our minds.

Walk the Walk

The trouble with being in the rat race is that even if you win, you're still a rat.

Caged Birds

They told me to grow roots, instead I grew wings. Birds born in a cage think flying is an illness.

你可能感兴趣的:(前端,html,前端,javascript,动画)