scroll.mjs 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // Utilities
  2. import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
  3. import { clamp, consoleWarn, propsFactory } from "../util/index.mjs"; // Types
  4. // Composables
  5. export const makeScrollProps = propsFactory({
  6. scrollTarget: {
  7. type: String
  8. },
  9. scrollThreshold: {
  10. type: [String, Number],
  11. default: 300
  12. }
  13. }, 'scroll');
  14. export function useScroll(props) {
  15. let args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  16. const {
  17. canScroll
  18. } = args;
  19. let previousScroll = 0;
  20. const target = ref(null);
  21. const currentScroll = shallowRef(0);
  22. const savedScroll = shallowRef(0);
  23. const currentThreshold = shallowRef(0);
  24. const isScrollActive = shallowRef(false);
  25. const isScrollingUp = shallowRef(false);
  26. const scrollThreshold = computed(() => {
  27. return Number(props.scrollThreshold);
  28. });
  29. /**
  30. * 1: at top
  31. * 0: at threshold
  32. */
  33. const scrollRatio = computed(() => {
  34. return clamp((scrollThreshold.value - currentScroll.value) / scrollThreshold.value || 0);
  35. });
  36. const onScroll = () => {
  37. const targetEl = target.value;
  38. if (!targetEl || canScroll && !canScroll.value) return;
  39. previousScroll = currentScroll.value;
  40. currentScroll.value = 'window' in targetEl ? targetEl.pageYOffset : targetEl.scrollTop;
  41. isScrollingUp.value = currentScroll.value < previousScroll;
  42. currentThreshold.value = Math.abs(currentScroll.value - scrollThreshold.value);
  43. };
  44. watch(isScrollingUp, () => {
  45. savedScroll.value = savedScroll.value || currentScroll.value;
  46. });
  47. watch(isScrollActive, () => {
  48. savedScroll.value = 0;
  49. });
  50. onMounted(() => {
  51. watch(() => props.scrollTarget, scrollTarget => {
  52. const newTarget = scrollTarget ? document.querySelector(scrollTarget) : window;
  53. if (!newTarget) {
  54. consoleWarn(`Unable to locate element with identifier ${scrollTarget}`);
  55. return;
  56. }
  57. if (newTarget === target.value) return;
  58. target.value?.removeEventListener('scroll', onScroll);
  59. target.value = newTarget;
  60. target.value.addEventListener('scroll', onScroll, {
  61. passive: true
  62. });
  63. }, {
  64. immediate: true
  65. });
  66. });
  67. onBeforeUnmount(() => {
  68. target.value?.removeEventListener('scroll', onScroll);
  69. });
  70. // Do we need this? If yes - seems that
  71. // there's no need to expose onScroll
  72. canScroll && watch(canScroll, onScroll, {
  73. immediate: true
  74. });
  75. return {
  76. scrollThreshold,
  77. currentScroll,
  78. currentThreshold,
  79. isScrollActive,
  80. scrollRatio,
  81. // required only for testing
  82. // probably can be removed
  83. // later (2 chars chlng)
  84. isScrollingUp,
  85. savedScroll
  86. };
  87. }
  88. //# sourceMappingURL=scroll.mjs.map