Scroll.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { Components, Options, RequestIntervalInterface, ScrollComponent } from '@splidejs/splide';
  2. import { EVENT_MOVE, EVENT_REFRESH, EVENT_SCROLL, EVENT_SCROLLED, EVENT_UPDATED } from '../../constants/events';
  3. import { SLIDE } from '../../constants/types';
  4. import { EventInterface, RequestInterval } from '../../constructors';
  5. import { Splide } from '../../core/Splide/Splide';
  6. import { abs, max } from '../../utils';
  7. import { BASE_VELOCITY, BOUNCE_DIFF_THRESHOLD, BOUNCE_DURATION, FRICTION_FACTOR, MIN_DURATION } from './constants';
  8. /**
  9. * The component for scrolling the slider.
  10. *
  11. * @since 3.0.0
  12. *
  13. * @param Splide - A Splide instance.
  14. * @param Components - A collection of components.
  15. * @param options - Options.
  16. *
  17. * @return A Scroll component object.
  18. */
  19. export function Scroll( Splide: Splide, Components: Components, options: Options ): ScrollComponent {
  20. const { on, emit } = EventInterface( Splide );
  21. const { Move } = Components;
  22. const { getPosition, getLimit } = Move;
  23. /**
  24. * Retains the active RequestInterval object.
  25. */
  26. let interval: RequestIntervalInterface;
  27. /**
  28. * Called when the component is mounted.
  29. */
  30. function mount(): void {
  31. on( EVENT_MOVE, clear );
  32. on( [ EVENT_UPDATED, EVENT_REFRESH ], cancel );
  33. }
  34. /**
  35. * Scrolls the slider to the provided destination.
  36. *
  37. * @param destination - The destination to scroll to.
  38. * @param duration - Optional. The scroll duration. If omitted, calculates it by the distance.
  39. * @param suppressConstraint - Optional. Whether to suppress constraint process when the slider exceeds bounds.
  40. */
  41. function scroll( destination: number, duration?: number, suppressConstraint?: boolean ): void {
  42. const start = getPosition();
  43. let friction = 1;
  44. duration = duration || computeDuration( abs( destination - start ) );
  45. clear();
  46. interval = RequestInterval( duration, onScrolled, rate => {
  47. const position = getPosition();
  48. const target = start + ( destination - start ) * easing( rate );
  49. const diff = ( target - getPosition() ) * friction;
  50. Move.translate( position + diff );
  51. if ( Splide.is( SLIDE ) && ! suppressConstraint && Move.isExceeded() ) {
  52. friction *= FRICTION_FACTOR;
  53. if ( abs( diff ) < BOUNCE_DIFF_THRESHOLD ) {
  54. bounce( Move.isExceededMin( getPosition() ) );
  55. }
  56. }
  57. }, 1 );
  58. emit( EVENT_SCROLL );
  59. interval.start();
  60. }
  61. /**
  62. * Triggers the bounce effect when the slider reaches bounds.
  63. *
  64. * @param backwards - The direction the slider is going towards.
  65. */
  66. function bounce( backwards: boolean ): void {
  67. scroll( getLimit( ! backwards ), BOUNCE_DURATION, true );
  68. }
  69. /**
  70. * Called when scroll ends or is canceled.
  71. */
  72. function onScrolled(): void {
  73. emit( EVENT_SCROLLED );
  74. }
  75. /**
  76. * Computes the scroll duration by the distance and the base velocity.
  77. *
  78. * @param distance - Distance in pixel.
  79. *
  80. * @return The duration for scroll.
  81. */
  82. function computeDuration( distance: number ): number {
  83. return max( distance / BASE_VELOCITY, MIN_DURATION );
  84. }
  85. /**
  86. * Clears the active interval.
  87. */
  88. function clear(): void {
  89. if ( interval ) {
  90. interval.cancel();
  91. }
  92. }
  93. /**
  94. * Cancels the active interval and emits the `scrolled` event.
  95. */
  96. function cancel(): void {
  97. if ( interval && ! interval.isPaused() ) {
  98. clear();
  99. onScrolled();
  100. }
  101. }
  102. /**
  103. * The easing function.
  104. *
  105. * @param t - A value to ease.
  106. *
  107. * @return An eased value.
  108. */
  109. function easing( t: number ): number {
  110. const { easingFunc } = options;
  111. return easingFunc ? easingFunc( t ) : 1 - Math.pow( 1 - t, 4 );
  112. }
  113. return {
  114. mount,
  115. destroy: clear,
  116. scroll,
  117. cancel,
  118. };
  119. }