Sticky.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import { createVNode as _createVNode } from "vue";
  2. import { ref, watch, computed, reactive, defineComponent } from "vue";
  3. import { extend, isHidden, unitToPx, numericProp, getScrollTop, getZIndexStyle, makeStringProp, makeNumericProp, createNamespace } from "../utils/index.mjs";
  4. import { useRect, useEventListener, useScrollParent } from "@vant/use";
  5. import { useVisibilityChange } from "../composables/use-visibility-change.mjs";
  6. const [name, bem] = createNamespace("sticky");
  7. const stickyProps = {
  8. zIndex: numericProp,
  9. position: makeStringProp("top"),
  10. container: Object,
  11. offsetTop: makeNumericProp(0),
  12. offsetBottom: makeNumericProp(0)
  13. };
  14. var stdin_default = defineComponent({
  15. name,
  16. props: stickyProps,
  17. emits: ["scroll", "change"],
  18. setup(props, {
  19. emit,
  20. slots
  21. }) {
  22. const root = ref();
  23. const scrollParent = useScrollParent(root);
  24. const state = reactive({
  25. fixed: false,
  26. width: 0,
  27. height: 0,
  28. transform: 0
  29. });
  30. const offset = computed(() => unitToPx(props.position === "top" ? props.offsetTop : props.offsetBottom));
  31. const rootStyle = computed(() => {
  32. const {
  33. fixed,
  34. height,
  35. width
  36. } = state;
  37. if (fixed) {
  38. return {
  39. width: `${width}px`,
  40. height: `${height}px`
  41. };
  42. }
  43. });
  44. const stickyStyle = computed(() => {
  45. if (!state.fixed) {
  46. return;
  47. }
  48. const style = extend(getZIndexStyle(props.zIndex), {
  49. width: `${state.width}px`,
  50. height: `${state.height}px`,
  51. [props.position]: `${offset.value}px`
  52. });
  53. if (state.transform) {
  54. style.transform = `translate3d(0, ${state.transform}px, 0)`;
  55. }
  56. return style;
  57. });
  58. const emitScroll = (scrollTop) => emit("scroll", {
  59. scrollTop,
  60. isFixed: state.fixed
  61. });
  62. const onScroll = () => {
  63. if (!root.value || isHidden(root)) {
  64. return;
  65. }
  66. const {
  67. container,
  68. position
  69. } = props;
  70. const rootRect = useRect(root);
  71. const scrollTop = getScrollTop(window);
  72. state.width = rootRect.width;
  73. state.height = rootRect.height;
  74. if (position === "top") {
  75. if (container) {
  76. const containerRect = useRect(container);
  77. const difference = containerRect.bottom - offset.value - state.height;
  78. state.fixed = offset.value > rootRect.top && containerRect.bottom > 0;
  79. state.transform = difference < 0 ? difference : 0;
  80. } else {
  81. state.fixed = offset.value > rootRect.top;
  82. }
  83. } else {
  84. const {
  85. clientHeight
  86. } = document.documentElement;
  87. if (container) {
  88. const containerRect = useRect(container);
  89. const difference = clientHeight - containerRect.top - offset.value - state.height;
  90. state.fixed = clientHeight - offset.value < rootRect.bottom && clientHeight > containerRect.top;
  91. state.transform = difference < 0 ? -difference : 0;
  92. } else {
  93. state.fixed = clientHeight - offset.value < rootRect.bottom;
  94. }
  95. }
  96. emitScroll(scrollTop);
  97. };
  98. watch(() => state.fixed, (value) => emit("change", value));
  99. useEventListener("scroll", onScroll, {
  100. target: scrollParent,
  101. passive: true
  102. });
  103. useVisibilityChange(root, onScroll);
  104. return () => {
  105. var _a;
  106. return _createVNode("div", {
  107. "ref": root,
  108. "style": rootStyle.value
  109. }, [_createVNode("div", {
  110. "class": bem({
  111. fixed: state.fixed
  112. }),
  113. "style": stickyStyle.value
  114. }, [(_a = slots.default) == null ? void 0 : _a.call(slots)])]);
  115. };
  116. }
  117. });
  118. export {
  119. stdin_default as default
  120. };