JavaScript原生——实现一个轮播图

轮播图(css代码):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        *
        {
            margin: 0;
            padding: 0;
        }
        /*
         做外层容器,主要用来遮罩里面内容,让里面的部分溢出隐藏
         大小等同于第一张图片显示大小
         因为里面的内容要做定位移动,因此该容器必须做相对定位
        */
        #maskDiv
        {
            width: 960px;
            height: 320px;
            overflow: hidden;
            position: relative;
        }
        /*
        left和right是轮播图中的左右两个按钮,因为他们后面也需要做动画效果,
        因此我们也加了绝对定位,这里我们直接使用设置它的位置,
        实际我们需要在图片加载完成后计算它的位置

        */
        #left
        {
            position: absolute;
            left: 10px;
            top:130px;
        }
        #right
        {
            position: absolute;
            right: 10px;
            top:130px;
        }
        /*
           小圆点容器,小圆点外层容器需要定位,并且距离底端20像素,无列表样式
        */
        #circleCon
        {
            position: absolute;
            bottom: 20px;
            list-style: none;
        }
        /*
         小圆点样式
         宽高都是20像素
         边框线是红色
         文字居中对齐
         左浮动,让小圆点横向排列
         每个小圆点之间的间距为10个像素
         小圆点做圆形剪裁
        */
        .circle
        {
            width: 20px;
            height: 20px;
            border: 1px solid #FF0000;
            text-align: center;
            line-height: 20px;
            float: left;
            margin-left: 10px;
            border-radius: 20px;
        }
        /*
         设置图片容器中的图片宽高是960和320像素

        */
        #imgCon img
        {
            width: 960px;
            height: 320px;
        }
        /*
        设置图片容器的font-size是0,可以达到让所有图片无缝连接一起
        并且定位

        */
        #imgCon
        {
            font-size: 0;
            position: absolute;

        }
    </style>
    <!--导入Tween缓动类-->
    <script src="js/Tween.js"></script>
</head>
<body>
<!--
   最外层容器是maskDiv,里面包含
   1、imgCon用来放置图片的容器
   2、circleCon用来放置小圆点的容器
   3、left和right是两个左右切换图片的按钮

-->
    <div id="maskDiv">
        <div id="imgCon"></div>
        <ul id="circleCon">
        </ul>
        <img id="left" src="image/left.png">
        <img id="right" src="image/right.png">
    </div>
<script>
    /*
    * 轮播图的功能:
    * 1、点击左右按钮可以切换图片,并且达到不会出现连续切换
    * 2、点击小圆点可以切换图片
    * 3、可以自动轮播图片,当鼠标离开时
    * 4、当鼠标在大容器上时,不能自动轮播,并且左右两个按钮缓动进入,离开大容器时,左右两个按钮缓动离开
    * 5、小圆点变化时,缓动效果变化透明度,逐渐消失
    * */
//    约束轮播图的宽度是960像素
    const ROLL_WIDTH=960;
//    约束自动轮播间隔时长是120帧
    const MAX_TIME=120;
    /*
    *获取界面中元素
    * 1、maskDiv最外层大容器,用来溢出隐藏和侦听鼠标经过事件
    * 2、imgCon图片容器,用来移动图片的位置
    * 3、circleCon小圆点容器,用来固定所有小圆点的位置
    * 4、leftBn和rightBn,左右两个按钮
    *
    * */
    var maskDiv=document.getElementById("maskDiv");
    var imgCon=document.getElementById("imgCon");
    var circleCon=document.getElementById("circleCon");
    var leftBn=document.getElementById("left");
    var rightBn=document.getElementById("right");
//    图片地址的数组
    var pathArr=["image/7.jpg","image/8.jpg","image/3.jpg","image/6.jpg","image/5.jpg"];
//   图片被预加载完成后将存储在这个数组中
    var imgArr=[];
//    用来判断当前预加载图片到多少,计算加载总数
    var sum=0;
//    用来定位当前轮播到第几张图片了,定位数值的
    var position=0;
//    用来判断方向,指的是轮播图向那个方向运动
    var direction="";
//    判断当前图片是否可以切换的布尔值,如果是true表示可以切换图片,如果是false,自动轮播以及点击任何按钮都不可以切换图片
    var movieBool=true;
//    上一个小圆点,这里记录上一次的小圆点,用来当改变圆点时,让上次的小圆点效果发生改变,改变成其它样式
    var preCircle;
//    设置一个自动轮播的时间,当这个时间是0的时候就开始切换图片,初始值是最大间隔时间
    var autoTime=MAX_TIME;
//    是否自动轮播布尔变量,如果鼠标经过最外层容器时,不允许自动轮播,负责可以自动轮播,初始设置为false表示可以自动轮播
    var autoBoolean=false;
//    先把动画帧调用起来
    animation();
//    预加载图片
    loadImage();

    /*
    *  做预加载图片,先加载第一张图片,pathArr的第0项
    *  侦听load事件
    * */
    function loadImage() {
        var img=new Image();
        img.addEventListener("load",loadHandler);
        img.src=pathArr[0];
    }
    /*
    * 当加载完成时,用sum++,判断sum是否达到了图片地址的所有长度,
    * 如果sum==pathArr.lenght-1,表示预加载全部图片完成
    * 否则的话,我们继续加载下一张图片
    *   当预加载全部完成时,也就是sum>pathArr.length-1
    *   做一下内容:
    *   1、创建小圆点
    *   2、将第一张图片放进图片容器中
    *   3、修改当前小圆点的样式,第一个小圆点变化
    *   4、设置一个对象,里面的type是mouseleave,带入到mouseoverHandler函数中
    *   可以伪造鼠标离开的假相,这时,两个左右按钮就会隐藏起来,因为初始时无法知道鼠标在那里
    *   5、侦听左右按钮的点击事件
    *   6、侦听鼠标在外容器上是进入还是离开的事件
    *
    * */
    function loadHandler(e) {
        e=e || window.event;
        sum++;
//        将每一张加载进入的图片复制后放入图片数组
        imgArr.push(this.cloneNode(false));
        if(sum>pathArr.length-1){
//            串联在一起,当加载图片完成后,我们创建原点和第一张图片
            createCircle();
            imgCon.appendChild(imgArr[0]);
            changeCircle();
            var a={type:"mouseleave"};
            mouseoverHandler(a);
            leftBn.addEventListener("click",changeImgHandler);
            rightBn.addEventListener("click",changeImgHandler);
            maskDiv.addEventListener("mouseenter",mouseoverHandler);
            maskDiv.addEventListener("mouseleave",mouseoverHandler);
        }else{
            this.src=pathArr[sum];
        }

    }
    /*
    * 创建小圆点,
    * 设置每个小圆点的样式是circle,
    * 表示小圆点里的内容时i+1,就是从1开始到最大值
    * 给小圆点做侦听点击事件。并且把小圆点放在小圆点容器中
    * 给小圆点容器的位置调整居中
    *
    * */
    function createCircle() {
        for(var i=0;i<pathArr.length;i++){
            var circle=document.createElement("li");
            circle.className="circle";
            circle.textContent=i+1;
            circle.addEventListener("click",circleClickHandler);
            circleCon.appendChild(circle);
        }
        circleCon.style.left=(ROLL_WIDTH-circleCon.offsetWidth)/2+"px";
    }
    
    /*
    * 点击小圆点事件
    * 1、先判断当前是否正在做轮播动画,
    *   如果movieBool是false时表示正在做轮播动画,不进入点击后续工作
    *  2、设置上次显示的当前小圆点,将它赋予变量preCircle,用于后需的变化
    *  3、获取当前点击的小圆点的索引值,因为小圆点里面的数值是索引+1,因此这里我们在给它-1就是索引值了
    *  4、如果当前点击的索引值是当前正在停留的那个索引我们不需要再切换图片,直接跳出
    *  5、如果点击的索引大于当前的所以,我们设定轮播方向是向左,否则我们设置向右。
    *  6、设置让当前的定位为鼠标点击的定位索引
    *  7、设置图片轮播效果,执行changeImageEff函数
    *  8、修改小圆点转换效果,执行changeCircle
    *
    * */
    function circleClickHandler(e) {
        e=e || window.event;
        if(!movieBool) return;
        preCircle=circleCon.children[position];
        var index=Number(this.textContent)-1;
        if(index==position) return;
        if(index>position){
            direction="left";
        }else{
            direction="right";
        }
        position=index;
        changeImageEff();
        changeCircle();
    }
    /*
    * 修改小圆点效果变化
    * 设置起始样式,让小圆点本次的透明度有0到1
    * 设置上一个小圆点的透明度从1到0
    * 设置缓动动画,用cicrcleTweenUpdate时时更新小圆点样式
    *
    * */
    function changeCircle() {
        var startObj={currentAlpha:0,otherAlpha:1};
        var finshObj={currentAlpha:1,otherAlpha:0};
        var tween=new TWEEN.Tween(startObj);
        tween.to(finshObj);
        tween.onUpdate(cicrcleTweenUpdate);
        tween.start();
    }
    /*
    * 点击左右按钮时,执行的切换图片
    * 1、先判断当前是否正在做轮播动画,
    *   如果movieBool是false时表示正在做轮播动画,不进入点击后续工作
    *   2、设置上次显示的当前小圆点,将它赋予变量preCircle,用于后需的变化
    *   3、如果当前点击的按钮是左边的按钮我们让方向设置为左边,并且定位数值+1,如果大于图片数组的最后下标时,让其定位为0
    *   4、如果当前点击的按钮是右边的按钮我们让方向设置为右边,并且定位数值-1,如果小于0时,让其定位为图片数组的最后下标
    *    5、设置图片轮播效果,执行changeImageEff函数
    *  6、修改小圆点转换效果,执行changeCircle
    *
    * */
    function changeImgHandler(e) {
        e=e || window.event;
        if(!movieBool) return;
        preCircle=circleCon.children[position];
        if(this==leftBn){
            direction="left";
            position++;
            if(position>imgArr.length-1){
                position=0;
            }
        }else if(this==rightBn){
            direction="right";
            position--;
            if(position<0){
                position=imgArr.length-1;
            }
        }
        changeImageEff();
        changeCircle();
    }
    /*
    * 设置图片轮播效果
    * 1、定义初始的对象和结束的对象
    * 2、如果方向向左,设置将定位的图片插入到图片容器的尾部,设置图片容器的位置是0
    *    设置开始的位置是0,目标缓动的位置是-960,刚好是一张图片的宽度
    *4、如果方向向右,设置将定位的图片插入到图片容器的头部,设置图片容器的位置是-960
    *    设置开始的位置是-960,目标缓动的位置是0,刚好是一张图片的宽度
    *   5、设置该容器的总宽度是两张图片的宽度,即1920px
    *   6、设置开始轮播movieBool布尔开关设置为false表示点击任何按钮都不可以继续轮播操作
    *   7、让图片容器开始缓动
    *   8、侦听update和complete,分别是当缓动过程中设置图片容器的位置,缓动结束后执行completeHandler函数
    * */
    function changeImageEff() {
        var startObj={};
        var finshObj={};
        if(direction=="left"){
            imgCon.appendChild(imgArr[position]);
            imgCon.style.left="0px";
            startObj.left=0;
            finshObj.left=-960;
        }else if(direction=="right"){
            imgCon.insertBefore(imgArr[position],imgCon.firstElementChild);
            imgCon.style.left="-960px";
            startObj.left=-960;
            finshObj.left=0;
        }
        imgCon.style.width="1920px";
        movieBool=false;
        var tween=new TWEEN.Tween(startObj);
        tween.to(finshObj);
        tween.onUpdate(upDateHandler);
        tween.onComplete(completeHandler);
        tween.start();
    }
    /*
    *图片缓动时,这是图片容器的左端位置不断变化
    *
    * */
    function upDateHandler() {
        imgCon.style.left=this.left+"px";
    }
    /*
    * 设置小圆点缓动时,小圆点的背景色变化和文字变化
    * 注意这里如果上一次的小圆点存在时,让他回复和其它小圆点一样
    * 当前点击的小圆点,让背景色变成红色,并且文字颜色变成白色
    * */
    function cicrcleTweenUpdate() {

        if(preCircle){
            preCircle.style.color="#000000";
            preCircle.style.backgroundColor="rgba(255,0,0,"+this.otherAlpha+")"
        }
        circleCon.children[position].style.color="#FFFFFF";
        circleCon.children[position].style.backgroundColor="rgba(255,0,0,"+this.currentAlpha+")"
    }
    /*
    * 当完成轮播后
    * 设置让movieBool是true,这样其它的按钮点击可以继续轮播了,表示打开开关
    * 如果方向向左,我们删除第一个元素
    * 如果方向向右,我们删除最后一个元素,这两个都是最早的图片,我们仅保留切换完成的图片
    * 设置图片容器的宽度为一张图片宽度,左边位置设为0
    *
    * */
    function completeHandler() {
        movieBool=true;
        if(direction=="left"){
            imgCon.firstElementChild.remove();
        }else if(direction=="right"){
            imgCon.lastElementChild.remove()
        }
        imgCon.style.width="960px";
        imgCon.style.left="0px";
    }
    /*
    * 鼠标离开大容器时,自动轮播
    * 1、当鼠标进入大容器时,左右按钮的位置从-50到10,缓动进入屏幕
    * 2、当鼠标离开大容器,左右按钮的位置从10到-50,缓动离开屏幕
    * 3、如果进入大容器时,autoBoolean设置为false,表示不可自动轮播
    * 4、离开大容器时,autoBoolean设为true,表示开始自动轮播
    * 5、设置左右按钮500毫秒缓动动画
    *
    * */
    function mouseoverHandler(e) {
        e=e || window.event;
        var startObj={};
        var finshObj={};
        var ease;
        if(e.type=="mouseenter"){
            startObj.left=-50;
            startObj.right=-50;
            finshObj.left=10;
            finshObj.right=10;
            ease=TWEEN.Easing.Bounce.Out;
            autoBoolean=false;
        }else if(e.type=="mouseleave"){
            startObj.left=10;
            startObj.right=10;
            finshObj.left=-50;
            finshObj.right=-50;
            ease=TWEEN.Easing.Bounce.In;
            autoBoolean=true;
        }
        var tween=new TWEEN.Tween(startObj);
        tween.to(finshObj,500);
        tween.easing(ease);
        tween.onUpdate(autoUpdataHandler);
        tween.start();
    }
    /*
    * 左右按钮在鼠标经过大容器或者离开大容器时位置的缓动设置
    * */
    function autoUpdataHandler() {
        leftBn.style.left=this.left+"px";
        rightBn.style.right=this.right+"px";
    }
    /*
    * 动画
    * 1、TWEEN.update()让所有的缓动动画更新,每帧更新
    * 2、每帧执行自动轮播图像更新
    *
    * */
    function animation() {
        requestAnimationFrame(animation);
        TWEEN.update();
        autoRollImageUpdate()
    }
    /*
    * 每帧自动轮播更新
    * 1、当不能自动轮播时(就是鼠标在大容器上时),设置自动轮播的间歇时间回到最大时间(120),并且不继续后续工作,跳出
    * 2、当当前正在做轮播时(movieBool是false,表示正在自动轮播),设置自动轮播的间歇时间为最大,并且跳出
    * 3、除以上两个之外,autoTime这个自动间歇时间每帧-1,如果它的值等于0,开始做自动轮播
    *    1、设置前一个的小圆点
    *    2、设置方向是向左
    *    3、设置自增当前的定位
    *    4、如果当前定位大于图片数组的数量时,回到0
    *    5、修改图片轮播效果
    *    6、修改圆点效果
    *    7、重新设置当前自动轮播间隔时间为最大值
    * */
    function autoRollImageUpdate() {
        if(!autoBoolean || !movieBool){
            autoTime=MAX_TIME;
            return;
        }
        autoTime--;
        if(autoTime==0){
            preCircle=circleCon.children[position];
            direction="left";
            position++;
            if(position>imgArr.length-1){
                position=0;
            }
            changeImageEff();
            changeCircle();
            autoTime=MAX_TIME;
        }
    }

</script>
</body>
</html>

轮播图(js代码):

/**
 * 
 * https://github.com/tweenjs/tween.js
 * ----------------------------------------------
 * See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors.
 * Thank you all!
 */

var TWEEN = TWEEN || (function () {

	var _tweens = [];

	return {

		getAll: function () {

			return _tweens;

		},

		removeAll: function () {

			_tweens = [];

		},

		add: function (tween) {

			_tweens.push(tween);

		},

		remove: function (tween) {

			var i = _tweens.indexOf(tween);

			if (i !== -1) {
				_tweens.splice(i, 1);
			}

		},

		update: function (time, preserve) {

			if (_tweens.length === 0) {
				return false;
			}

			var i = 0;

			time = time !== undefined ? time : TWEEN.now();

			while (i < _tweens.length) {

				if (_tweens[i].update(time) || preserve) {
					i++;
				} else {
					_tweens.splice(i, 1);
				}

			}

			return true;

		}
	};

})();


// Include a performance.now polyfill.
// In node.js, use process.hrtime.
if (typeof (window) === 'undefined' && typeof (process) !== 'undefined') {
	TWEEN.now = function () {
		var time = process.hrtime();

		// Convert [seconds, nanoseconds] to milliseconds.
		return time[0] * 1000 + time[1] / 1000000;
	};
}
// In a browser, use window.performance.now if it is available.
else if (typeof (window) !== 'undefined' &&
         window.performance !== undefined &&
		 window.performance.now !== undefined) {
	// This must be bound, because directly assigning this function
	// leads to an invocation exception in Chrome.
	TWEEN.now = window.performance.now.bind(window.performance);
}
// Use Date.now if it is available.
else if (Date.now !== undefined) {
	TWEEN.now = Date.now;
}
// Otherwise, use 'new Date().getTime()'.
else {
	TWEEN.now = function () {
		return new Date().getTime();
	};
}


TWEEN.Tween = function (object) {

	var _object = object;
	var _valuesStart = {};
	var _valuesEnd = {};
	var _valuesStartRepeat = {};
	var _duration = 1000;
	var _repeat = 0;
	var _repeatDelayTime;
	var _yoyo = false;
	var _isPlaying = false;
	var _reversed = false;
	var _delayTime = 0;
	var _startTime = null;
	var _easingFunction = TWEEN.Easing.Linear.None;
	var _interpolationFunction = TWEEN.Interpolation.Linear;
	var _chainedTweens = [];
	var _onStartCallback = null;
	var _onStartCallbackFired = false;
	var _onUpdateCallback = null;
	var _onCompleteCallback = null;
	var _onStopCallback = null;

	this.to = function (properties, duration) {

		_valuesEnd = properties;

		if (duration !== undefined) {
			_duration = duration;
		}

		return this;

	};

	this.start = function (time) {

		TWEEN.add(this);

		_isPlaying = true;

		_onStartCallbackFired = false;

		_startTime = time !== undefined ? time : TWEEN.now();
		_startTime += _delayTime;

		for (var property in _valuesEnd) {

			// Check if an Array was provided as property value
			if (_valuesEnd[property] instanceof Array) {

				if (_valuesEnd[property].length === 0) {
					continue;
				}

				// Create a local copy of the Array with the start value at the front
				_valuesEnd[property] = [_object[property]].concat(_valuesEnd[property]);

			}

			// If `to()` specifies a property that doesn't exist in the source object,
			// we should not set that property in the object
			if (_object[property] === undefined) {
				continue;
			}

			// Save the starting value.
			_valuesStart[property] = _object[property];

			if ((_valuesStart[property] instanceof Array) === false) {
				_valuesStart[property] *= 1.0; // Ensures we're using numbers, not strings
			}

			_valuesStartRepeat[property] = _valuesStart[property] || 0;

		}

		return this;

	};

	this.stop = function () {

		if (!_isPlaying) {
			return this;
		}

		TWEEN.remove(this);
		_isPlaying = false;

		if (_onStopCallback !== null) {
			_onStopCallback.call(_object, _object);
		}

		this.stopChainedTweens();
		return this;

	};

	this.end = function () {

		this.update(_startTime + _duration);
		return this;

	};

	this.stopChainedTweens = function () {

		for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
			_chainedTweens[i].stop();
		}

	};

	this.delay = function (amount) {

		_delayTime = amount;
		return this;

	};

	this.repeat = function (times) {

		_repeat = times;
		return this;

	};

	this.repeatDelay = function (amount) {

		_repeatDelayTime = amount;
		return this;

	};

	this.yoyo = function (yoyo) {

		_yoyo = yoyo;
		return this;

	};


	this.easing = function (easing) {

		_easingFunction = easing;
		return this;

	};

	this.interpolation = function (interpolation) {

		_interpolationFunction = interpolation;
		return this;

	};

	this.chain = function () {

		_chainedTweens = arguments;
		return this;

	};

	this.onStart = function (callback) {

		_onStartCallback = callback;
		return this;

	};

	this.onUpdate = function (callback) {

		_onUpdateCallback = callback;
		return this;

	};

	this.onComplete = function (callback) {

		_onCompleteCallback = callback;
		return this;

	};

	this.onStop = function (callback) {

		_onStopCallback = callback;
		return this;

	};

	this.update = function (time) {

		var property;
		var elapsed;
		var value;

		if (time < _startTime) {
			return true;
		}

		if (_onStartCallbackFired === false) {

			if (_onStartCallback !== null) {
				_onStartCallback.call(_object, _object);
			}

			_onStartCallbackFired = true;
		}

		elapsed = (time - _startTime) / _duration;
		elapsed = elapsed > 1 ? 1 : elapsed;

		value = _easingFunction(elapsed);

		for (property in _valuesEnd) {

			// Don't update properties that do not exist in the source object
			if (_valuesStart[property] === undefined) {
				continue;
			}

			var start = _valuesStart[property] || 0;
			var end = _valuesEnd[property];

			if (end instanceof Array) {

				_object[property] = _interpolationFunction(end, value);

			} else {

				// Parses relative end values with start as base (e.g.: +10, -3)
				if (typeof (end) === 'string') {

					if (end.charAt(0) === '+' || end.charAt(0) === '-') {
						end = start + parseFloat(end);
					} else {
						end = parseFloat(end);
					}
				}

				// Protect against non numeric properties.
				if (typeof (end) === 'number') {
					_object[property] = start + (end - start) * value;
				}

			}

		}

		if (_onUpdateCallback !== null) {
			_onUpdateCallback.call(_object, value);
		}

		if (elapsed === 1) {

			if (_repeat > 0) {

				if (isFinite(_repeat)) {
					_repeat--;
				}

				// Reassign starting values, restart by making startTime = now
				for (property in _valuesStartRepeat) {

					if (typeof (_valuesEnd[property]) === 'string') {
						_valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property]);
					}

					if (_yoyo) {
						var tmp = _valuesStartRepeat[property];

						_valuesStartRepeat[property] = _valuesEnd[property];
						_valuesEnd[property] = tmp;
					}

					_valuesStart[property] = _valuesStartRepeat[property];

				}

				if (_yoyo) {
					_reversed = !_reversed;
				}

				if (_repeatDelayTime !== undefined) {
					_startTime = time + _repeatDelayTime;
				} else {
					_startTime = time + _delayTime;
				}

				return true;

			} else {

				if (_onCompleteCallback !== null) {

					_onCompleteCallback.call(_object, _object);
				}

				for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
					// Make the chained tweens start exactly at the time they should,
					// even if the `update()` method was called way past the duration of the tween
					_chainedTweens[i].start(_startTime + _duration);
				}

				return false;

			}

		}

		return true;

	};

};


TWEEN.Easing = {

	Linear: {

		None: function (k) {

			return k;

		}

	},

	Quadratic: {

		In: function (k) {

			return k * k;

		},

		Out: function (k) {

			return k * (2 - k);

		},

		InOut: function (k) {

			if ((k *= 2) < 1) {
				return 0.5 * k * k;
			}

			return - 0.5 * (--k * (k - 2) - 1);

		}

	},

	Cubic: {

		In: function (k) {

			return k * k * k;

		},

		Out: function (k) {

			return --k * k * k + 1;

		},

		InOut: function (k) {

			if ((k *= 2) < 1) {
				return 0.5 * k * k * k;
			}

			return 0.5 * ((k -= 2) * k * k + 2);

		}

	},

	Quartic: {

		In: function (k) {

			return k * k * k * k;

		},

		Out: function (k) {

			return 1 - (--k * k * k * k);

		},

		InOut: function (k) {

			if ((k *= 2) < 1) {
				return 0.5 * k * k * k * k;
			}

			return - 0.5 * ((k -= 2) * k * k * k - 2);

		}

	},

	Quintic: {

		In: function (k) {

			return k * k * k * k * k;

		},

		Out: function (k) {

			return --k * k * k * k * k + 1;

		},

		InOut: function (k) {

			if ((k *= 2) < 1) {
				return 0.5 * k * k * k * k * k;
			}

			return 0.5 * ((k -= 2) * k * k * k * k + 2);

		}

	},

	Sinusoidal: {

		In: function (k) {

			return 1 - Math.cos(k * Math.PI / 2);

		},

		Out: function (k) {

			return Math.sin(k * Math.PI / 2);

		},

		InOut: function (k) {

			return 0.5 * (1 - Math.cos(Math.PI * k));

		}

	},

	Exponential: {

		In: function (k) {

			return k === 0 ? 0 : Math.pow(1024, k - 1);

		},

		Out: function (k) {

			return k === 1 ? 1 : 1 - Math.pow(2, - 10 * k);

		},

		InOut: function (k) {

			if (k === 0) {
				return 0;
			}

			if (k === 1) {
				return 1;
			}

			if ((k *= 2) < 1) {
				return 0.5 * Math.pow(1024, k - 1);
			}

			return 0.5 * (- Math.pow(2, - 10 * (k - 1)) + 2);

		}

	},

	Circular: {

		In: function (k) {

			return 1 - Math.sqrt(1 - k * k);

		},

		Out: function (k) {

			return Math.sqrt(1 - (--k * k));

		},

		InOut: function (k) {

			if ((k *= 2) < 1) {
				return - 0.5 * (Math.sqrt(1 - k * k) - 1);
			}

			return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);

		}

	},

	Elastic: {

		In: function (k) {

			if (k === 0) {
				return 0;
			}

			if (k === 1) {
				return 1;
			}

			return -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI);

		},

		Out: function (k) {

			if (k === 0) {
				return 0;
			}

			if (k === 1) {
				return 1;
			}

			return Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1;

		},

		InOut: function (k) {

			if (k === 0) {
				return 0;
			}

			if (k === 1) {
				return 1;
			}

			k *= 2;

			if (k < 1) {
				return -0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI);
			}

			return 0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) + 1;

		}

	},

	Back: {

		In: function (k) {

			var s = 1.70158;

			return k * k * ((s + 1) * k - s);

		},

		Out: function (k) {

			var s = 1.70158;

			return --k * k * ((s + 1) * k + s) + 1;

		},

		InOut: function (k) {

			var s = 1.70158 * 1.525;

			if ((k *= 2) < 1) {
				return 0.5 * (k * k * ((s + 1) * k - s));
			}

			return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);

		}

	},

	Bounce: {

		In: function (k) {

			return 1 - TWEEN.Easing.Bounce.Out(1 - k);

		},

		Out: function (k) {

			if (k < (1 / 2.75)) {
				return 7.5625 * k * k;
			} else if (k < (2 / 2.75)) {
				return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75;
			} else if (k < (2.5 / 2.75)) {
				return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375;
			} else {
				return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375;
			}

		},

		InOut: function (k) {

			if (k < 0.5) {
				return TWEEN.Easing.Bounce.In(k * 2) * 0.5;
			}

			return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5;

		}

	}

};

TWEEN.Interpolation = {

	Linear: function (v, k) {

		var m = v.length - 1;
		var f = m * k;
		var i = Math.floor(f);
		var fn = TWEEN.Interpolation.Utils.Linear;

		if (k < 0) {
			return fn(v[0], v[1], f);
		}

		if (k > 1) {
			return fn(v[m], v[m - 1], m - f);
		}

		return fn(v[i], v[i + 1 > m ? m : i + 1], f - i);

	},

	Bezier: function (v, k) {

		var b = 0;
		var n = v.length - 1;
		var pw = Math.pow;
		var bn = TWEEN.Interpolation.Utils.Bernstein;

		for (var i = 0; i <= n; i++) {
			b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i);
		}

		return b;

	},

	CatmullRom: function (v, k) {

		var m = v.length - 1;
		var f = m * k;
		var i = Math.floor(f);
		var fn = TWEEN.Interpolation.Utils.CatmullRom;

		if (v[0] === v[m]) {

			if (k < 0) {
				i = Math.floor(f = m * (1 + k));
			}

			return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i);

		} else {

			if (k < 0) {
				return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]);
			}

			if (k > 1) {
				return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]);
			}

			return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i);

		}

	},

	Utils: {

		Linear: function (p0, p1, t) {

			return (p1 - p0) * t + p0;

		},

		Bernstein: function (n, i) {

			var fc = TWEEN.Interpolation.Utils.Factorial;

			return fc(n) / fc(i) / fc(n - i);

		},

		Factorial: (function () {

			var a = [1];

			return function (n) {

				var s = 1;

				if (a[n]) {
					return a[n];
				}

				for (var i = n; i > 1; i--) {
					s *= i;
				}

				a[n] = s;
				return s;

			};

		})(),

		CatmullRom: function (p0, p1, p2, p3, t) {

			var v0 = (p2 - p0) * 0.5;
			var v1 = (p3 - p1) * 0.5;
			var t2 = t * t;
			var t3 = t * t2;

			return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (- 3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;

		}

	}

};

// UMD (Universal Module Definition)
(function (root) {

	if (typeof define === 'function' && define.amd) {

		// AMD
		define([], function () {
			return TWEEN;
		});

	} else if (typeof module !== 'undefined' && typeof exports === 'object') {

		// Node.js
		module.exports = TWEEN;

	} else if (root !== undefined) {

		// Global variable
		root.TWEEN = TWEEN;

	}

})(this);


猜你喜欢

转载自blog.csdn.net/fenglei0415/article/details/80456183