218 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Vue
		
	
	
<template>
 | 
						|
  <view class="ui-fixed">
 | 
						|
    <view
 | 
						|
      class="ui-fixed-box"
 | 
						|
      :id="`fixed-${uuid}`"
 | 
						|
      :class="[{ fixed: state.fixed }]"
 | 
						|
      :style="[
 | 
						|
        {
 | 
						|
          left: sticky ? 'auto' : '0px',
 | 
						|
          top: state.fixed && !bottom ? (noNav ? val : val + sys_navBar) + 'px' : 'auto',
 | 
						|
          bottom: insetHeight,
 | 
						|
          zIndex: index + sheep.$zIndex.navbar,
 | 
						|
        },
 | 
						|
        !alway ? { opacity: state.opacityVal } : '',
 | 
						|
      ]"
 | 
						|
    >
 | 
						|
      <view
 | 
						|
        class="ui-fixed-content"
 | 
						|
        @tap="toTop"
 | 
						|
        :style="[{ zIndex: index + sheep.$zIndex.navbar }]"
 | 
						|
      >
 | 
						|
        <slot></slot>
 | 
						|
        <view
 | 
						|
          v-if="safeAreaInsets.bottom && bottom && isInset"
 | 
						|
          class="inset-bottom"
 | 
						|
          :style="[{ height: safeAreaInsets.bottom + 'px' }]"
 | 
						|
        ></view>
 | 
						|
      </view>
 | 
						|
      <view class="ui-fixed-bottom" :class="[bg]" v-if="bottom"></view>
 | 
						|
      <view
 | 
						|
        class="ui-fixed-bg"
 | 
						|
        :class="[ui, bg]"
 | 
						|
        :style="[
 | 
						|
          { zIndex: index + sheep.$zIndex.navbar - 1 },
 | 
						|
          bgStyles,
 | 
						|
          opacity ? { opacity: state.opacityVal } : '',
 | 
						|
        ]"
 | 
						|
      ></view>
 | 
						|
    </view>
 | 
						|
    <view
 | 
						|
      class="skeleton"
 | 
						|
      :style="[{ height: state.content.height + 'px', width: width + 'px' }]"
 | 
						|
      v-if="sticky ? state.fixed : placeholder && state.fixed"
 | 
						|
    ></view>
 | 
						|
  </view>
 | 
						|
</template>
 | 
						|
 | 
						|
<script setup>
 | 
						|
  import { onPageScroll } from '@dcloudio/uni-app';
 | 
						|
  import { getCurrentInstance, unref, onMounted, reactive, nextTick, computed } from 'vue';
 | 
						|
  import sheep from '@/sheep';
 | 
						|
  const { safeAreaInsets } = sheep.$platform.device;
 | 
						|
 | 
						|
  const vm = getCurrentInstance();
 | 
						|
 | 
						|
  const uuid = sheep.$helper.guid();
 | 
						|
  const sys_navBar = sheep.$platform.navbar;
 | 
						|
  const state = reactive({
 | 
						|
    content: {},
 | 
						|
    fixed: true,
 | 
						|
    scrollTop: 0,
 | 
						|
    opacityVal: 0,
 | 
						|
  });
 | 
						|
  const insetHeight = computed(() => {
 | 
						|
    if (state.fixed && props.bottom) {
 | 
						|
      if (props.isInset) {
 | 
						|
        return props.val + 'px';
 | 
						|
      } else {
 | 
						|
        return props.val + safeAreaInsets.bottom + 'px';
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      return 'auto';
 | 
						|
    }
 | 
						|
  });
 | 
						|
  const props = defineProps({
 | 
						|
    noNav: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
    bottom: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
    bg: {
 | 
						|
      type: String,
 | 
						|
      default: '',
 | 
						|
    },
 | 
						|
    bgStyles: {
 | 
						|
      type: Object,
 | 
						|
      default() {},
 | 
						|
    },
 | 
						|
    val: {
 | 
						|
      type: Number,
 | 
						|
      default: 0,
 | 
						|
    },
 | 
						|
    width: {
 | 
						|
      type: [String, Number],
 | 
						|
      default: 0,
 | 
						|
    },
 | 
						|
    alway: {
 | 
						|
      type: Boolean,
 | 
						|
      default: true,
 | 
						|
    },
 | 
						|
    opacity: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
    index: {
 | 
						|
      type: [Number, String],
 | 
						|
      default: 0,
 | 
						|
    },
 | 
						|
    placeholder: {
 | 
						|
      type: [Boolean],
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
    sticky: {
 | 
						|
      type: [Boolean],
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
    noFixed: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
    ui: {
 | 
						|
      type: String,
 | 
						|
      default: '',
 | 
						|
    },
 | 
						|
    clickTo: {
 | 
						|
      type: Boolean,
 | 
						|
      default: false,
 | 
						|
    },
 | 
						|
    //是否需要安全区
 | 
						|
    isInset: {
 | 
						|
      type: Boolean,
 | 
						|
      default: true,
 | 
						|
    },
 | 
						|
  });
 | 
						|
 | 
						|
  state.fixed = !unref(props.sticky);
 | 
						|
  onPageScroll((e) => {
 | 
						|
    let top = e.scrollTop;
 | 
						|
    state.scrollTop = top;
 | 
						|
    state.opacityVal = top > sheep.$platform.navbar ? 1 : top * 0.01;
 | 
						|
  });
 | 
						|
 | 
						|
  onMounted(() => {
 | 
						|
    nextTick(() => {
 | 
						|
      computedQuery();
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  const computedQuery = () => {
 | 
						|
    uni.createSelectorQuery()
 | 
						|
      .in(vm)
 | 
						|
      .select(`#fixed-${uuid}`)
 | 
						|
      .boundingClientRect((data) => {
 | 
						|
        if (data != null) {
 | 
						|
          state.content = data;
 | 
						|
          if (unref(props.sticky)) {
 | 
						|
            setFixed(state.scrollTop);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      })
 | 
						|
      .exec();
 | 
						|
  };
 | 
						|
 | 
						|
  const setFixed = (value) => {
 | 
						|
    if (unref(props.bottom)) {
 | 
						|
      state.fixed =
 | 
						|
        value >=
 | 
						|
        state.content.bottom -
 | 
						|
          sheep.$platform.device.windowHeight +
 | 
						|
          state.content.height +
 | 
						|
          unref(props.val);
 | 
						|
    } else {
 | 
						|
      state.fixed =
 | 
						|
        value >=
 | 
						|
        state.content.top -
 | 
						|
          (unref(props.noNav) ? unref(props.val) : unref(props.val) + sheep.$platform.navbar);
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  const toTop = () => {
 | 
						|
    if (props.hasToTop) {
 | 
						|
      uni.pageScrollTo({
 | 
						|
        scrollTop: state.content.top,
 | 
						|
        duration: 100,
 | 
						|
      });
 | 
						|
    }
 | 
						|
  };
 | 
						|
</script>
 | 
						|
 | 
						|
<style lang="scss">
 | 
						|
  .ui-fixed {
 | 
						|
    .ui-fixed-box {
 | 
						|
      position: relative;
 | 
						|
      width: 100%;
 | 
						|
      &.fixed {
 | 
						|
        position: fixed;
 | 
						|
      }
 | 
						|
      .ui-fixed-content {
 | 
						|
        position: relative;
 | 
						|
      }
 | 
						|
      .ui-fixed-bg {
 | 
						|
        position: absolute;
 | 
						|
        width: 100%;
 | 
						|
        height: 100%;
 | 
						|
        top: 0;
 | 
						|
        z-index: 1;
 | 
						|
        pointer-events: none;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  .inset-bottom {
 | 
						|
    background: #fff;
 | 
						|
  }
 | 
						|
</style>
 |