Clip.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /**
  2. * 动画主控制器
  3. * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件
  4. * @config life(1000) 动画时长
  5. * @config delay(0) 动画延迟时间
  6. * @config loop(true)
  7. * @config onframe
  8. * @config easing(optional)
  9. * @config ondestroy(optional)
  10. * @config onrestart(optional)
  11. *
  12. * TODO pause
  13. */
  14. import easingFuncs, {AnimationEasing} from './easing';
  15. import type Animation from './Animation';
  16. import { isFunction, noop } from '../core/util';
  17. import { createCubicEasingFunc } from './cubicEasing';
  18. type OnframeCallback = (percent: number) => void;
  19. type ondestroyCallback = () => void
  20. type onrestartCallback = () => void
  21. export type DeferredEventTypes = 'destroy' | 'restart'
  22. // type DeferredEventKeys = 'ondestroy' | 'onrestart'
  23. export interface ClipProps {
  24. life?: number
  25. delay?: number
  26. loop?: boolean
  27. easing?: AnimationEasing
  28. onframe?: OnframeCallback
  29. ondestroy?: ondestroyCallback
  30. onrestart?: onrestartCallback
  31. }
  32. export default class Clip {
  33. private _life: number
  34. private _delay: number
  35. private _inited: boolean = false
  36. private _startTime = 0 // 开始时间单位毫秒
  37. private _pausedTime = 0
  38. private _paused = false
  39. animation: Animation
  40. loop: boolean
  41. easing: AnimationEasing
  42. easingFunc: (p: number) => number
  43. // For linked list. Readonly
  44. next: Clip
  45. prev: Clip
  46. onframe: OnframeCallback
  47. ondestroy: ondestroyCallback
  48. onrestart: onrestartCallback
  49. constructor(opts: ClipProps) {
  50. this._life = opts.life || 1000;
  51. this._delay = opts.delay || 0;
  52. this.loop = opts.loop || false;
  53. this.onframe = opts.onframe || noop;
  54. this.ondestroy = opts.ondestroy || noop;
  55. this.onrestart = opts.onrestart || noop;
  56. opts.easing && this.setEasing(opts.easing);
  57. }
  58. step(globalTime: number, deltaTime: number): boolean {
  59. // Set startTime on first step, or _startTime may has milleseconds different between clips
  60. // PENDING
  61. if (!this._inited) {
  62. this._startTime = globalTime + this._delay;
  63. this._inited = true;
  64. }
  65. if (this._paused) {
  66. this._pausedTime += deltaTime;
  67. return;
  68. }
  69. const life = this._life;
  70. let elapsedTime = globalTime - this._startTime - this._pausedTime;
  71. let percent = elapsedTime / life;
  72. // PENDING: Not begin yet. Still run the loop.
  73. // In the case callback needs to be invoked.
  74. // Or want to update to the begin state at next frame when `setToFinal` and `delay` are both used.
  75. // To avoid the unexpected blink.
  76. if (percent < 0) {
  77. percent = 0;
  78. }
  79. percent = Math.min(percent, 1);
  80. const easingFunc = this.easingFunc;
  81. const schedule = easingFunc ? easingFunc(percent) : percent;
  82. this.onframe(schedule);
  83. // 结束
  84. if (percent === 1) {
  85. if (this.loop) {
  86. // Restart
  87. const remainder = elapsedTime % life;
  88. this._startTime = globalTime - remainder;
  89. this._pausedTime = 0;
  90. this.onrestart();
  91. }
  92. else {
  93. return true;
  94. }
  95. }
  96. return false;
  97. }
  98. pause() {
  99. this._paused = true;
  100. }
  101. resume() {
  102. this._paused = false;
  103. }
  104. setEasing(easing: AnimationEasing) {
  105. this.easing = easing;
  106. this.easingFunc = isFunction(easing)
  107. ? easing
  108. : easingFuncs[easing] || createCubicEasingFunc(easing);
  109. }
  110. }