1 /** 2 * JET (Javascript Extension Tools) 3 * Copyright (c) 2009, KDV.cn, All rights reserved. 4 * Code licensed under the BSD License: 5 * http://developer.kdv.cn/jet/license.txt 6 * 7 * @fileOverview Jx! 8 * @version 1.0 9 * @author Kinvix(<a href="mailto:Kinvix@gmail.com">Kinvix@gmail.com</a>) 10 * @description 11 * 12 */ 13 14 15 /** 16 * @description 17 * Package: jet.fx 18 * 19 * Need package: 20 * jet.core.js 21 * 22 */ 23 24 25 26 Jx().$package(function(J){ 27 /** 28 * 动画包 29 * @namespace 30 * @name fx 31 */ 32 J.fx = J.fx || {}; 33 34 var $D = J.dom, 35 $E = J.event; 36 37 /** 38 * 动画缓动公式 39 * 40 * Linear:无缓动效果; 41 * Quadratic:二次方的缓动(t^2); 42 * Cubic:三次方的缓动(t^3); 43 * Quartic:四次方的缓动(t^4); 44 * Quintic:五次方的缓动(t^5); 45 * Sinusoidal:正弦曲线的缓动(sin(t)); 46 * Exponential:指数曲线的缓动(2^t); 47 * Circular:圆形曲线的缓动(sqrt(1-t^2)); 48 * Elastic:指数衰减的正弦曲线缓动; 49 * Back:超过范围的三次方缓动((s+1)*t^3 - s*t^2); 50 * Bounce:指数衰减的反弹缓动。 51 * 52 * 每个效果都分三个缓动方式(方法),分别是: 53 * easeIn:从0开始加速的缓动; 54 * easeOut:减速到0的缓动; 55 * easeInOut:前半段从0开始加速,后半段减速到0的缓动。 56 * 其中Linear是无缓动效果,没有以上效果。 57 * 58 * p,pos: current(当前); 59 * x: value(其他参数); 60 */ 61 var Transition = new J.Class({ 62 init:function(transition, params){ 63 params = J.array.toArray(params); 64 65 var newTransition = J.extend(transition, { 66 //从0开始加速的缓动; 67 easeIn: function(pos){ 68 return transition(pos, params); 69 }, 70 //减速到0的缓动; 71 easeOut: function(pos){ 72 return 1 - transition(1 - pos, params); 73 }, 74 //前半段从0开始加速,后半段减速到0的缓动。 75 easeInOut: function(pos){ 76 return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2; 77 } 78 }); 79 80 return newTransition; 81 } 82 }); 83 84 /** 85 * 86 * 动画缓动公式 87 * 88 * Linear:无缓动效果; 89 * Quadratic:二次方的缓动(t^2); 90 * Cubic:三次方的缓动(t^3); 91 * Quartic:四次方的缓动(t^4); 92 * Quintic:五次方的缓动(t^5); 93 * Sinusoidal:正弦曲线的缓动(sin(t)); 94 * Exponential:指数曲线的缓动(2^t); 95 * Circular:圆形曲线的缓动(sqrt(1-t^2)); 96 * Elastic:指数衰减的正弦曲线缓动; 97 * Back:超过范围的三次方缓动((s+1)*t^3 - s*t^2); 98 * Bounce:指数衰减的反弹缓动。 99 * 100 * 每个效果都分三个缓动方式(方法),分别是: 101 * easeIn:从0开始加速的缓动; 102 * easeOut:减速到0的缓动; 103 * easeInOut:前半段从0开始加速,后半段减速到0的缓动。 104 * 其中Linear是无缓动效果,没有以上效果。 105 * 106 * p,pos: current(当前); 107 * x: value(其他参数); 108 * 109 * @memberOf fx 110 * @namespace 111 * @name transitions 112 */ 113 var transitions = { 114 /** 115 * linear:无缓动效果; 116 * @memberOf fx.transitions 117 * @function 118 * @name linear 119 */ 120 linear: function(p){ 121 return p; 122 }, 123 extend : function(newTransitions){ 124 for (var transition in newTransitions){ 125 this[transition] = new Transition(newTransitions[transition]); 126 } 127 } 128 129 }; 130 131 132 133 134 transitions.extend( 135 /** 136 * @lends fx.transitions 137 */ 138 { 139 140 /** 141 * pow:n次方的缓动(t^n),n默认为6; 142 */ 143 pow: function(p, x){ 144 return Math.pow(p, x && x[0] || 6); 145 }, 146 147 /** 148 * exponential:指数曲线的缓动(2^t); 149 */ 150 exponential: function(p){ 151 return Math.pow(2, 8 * (p - 1)); 152 }, 153 154 /** 155 * circular:圆形曲线的缓动(sqrt(1-t^2)); 156 */ 157 circular: function(p){ 158 return 1 - Math.sin(Math.acos(p)); 159 }, 160 161 /** 162 * sinusoidal:正弦曲线的缓动(sin(t)); 163 */ 164 sinusoidal: function(p){ 165 return 1 - Math.sin((1 - p) * Math.PI / 2); 166 }, 167 168 /** 169 * back:超过范围的三次方缓动((s+1)*t^3 - s*t^2); 170 */ 171 back: function(p, x){ 172 x = x && x[0] || 1.618; 173 return Math.pow(p, 2) * ((x + 1) * p - x); 174 }, 175 176 /** 177 * bounce:指数衰减的反弹缓动。 178 */ 179 bounce: function(p){ 180 var value; 181 for (var a = 0, b = 1; 1; a += b, b /= 2){ 182 if (p >= (7 - 4 * a) / 11){ 183 value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2); 184 break; 185 } 186 } 187 return value; 188 }, 189 190 /** 191 * elastic:指数衰减的正弦曲线缓动; 192 */ 193 elastic: function(p, x){ 194 return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3); 195 } 196 197 }); 198 199 // quadratic,cubic,quartic,quintic:2-5次方的缓动(t^5); 200 var pows = ['quadratic', 'cubic', 'quartic', 'quintic']; 201 J.array.forEach(pows, function(transition, i){ 202 transitions[transition] = new Transition(function(p){ 203 return Math.pow(p, [i + 2]); 204 }); 205 }); 206 207 208 209 /** 210 * 节拍器类 211 * @class 212 * @memberOf fx 213 * @name Beater 214 * @constructor 215 * 216 * @param {Number} duration: 节拍时长,单位毫秒 217 * @param {Number} loop: 循环次数,默认为1,0为无限循环 218 * @param {Number} fps: fps你懂的 219 * @return 节拍器实例 220 */ 221 var Beater = new J.Class( 222 /** 223 * @lends fx.Beater.prototype 224 */ 225 { 226 /** 227 * @ignore 228 */ 229 init : function(option){ 230 231 this.setOption(option); 232 }, 233 234 setOption: function(option){ 235 this.option = option = J.extend({ 236 duration:500, 237 loop:1, 238 //不建议fps超过62,因为浏览器setInterval方法的限制,最小时间间隔是16ms,所以每秒实际帧速不能超过62帧 239 fps:1000/(option && option.duration || 500) 240 }, option); 241 }, 242 243 start: function(){ 244 if (!this.check()){ 245 return this; 246 } 247 var option = this.option; 248 this.time = 0; 249 this.loop = option.loop; 250 this.onStart.apply(this, arguments); 251 this.startTimer(); 252 return this; 253 }, 254 255 pause: function(){ 256 this.stopTimer(); 257 return this; 258 }, 259 260 resume: function(){ 261 this.startTimer(); 262 return this; 263 }, 264 265 end: function(){ 266 if (this.stopTimer()){ 267 this.onEnd.apply(this, arguments); 268 } 269 return this; 270 }, 271 272 cancel: function(){ 273 if (this.stopTimer()){ 274 this.onCancel.apply(this, arguments); 275 } 276 return this; 277 }, 278 279 onStart: function(){ 280 $E.notifyObservers(this, "start"); 281 }, 282 283 onEnd: function(){ 284 $E.notifyObservers(this, "end"); 285 }, 286 287 onCancel: function(){ 288 $E.notifyObservers(this, "cancel"); 289 }, 290 291 onLoop: function(){ 292 $E.notifyObservers(this, "loop"); 293 }, 294 295 onBeater: function(){ 296 $E.notifyObservers(this, "beater"); 297 }, 298 299 check: function(){ 300 if (!this.timer){ 301 return true; 302 } 303 return false; 304 }, 305 306 setDuration: function(duration){ 307 this.option.duration = duration || 500; 308 }, 309 310 update: function(){ 311 var that = this, 312 time = J.now(), 313 option = that.option; 314 if (time < that.time + that.option.duration){ 315 that.onBeater((time - that.time) / option.duration); 316 } else { 317 that.onBeater(1); 318 that.onLoop(); 319 if(option.loop<=0 || --that.loop){ 320 that.time = time; 321 }else{ 322 that.stopTimer(); 323 that.onEnd(); 324 } 325 } 326 }, 327 328 stopTimer: function(){ 329 if (!this.timer){ 330 return false; 331 } 332 this.time = J.now() - this.time; 333 this.timer = removeInstance(this); 334 return true; 335 }, 336 337 startTimer: function(){ 338 if (this.timer) return false; 339 this.time = J.now() - this.time; 340 this.timer = addInstance(this); 341 return true; 342 } 343 }); 344 345 var instances = {}, timers = {}; 346 347 var loop = function(list){ 348 for (var i = list.length; i--;){ 349 if (list[i]){ 350 list[i].update(); 351 } 352 } 353 }; 354 355 var addInstance = function(instance){ 356 var fps = instance.option.fps, 357 list = instances[fps] || (instances[fps] = []); 358 list.push(instance); 359 if (!timers[fps]){ 360 timers[fps] = setInterval(function(){ 361 loop(list); 362 }, Math.round(1000 / fps)); 363 } 364 return true; 365 }; 366 367 var removeInstance = function(instance){ 368 var fps = instance.option.fps, 369 list = instances[fps] || []; 370 J.array.remove(list,instance); 371 if (!list.length && timers[fps]){ 372 timers[fps] = clearInterval(timers[fps]); 373 } 374 return false; 375 }; 376 377 378 379 /** 380 * 动画类 381 * @class 382 * @name Animation 383 * @memberOf fx 384 * @extends fx.Beater 385 * 386 * @param {Element} element 动画的dom 387 * @param {String} property css 属性 388 * @param {Mix} from 起始值 389 * @param {Mix} to 到达值 390 * @param {String} unit 单位 391 * @param {Number} duration 动画时长,单位毫秒 392 * @param {Number} loop 循环次数,默认为1,0为无限循环 393 * @param {Function} transition 变化公式 394 * @param {Number} fps fps你懂的 395 * @param {Function} css属性转换器 396 * @return 动画实例 397 */ 398 var Animation = new J.Class({extend:Beater}, 399 /** 400 * @lends fx.Animation.prototype 401 */ 402 { 403 /** 404 * @ignore 405 */ 406 init : function(option){ 407 Animation.superClass['init'].apply(this,arguments); 408 this.option = this.option || {}; 409 option = J.extend(this.option, { 410 element: null, 411 property: "", 412 from: 0, 413 to: 1, 414 unit:false, 415 transition: transitions.linear, 416 //不建议fps超过62,因为浏览器setInterval方法的限制,最小时间间隔是16ms,所以每秒实际帧速不能超过62帧 417 fps:25, 418 converter:converter 419 }, option); 420 this.from = option.from; 421 this.to = option.to; 422 }, 423 424 getTransition: function(){ 425 var transition = this.option.transition || transitions.sinusoidal.easeInOut; 426 return transition; 427 }, 428 429 set: function(now){ 430 var option = this.option; 431 this.render(option.element, option.property, now, option.unit); 432 return this; 433 }, 434 435 setFromTo: function(from, to){ 436 this.from = from; 437 this.to = to; 438 }, 439 440 render: function(element, property, value, unit){ 441 $D.setStyle(element, property, this.option.converter(value,unit)); 442 }, 443 444 compute: function(from, to, delta){ 445 return compute(from, to, delta); 446 }, 447 448 onStart: function(from,to){ 449 var that = this; 450 var option = that.option; 451 that.from = J.isNumber(from) ? from : option.from; 452 that.to = J.isNumber(to) ? to : option.to; 453 $E.notifyObservers(this, "start"); 454 }, 455 456 onEnd: function(){ 457 var that = this; 458 var delta = that.option.transition(1); 459 that.set(that.compute(that.from, that.to, delta)); 460 $E.notifyObservers(this, "end"); 461 }, 462 463 onCancel: function(){ 464 var that = this; 465 var delta = that.option.transition(0); 466 that.set(that.compute(that.from, that.to, delta)); 467 $E.notifyObservers(this, "cancel"); 468 }, 469 470 onBeater: function(progress){ 471 var that = this; 472 var delta = that.option.transition(progress); 473 that.set(that.compute(that.from, that.to, delta)); 474 } 475 }); 476 477 var compute = function(from, to, delta){ 478 return (to - from) * delta + from; 479 }; 480 481 var converter = function(value,unit){ 482 return (unit) ? value + unit : value; 483 }; 484 485 /** 486 * TODO 这是原来的动画类, peli改了原来的动画类,抽出了节拍器, 487 * 但是新的动画类有问题,但是我没时间改, 就先用着旧的 488 * by az , 2011-3-28 489 * @ignore 490 * 491 * @param {Element} element: 动画的dom 492 * @param {String} property: css 属性 493 * @param {Mix} from: 起始值 494 * @param {Mix} to: 到达值 495 * @param {String} unit: 单位 496 * @param {Number} duration: 动画时长,单位毫秒 497 * @param {Function} transition: 变化公式 498 * @param {Number} fps: fps你懂的 499 * 500 * @return 动画实例 501 */ 502 var Animation2 = new J.Class({ 503 init : function(option){ 504 //el, style, begin, end, fx, total 505 this.option = option = J.extend({ 506 element: null, 507 property: "", 508 from: 0, 509 to: 0, 510 unit:false, 511 duration:500, 512 transition: transitions.linear, 513 //不建议fps超过62,因为浏览器setInterval方法的限制,最小时间间隔是16ms,所以每秒实际帧速不能超过62帧 514 fps:25 515 }, option); 516 517 this.from = option.from; 518 this.to = option.to; 519 520 521 }, 522 523 524 getTransition: function(){ 525 var transition = this.option.transition || transitions.sinusoidal.easeInOut; 526 return transition; 527 }, 528 update: function(){ 529 var that = this, 530 time = J.now(); 531 var option = that.option; 532 if (time < that.time + that.option.duration){ 533 var delta = option.transition((time - that.time) / option.duration); 534 that.set(that.compute(that.from, that.to, delta)); 535 } else { 536 that.set(that.compute(that.from, that.to, 1)); 537 that.end(); 538 539 } 540 }, 541 542 set: function(now){ 543 var option = this.option; 544 this.render(option.element, option.property, now, option.unit); 545 return this; 546 }, 547 548 setDuration: function(duration){ 549 this.option.duration = duration || 500; 550 }, 551 552 setFromTo: function(from, to){ 553 this.from = from; 554 this.to = to; 555 }, 556 557 render: function(element, property, value, unit){ 558 value = (unit) ? value + unit : value; 559 $D.setStyle(element, property, value); 560 }, 561 562 compute: function(from, to, delta){ 563 return compute(from, to, delta); 564 }, 565 566 check: function(){ 567 if (!this.timer){ 568 return true; 569 } 570 return false; 571 }, 572 573 start: function(from, to){ 574 var that = this; 575 if (!that.check(from, to)){ 576 return that; 577 } 578 var option = that.option; 579 that.from = J.isNumber(from) ? from : option.from; 580 that.to = J.isNumber(to) ? to : option.to; 581 582 that.time = 0; 583 that.startTimer(); 584 that.onStart(); 585 586 return that; 587 }, 588 589 end: function(){ 590 if (this.stopTimer()){ 591 this.onEnd(); 592 } 593 return this; 594 }, 595 596 cancel: function(){ 597 if (this.stopTimer()){ 598 this.onCancel(); 599 } 600 return this; 601 }, 602 603 onStart: function(){ 604 $E.notifyObservers(this, "start"); 605 }, 606 607 onEnd: function(){ 608 $E.notifyObservers(this, "end"); 609 }, 610 611 onCancel: function(){ 612 $E.notifyObservers(this, "cancel"); 613 }, 614 615 pause: function(){ 616 this.stopTimer(); 617 return this; 618 }, 619 620 resume: function(){ 621 this.startTimer(); 622 return this; 623 }, 624 625 stopTimer: function(){ 626 if (!this.timer){ 627 return false; 628 } 629 this.time = J.now() - this.time; 630 this.timer = removeInstance(this); 631 return true; 632 }, 633 634 startTimer: function(){ 635 if (this.timer) return false; 636 this.time = J.now() - this.time; 637 this.timer = addInstance(this); 638 return true; 639 } 640 }); 641 642 J.fx.Beater = Beater; 643 J.fx.Animation = Animation; 644 J.fx.Animation2 = Animation2; 645 J.fx.transitions = transitions; 646 }); 647 648 649 650 651 652 653 654 655 656 657 658 659 660