Ben Lin
2024-06-27 dfd2d0218bbcea93b4bfb0e7ca7ac9b1cc1cc7da
src/components/Scrollbar/src/Scrollbar.vue
@@ -3,7 +3,7 @@
    <div
      ref="wrap"
      :class="[wrapClass, 'scrollbar__wrap', native ? '' : 'scrollbar__wrap--hidden-default']"
      :style="style"
      :style="wrapStyle"
      @scroll="handleScroll"
    >
      <component :is="tag" ref="resize" :class="['scrollbar__view', viewClass]" :style="viewStyle">
@@ -16,120 +16,112 @@
    </template>
  </div>
</template>
<script lang="ts">
  import { addResizeListener, removeResizeListener } from '/@/utils/event';
  import componentSetting from '/@/settings/componentSetting';
  import { toObject } from './util';
<script lang="ts" setup>
  import {
    defineComponent,
    ref,
    onMounted,
    onBeforeUnmount,
    nextTick,
    provide,
    computed,
    unref,
    watch,
    type PropType,
  } from 'vue';
  import type { StyleValue } from '@/utils/types';
  import { addResizeListener, removeResizeListener } from '@/utils/event';
  import componentSetting from '@/settings/componentSetting';
  import Bar from './bar';
  const { scrollbar } = componentSetting;
  defineOptions({ name: 'Scrollbar' });
  export default defineComponent({
    name: 'Scrollbar',
    // inheritAttrs: false,
    components: { Bar },
    props: {
      native: {
        type: Boolean,
        default: scrollbar?.native ?? false,
      },
      wrapStyle: {
        type: [String, Array],
        default: '',
      },
      wrapClass: {
        type: [String, Array],
        default: '',
      },
      viewClass: {
        type: [String, Array],
        default: '',
      },
      viewStyle: {
        type: [String, Array],
        default: '',
      },
      noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能
      tag: {
        type: String,
        default: 'div',
      },
  const props = defineProps({
    native: {
      type: Boolean,
      default: componentSetting.scrollbar?.native ?? false,
    },
    setup(props) {
      const sizeWidth = ref('0');
      const sizeHeight = ref('0');
      const moveX = ref(0);
      const moveY = ref(0);
      const wrap = ref();
      const resize = ref();
      provide('scroll-bar-wrap', wrap);
      const style = computed(() => {
        if (Array.isArray(props.wrapStyle)) {
          return toObject(props.wrapStyle);
        }
        return props.wrapStyle;
      });
      const handleScroll = () => {
        if (!props.native) {
          moveY.value = (unref(wrap).scrollTop * 100) / unref(wrap).clientHeight;
          moveX.value = (unref(wrap).scrollLeft * 100) / unref(wrap).clientWidth;
        }
      };
      const update = () => {
        if (!unref(wrap)) return;
        const heightPercentage = (unref(wrap).clientHeight * 100) / unref(wrap).scrollHeight;
        const widthPercentage = (unref(wrap).clientWidth * 100) / unref(wrap).scrollWidth;
        sizeHeight.value = heightPercentage < 100 ? heightPercentage + '%' : '';
        sizeWidth.value = widthPercentage < 100 ? widthPercentage + '%' : '';
      };
      onMounted(() => {
        if (props.native) return;
        nextTick(update);
        if (!props.noresize) {
          addResizeListener(unref(resize), update);
          addResizeListener(unref(wrap), update);
          addEventListener('resize', update);
        }
      });
      onBeforeUnmount(() => {
        if (props.native) return;
        if (!props.noresize) {
          removeResizeListener(unref(resize), update);
          removeResizeListener(unref(wrap), update);
          removeEventListener('resize', update);
        }
      });
      return {
        moveX,
        moveY,
        sizeWidth,
        sizeHeight,
        style,
        wrap,
        resize,
        update,
        handleScroll,
      };
    wrapStyle: {
      type: [String, Array, Object] as PropType<StyleValue>,
      default: '',
    },
    wrapClass: {
      type: [String, Array],
      default: '',
    },
    viewClass: {
      type: [String, Array],
      default: '',
    },
    viewStyle: {
      type: [String, Array],
      default: '',
    },
    noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能
    tag: {
      type: String,
      default: 'div',
    },
    scrollHeight: {
      // 用于监控内部scrollHeight的变化
      type: Number,
      default: 0,
    },
  });
  const sizeWidth = ref('0');
  const sizeHeight = ref('0');
  const moveX = ref(0);
  const moveY = ref(0);
  const wrap = ref();
  const resize = ref();
  provide('scroll-bar-wrap', wrap);
  const handleScroll = () => {
    if (!props.native) {
      moveY.value = (unref(wrap).scrollTop * 100) / unref(wrap).clientHeight;
      moveX.value = (unref(wrap).scrollLeft * 100) / unref(wrap).clientWidth;
    }
  };
  const update = () => {
    if (!unref(wrap)) return;
    const heightPercentage = (unref(wrap).clientHeight * 100) / unref(wrap).scrollHeight;
    const widthPercentage = (unref(wrap).clientWidth * 100) / unref(wrap).scrollWidth;
    sizeHeight.value = heightPercentage < 100 ? heightPercentage + '%' : '';
    sizeWidth.value = widthPercentage < 100 ? widthPercentage + '%' : '';
  };
  watch(
    () => props.scrollHeight,
    () => {
      if (props.native) return;
      update();
    },
  );
  defineExpose({
    wrap,
  });
  onMounted(() => {
    if (props.native) return;
    nextTick(update);
    if (!props.noresize) {
      addResizeListener(unref(resize), update);
      addResizeListener(unref(wrap), update);
      addEventListener('resize', update);
    }
  });
  onBeforeUnmount(() => {
    if (props.native) return;
    if (!props.noresize) {
      removeResizeListener(unref(resize), update);
      removeResizeListener(unref(wrap), update);
      removeEventListener('resize', update);
    }
  });
</script>
<style lang="less">