scroll.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import BezierEasing from 'bezier-easing'
  2. let _ = {
  3. $ (selector) {
  4. return document.querySelector(selector)
  5. },
  6. on (element, events, handler) {
  7. if (!(events instanceof Array)) {
  8. events = [events]
  9. }
  10. for (let i = 0; i < events.length; i++) {
  11. element.addEventListener(events[i], handler)
  12. }
  13. },
  14. off (element, events, handler) {
  15. if (!(events instanceof Array)) {
  16. events = [events]
  17. }
  18. for (let i = 0; i < events.length; i++) {
  19. element.removeEventListener(events[i], handler)
  20. }
  21. }
  22. }
  23. let eases = {
  24. ease: [0.25, 0.1, 0.25, 1.0],
  25. linear: [0.00, 0.0, 1.00, 1.0],
  26. 'ease-in': [0.42, 0.0, 1.00, 1.0],
  27. 'ease-out': [0.00, 0.0, 0.58, 1.0],
  28. 'ease-in-out': [0.42, 0.0, 0.58, 1.0]
  29. }
  30. export const scrollTo = (element, duration = 500, options) => {
  31. options = options || {}
  32. options.easing = eases[options.easing || 'ease']
  33. if (typeof element === 'string') {
  34. element = _.$(element)
  35. }
  36. let page = _.$('html, body')
  37. let events = [
  38. 'scroll',
  39. 'mousedown',
  40. 'wheel',
  41. 'DOMMouseScroll',
  42. 'mousewheel',
  43. 'keyup',
  44. 'touchmove'
  45. ]
  46. let abort = false
  47. let abortFn = () => {
  48. abort = true
  49. }
  50. _.on(page, events, abortFn)
  51. let initialY = window.pageYOffset
  52. let elementY = 0
  53. if (Object.is(typeof element, 'number')) {
  54. elementY = element
  55. } else {
  56. elementY = initialY + element.getBoundingClientRect().top + (options.offset || 0)
  57. }
  58. let targetY = document.body.scrollHeight - elementY < window.innerHeight
  59. ? document.body.scrollHeight - window.innerHeight
  60. : elementY
  61. if (options.offset) {
  62. targetY += options.offset
  63. }
  64. let diff = targetY - initialY
  65. let easing = BezierEasing.apply(BezierEasing, options.easing)
  66. let start
  67. let done = () => {
  68. _.off(page, events, abortFn)
  69. if (abort && options.onCancel) options.onCancel()
  70. if (!abort && options.onDone) options.onDone()
  71. }
  72. if (!diff) return
  73. window.requestAnimationFrame(function step (timestamp) {
  74. if (abort) return done()
  75. if (!start) start = timestamp
  76. let time = timestamp - start
  77. let progress = Math.min(time / duration, 1)
  78. progress = easing(progress)
  79. window.scrollTo(0, initialY + diff * progress)
  80. if (time < duration) {
  81. window.requestAnimationFrame(step)
  82. } else {
  83. done()
  84. }
  85. })
  86. }