애니메이션의 원리
소위 애니메이션은 일련의 움직임을 통해 형성된 움직이는 그림이다. 웹 페이지에서는 요소의 CSS 값을 지속적으로 변경하여 동적 효과를 얻을 수 있습니다.
사용된 공식
총 거리 S = 총 시간 T * 속도 V, 즉: V = S/T
현재 거리 s = S/T * 경과 시간 t, 즉 는: s = S * (t/T)
즉, 현재 거리 = 총 거리 * (경과 시간/총 시간)
즉, 애니메이션 요소 시작 값(애니메이션 요소 끝 값 - 애니메이션 요소 시작 값)입니다. ) * ( 현재 시간 - 시작 시간) / (애니메이션에 필요한 시간) 값 형식
위 수식을 사용하면 JavaScript의 setInterval 또는 setTimeout을 사용하여 간단한 애니메이션을 만들 수 있습니다.
그러나 애니메이션 라이브러리를 구축하려면 다른 요소도 고려해야 합니다. 예를 들어 동일한 요소의 애니메이션은 순차적으로 실행되어야 합니다. 다양한 요소의 애니메이션을 동시에 실행할 수 있습니다.
이 경우 이러한 애니메이션을 관리하려면 다른 개체를 사용해야 합니다. 나의 초기 아이디어는 각 요소를 배열에 넣고 여러 setInterval을 사용하여 배열의 애니메이션 기능을 반복하고 하나씩 실행하는 것이었습니다.
animate1 = [{elem,fn},{elem,fn}];
animate2 = [{elem,fn},{elem,fn}]
달성하면 동일한 요소 애니메이션이 순차적으로 실행되는 반면, 다른 요소 애니메이션은 동시에 실행될 수 있습니다. 하지만 여기에는 문제가 있습니다. 즉, 애니메이션의 요소가 10개 이상인 경우입니다. 프로그램은 10개의 setInterval을 열어야 합니다.
이러한 상황을 방지하기 위해 위 내용을 바탕으로 몇 가지 개선 작업을 진행했습니다. 따라서 애니메이션이 아무리 많아도 마찬가지입니다. 모두 setInterval을 사용하여 수행됩니다. 수정된 구조는 다음과 같습니다.
[
[elem ,[fn ,fn,fn,fn]],
[요소,[fn,fn,fn,fn]],
[요소,[fn,fn,fn,fn]]
]
이런 방식으로 setInterval을 사용하여 모든 애니메이션을 완성할 수 있습니다. 수행해야 할 작업은 elem을 반복하고 elem 이후 요소의 첫 번째 fn을 실행하는 것입니다. fn을 삭제합니다. 다음 fn을 호출합니다. 모든 fn이 비어 있으면 큰 배열에서 elem을 삭제합니다. elem이 비어 있으면 setInterval을 지웁니다. 그러면 논리가 이해가 될 것입니다.
그러나 애니메이션에서 가장 중요한 또 하나의 요소가 있는데 바로 완화입니다. 완화가 없으면 애니메이션이 매우 경직된 것처럼 보입니다. 동일성. 현재 js 애니메이션에는 많은 여유 알고리즘이 사용되며 이는 대략 두 가지 범주로 나눌 수 있습니다.
하나는 플래시 클래스이고 다른 하나는 프로토타입 클래스입니다.
flash에는 4개의 매개변수가 필요합니다.
1. 시간의 초기 시간 t
2. 애니메이션의 초기 값 b
3. 애니메이션의 종료 값 c
4. 🎜>다음은 Flash 유형의 균일 모션 알고리즘입니다.
Linear: function(t,b,c,d){ return c*t/d b; }
다른 하나는 프로토타입입니다. 하나, 그러면 현재 시간 t와 기간 d의 비율(t/d)입니다.
두 번째 방법은 매개변수가 편리하기 때문에 사용했습니다. 또한 위의 애니메이션 공식에 더 적합합니다. 다음은 프로토타입 균일 모션 알고리즘
linear: function(t){ return t;}입니다.
easing을 추가하면 위의 공식은
애니메이션 요소가 됩니다. 시작 값(애니메이션 요소 종료 값 - 애니메이션 요소 시작 값) * 완화 함수((현재 시간 - 시작 시간) / (애니메이션 필요 시간)) 값 형식입니다.
이제 전체 애니메이션 디자인이 끝났습니다. 다른 분들의 블로그를 참고해서 감사의 말씀을 전하고 싶습니다.
마지막으로 자세한 코드를 올려드리겠습니다.
코드 복사 코드는 다음과 같습니다.
/**
* 생성시간 2012/08/29
* @author lynx cat.
* @version 0.77beta.
*/
(function(win,doc){
var win = win || window;
var doc = doc || win. document,
pow = Math.pow,
sin = Math.sin,
PI = Math.PI,
BACK_CONST = 1.70158
var Easing = {
// 匀速运动
linear: function(t){
return t;
},
easeIn: function(t) {
return t * t
},
easeOut: 함수 (t) {
return ( 2 - t) * t
},
easeBoth : function (t) {
return (t *= 2)
.5 * t * t :
.5 * (1 - (--t) * (t - 2))
},
easeInStrong : function (t) {
return t * t * t * t
},
easeOutStrong: 함수(t) {
return 1 - (--t) * t * t * t
},
easeBothStrong: 함수(t ) {
반환 (t *= 2) < 1 ?
.5 * t * t * t * t :
.5 * (2 - (t -= 2) * t * t * t);
},
easeOutQuart : function(t){
return -(pow((t-1), 4) -1)
},
easeInOutExpo : function(t ){
if(t===0) return 0;
if(t===1) return 1
if((t/=0.5) < 1) return 0.5 * pow( 2,10 * (t-1));
return 0.5 * (-pow(2, -10 * --t) 2)
},
easeOutExpo : function(t){
반환 (t===1) ? 1 : -pow(2, -10 * t) 1;
},
swingFrom : function(t) {
return t*t*((BACK_CONST 1)*t - BACK_CONST);
},
swingTo: function(t) {
return (t-=1)*t*((BACK_CONST 1)*t BACK_CONST) 1;
},
sinusoidal : function(t) {
return (-Math.cos(t*PI)/2) 0.5;
},
flicker : function(t) {
var t = t (Math.random()-0.5)/5;
return this.sinusoidal(t 1 ? 1 : t);
},
backIn : function (t) {
if (t === 1) t -= .001;
t * t * ((BACK_CONST 1) * t - BACK_CONST)를 반환합니다.
},
backOut : 함수 (t) {
return (t -= 1) * t * ((BACK_CONST 1) * t BACK_CONST) 1;
},
bounce : 함수(t) {
var s = 7.5625, r;
if (t r = s * t * t;
}
else if (t r = s * (t -= (1.5 / 2.75)) * t .75;
}
else if (t r = s * (t -= (2.25 / 2.75)) * t .9375;
}
else {
r = s * (t -= (2.625 / 2.75)) * t .984375;
}
r을 반환합니다.
}
};
/**
* 대화 메소드가 포함된 객체를 반환하는 데 사용되는 Cornerstone
* @param elem
* @return {Object}
*/
function catfx(elem){
elem = elem 유형 === 'string' ? doc.getElementById(elem) : 요소;
새 fx(elem)를 반환합니다.
}
/**
* 대화 메소드가 포함된 객체를 반환하는 데 사용되는 내부 초석
* @param elem
* @return {Object}
*/
function fx(elem){
this.elem = elem;
이것을 돌려주세요;
}
/**
* 기본 클래스에는 몇 가지 기본 메서드와 불변값이 포함되어 있습니다
*/
var fxBase = {
속도: {
느림: 600,
빠름: 200,
기본값: 400
},
fxAttrs : [],
fxMap:[],
/**
* 객체 요소의 CSS 값을 반환합니다.
* @param elem
* @param p
* @return css value
*/
getStyle : function(){
var fn = function (){} ;
if('getCompulatedStyle' in win){
fn = function(elem, p){
var p = p.replace(/-(w)/g,function(i,str){
return str.toUpperCase()
});
var val = getCompulatedStyle(elem, null)[p];
if(~(' ' p ' ').indexOf(' 왼쪽 오른쪽 위 아래 ') && val === 'auto'){
val = '0px';
}
반환 값;
}
}else {
fn = function(elem, p){
var p = p.replace(/-(w)/g,function(i,str){
return str.toUpperCase()
});
var val = elem.currentStyle[p];
if(~(' ' p ' ').indexOf(' width height') && val === 'auto'){
var ect = elem.getBoundingClientRect();
val = ( p === '너비' ? REC.오른쪽 - REC.왼쪽 : REC.하단 - REC.TOP ) 'px';
}
if(p === '불투명도'){
var filter = elem.currentStyle.filter;
if( /opacity/.test(filter) ){
val = filter.match( /d / )[0] / 100;
val = (val === 1 || val === 0) ? val.toFixed(0) : val.toFixed(1);
}else if( val === 정의되지 않음 ){
val = 1;
}
}
if(~(' ' p ' ').indexOf(' 왼쪽 오른쪽 위 아래 ') && val === 'auto'){
val = '0px';
}
반환 값;
}
}
fn을 반환;
}(),
/**
* 객체 요소의 CSS 값을 반환합니다
* @param 색상 값(red, pink, blue 및 기타 영문은 아직 지원되지 않음)
* @return rgb(x,x,x)
*/
getColor : function(val){
var r, g, b;
if(/rgb/.test(val)){
var arr = val.match(/d /g);
r = arr[0];
g = arr[1];
b = arr[2];
}else if(/#/.test(val)){
var len = val.length;
if( len === 7 ){
r =parseInt( val.slice(1, 3), 16);
g =parseInt(val.slice(3, 5), 16);
b =parseInt(val.slice(5), 16);
}
else if( len === 4 ){
r =parseInt(val.charAt(1) val.charAt(1), 16);
g = parsInt(val.charAt(2) val.charAt(2), 16);
b = parsInt(val.charAt(3) val.charAt(3), 16);
}
}else{
return val;
}
return {
r:parseFloat(r),
g:parseFloat(g),
b:parseFloat(b)
}
},
/**
* Return the parsed css
* @param prop
* @return {val:val,unit:unit}
*/
parseStyle : function(prop){
var val = parseFloat(prop),
unit = prop.replace(/^[-d.] /, '');
if(isNaN(val)){
val = this.getColor(unit);
unit = '';
}
return {val : val, unit : unit};
},
/**
* Set the transparency of the element
* @param elem
* @param val
*/
setOpacity : function(elem, val){
if( 'getComputedStyle' in win ){
elem.style.opacity = val === 1 ? '' : val;
}else{
elem.style.zoom = 1;
elem.style.filter = val === 1 ? '' : 'alpha(opacity=' val * 100 ')';
}
},
/**
* Set the css value of the element
* @param elem
* @param prop
* @param val
*/
setStyle : function(elem, prop, val){
if(prop != 'opacity'){
prop = prop.replace(/-(w)/g,function(i,p){
return p.toUpperCase();
});
elem.style[prop] = val;
}else{
this.setOpacity(elem, val);
}
},
/**
* Return the parsed prop
* @param prop
* @return {prop}
*/
parseProp : function(prop){
var props = {};
for(var i in prop){
props[i] = this.parseStyle(prop[i].toString());
}
return props;
},
/**
* Correct user parameters
* @param elem
* @param duration
* @param easing
* @param callback
* @return {options}
*/
setOption : function(elem,duration, easing, callback){
var options = {};
var _this = this;
options.duration = function(duration){
if(typeof duration == 'number'){
return duration;
}else if(typeof duration == 'string' && _this.speed[duration]){
return _this.speed[duration];
}else{
return _this.speed.defaults;
}
}(duration);
options.easing = function(easing){
if(typeof easing == 'function'){
return easing;
}else if(typeof easing == 'string' && Easing[easing]){
return Easing[easing];
}else{
return Easing.linear;
}
}(easing);
options.callback = function(callback){
var _this = this;
return function (){
if(typeof callback == 'function'){
callback.call(elem);
}
}
}(callback)
return options;
},
/**
* Maintain setInterval function and start animation
*/
tick : function(){
var _this = this;
if(!_this.timer){
_this.timer = setInterval(function(){
for(var i = 0, len = _this.fxMap.length; i < len; i ){
var elem = _this.fxMap[i][0];
var core = _this.data(elem)[0];
core(elem);
}
},16);
}
},
/**
* Stop all animations
*/
stop : function(){
if(this.timer){
clearInterval(this.timer);
this.timer = undefined;
}
},
/**
* Store or take out the queue
* @param elem
*/
data : function(elem){
for(var i = 0, len = this.fxMap.length; i < len; i ){
var data = this.fxMap[i];
if(elem === data[0]){
return data[1];
}
}
this.fxMap.push([elem,[]]);
return this.fxMap[this.fxMap.length - 1][1];
},
/**
* Delete queue
* @param elem
*/
removeData : function(elem){
for(var i = 0, len = this.fxMap.length; i < len; i ){
var data = this.fxMap[i];
if(elem === data[0]){
this.fxMap.splice(i, 1);
if(this.isDataEmpty()){
this.stop();
}
}
}
},
isDataEmpty : function(){
return this.fxMap.length == 0;
}
}, $ = fxBase;
/**
* Core object, used to generate animation objects.
* @param elem
* @param props
* @param options
* @return {Object}
*/
function fxCore(elem, props, options){
this.elem = elem;
this.props = props;
this.options = options;
this.start();
}
fxCore.prototype = {
constructor : fxCore,
/**
* Add the animation function to the queue and start the animation.
*/
start : function(){
var cores = $.data(this.elem);
cores.push(this.step());
$.tick();
},
/**
* 核心方法,控制每一帧元素的状态。
* @return 함수
*/
step : function(){
var _this = this;
var fn = function(elem){
var t = Date.now() - this.startTime;
if(Date.now() < this.startTime this.options.duration){
if(t <= 1){ t = 1;}
for(this.target의 var i ){
if(typeof this.source[i]['val'] === 'number'){
var val =parseFloat((this.source[i]['val'] (this. target[i]['val'] - this.source[i]['val']) * this.options.easing(t / this.options.duration)).toFixed(7));
}else{
var r =parseInt(this.source[i]['val']['r'] (this.target[i]['val']['r'] - this. source[i]['val']['r']) * this.options.easing(t / this.options.duration));
var g =parseInt(this.source[i]['val']['g'] (this.target[i]['val']['g'] - this.source[i][' val']['g']) * this.options.easing(t / this.options.duration));
var b =parseInt(this.source[i]['val']['b'] (this.target[i]['val']['b'] - this.source[i][' val']['b']) * this.options.easing(t / this.options.duration));
var val = 'rgb(' r ',' g ',' b ')';
}
$.setStyle(this.elem,i,val this.source[i]['unit']);
}
}else{
for(var i in this.target){
if(typeof this.target[i]['val'] === 'number'){
var val = this.target[i]['val'];
}else{
var val = 'rgb(' this.target[i]['val']['r'] ',' this.target[i]['val']['g' ] ',' this.target[i]['val']['b'] ')';
}
$.setStyle(elem,i,val this.source[i]['unit']);
}
var cores = $.data(elem);
cores.shift();
this.options.callback();
if(cores.length == 0){
$.setStyle(elem,'overflow',this.overflow);
$.removeData(elem);
}
}
}
반환 함수(elem){
if(!_this.startTime){
var source = {};
_this.target = _this.props;
for(var i in _this.props){
var val = $.getStyle(_this.elem, i);
소스[i] = $.parseStyle(val);
}
_this.source = 소스;
_this.startTime = Date.now();
_this.overflow = $.getStyle(elem,'overflow');
$.setStyle(elem,'overflow','hidden');
}
fn.call(_this,elem);
}
}
}
/**
* 외부 인터페이스 클래스.
*/
fx.prototype = {
constructor : fx,
/**
* 애니메이션 메소드
* @param prop
* @param 기간
* @param easing
* @param 콜백
* @return {Object}
*/
animate: function(prop, Duration, easing, callback){
if(arguments.length == 3 && typeof easing === 'function'){ //多数时候用户第三个参数是回调
콜백 = 완화;
완화 = 정의되지 않음;
}
var props = $.parseProp(prop);
var options = $.setOption(this.elem,duration,easing,callback);
var core = new fxCore(this.elem,props,options);
이것을 돌려주세요;
},
/**
* 애니메이션 중지 방법
* catjs('your element id').stop() 방법 사용
*/
stop : function(){
$.removeData(this.elem);
}
}
win.catfx = catfx;
})(이 문서);
使用起来也比较简单.直接catfx('ID').animate({'margin-left':200,'Background-color':'#ff0000'},600,'easeOut ',기능(){});
jquery의 사용 방법은 다양하지 않습니다.数为函数,并且总共只有三个参数时。第三个参数为回调。
例:catfx('ID').animate({'margin-left':200,'Background-color':'#ff0000'},600,function (){alert('洒家是回调函数~')});