index.vue 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. <template>
  2. <div class="sticky" :class="{ stickyed: data.stickyed }" :style="{ top: props.top }">
  3. <slot />
  4. </div>
  5. </template>
  6. <script lang="ts" setup>
  7. type stickyResult = UniApp.ObserveResult & { stickyed?: boolean }
  8. const props = withDefaults(
  9. defineProps<{
  10. top?: string
  11. relativeTo?: string
  12. targetSelector?: string
  13. targetComponent?: any
  14. }>(),
  15. {
  16. top: '',
  17. },
  18. )
  19. const emit = defineEmits<{ (e: 'stickChange', sr: stickyResult): void }>()
  20. const data = reactive({
  21. stickyed: false,
  22. })
  23. onMounted(() => {
  24. // #ifdef MP
  25. if (props.relativeTo && props.targetSelector) {
  26. const iob = uni.createIntersectionObserver(
  27. props.targetComponent || getCurrentInstance().parent.parent,
  28. )
  29. iob.relativeTo(props.relativeTo).observe(props.targetSelector, (res) => {
  30. const sr: stickyResult = res
  31. console.log(res.intersectionRatio)
  32. sr.stickyed = res.intersectionRatio > 0
  33. data.stickyed = sr.stickyed
  34. emit('stickChange', sr)
  35. })
  36. }
  37. // #endif
  38. })
  39. </script>
  40. <style lang="scss" scoped>
  41. .sticky {
  42. // 防止顶部镂空
  43. top: -1rpx;
  44. z-index: 1;
  45. // 防止部分真机误判相交状态
  46. margin-bottom: 1px;
  47. background-color: $bg-color;
  48. }
  49. .stickyed {
  50. border-bottom: 1rpx solid $border-color;
  51. @extend %box-shadow;
  52. &::before,
  53. &::after {
  54. position: absolute;
  55. top: 0;
  56. z-index: -1;
  57. width: $p-spac;
  58. height: 100%;
  59. content: '';
  60. background-color: #fff;
  61. border-bottom: 1rpx solid $border-color;
  62. }
  63. &::before {
  64. left: -$p-spac;
  65. }
  66. &::after {
  67. right: -$p-spac;
  68. }
  69. }
  70. </style>