跳至主要內容

自封装的组件

XinYang's Blog大约 4 分钟移动开发

录音组件

查看代码
useVModel
import { computed } from "vue";
export const useVModel = (props: AnyObject, propName: string, emit: (...args: any) => void) => {
  return computed({
    get() {
      return new Proxy(props, {
        set(obj, name, val) {
          emit("update:" + propName, {
            ...obj,
            [name]: val,
          });
          return true;
        },
      });
    },
    set(val) {
      emit("update:" + propName, val);
    },
  });
};

动画组件

仿vue原生Transition组件

注意事项:需要下载animate.css库,同时注意引入的时候不能命名为Transition进行使用,会与vue原生的Transition组件冲突,而原生组件在APP中是不支持的

查看代码
<template>
  <view
    v-show="delayShow"
    :class="[customClass, 'animate__animated', 'transition-animate', show ? (appear ? enterClass : '') : leaveClass]"
    :style="{
      ...customStyle,
      '--animate-duration': `${duration}ms`,
      '--animate-delay': `${delay}ms`,
    }"
    @click="$emit('click')">
    <slot name="default" />
  </view>
</template>

<script setup lang="ts">
import { ref, watch, nextTick } from "vue";
const delayShow = ref<boolean>(false);
let durationTime: number = 0;
let timer: NodeJS.Timeout;
const emit = defineEmits(["open", "opened", "close", "closed", "click"]);
const props = withDefaults(
  defineProps<{
    /**
     * 是否显示动画组件,默认值为true,可不传
     */
    show?: boolean;
    /**
     *  首次渲染时,是否显示动画效果,默认值为false,可不传
     */
    appear?: boolean;
    /**
     * 组件显示时的动画类名,默认值为:animate__fadeIn,可不传,更多动画效果参见:https://animate.style/
     */
    enterClass?: string;
    /**
     * 组件隐藏时的动画类名,默认值为:animate__fadeOut,可不传,更多动画效果参见:https://animate.style/
     */
    leaveClass?: string;
    /**
     * 组件显示或隐藏时动画持续时间,默认值为500,单位毫秒,可不传
     */
    duration?: number;
    /**
     * 组件显示或隐藏时的延迟时间,即多少秒之后组件再显示或隐藏,默认值为0,单位毫秒,可不传
     */
    delay?: number;
    /**
     * 组件的内联样式,默认值为 {},可不传
     */
    customStyle?: object;
    /**
     * 组件的类名,默认值为 "",可不传
     */
    customClass?: string;
  }>(),
  {
    show: true,
    appear: false,
    enterClass: "animate__fadeIn",
    leaveClass: "animate__fadeOut",
    duration: 500,
    delay: 0,
    customStyle: () => {
      return {};
    },
    customClass: "",
  }
);

const show = ref<boolean>(props.show);

watch(
  () => props.show,
  nV => {
    show.value = nV;
  }
);

watch(
  show,
  nV => {
    if (timer) clearTimeout(timer);
    if (nV) {
      delayShow.value = nV;
      emit("open");
      timer = setTimeout(
        () => {
          emit("opened");
        },
        props.enterClass ? props.duration + props.delay : 0
      );
    } else {
      emit("close");
      timer = setTimeout(
        () => {
          delayShow.value = nV;
          emit("closed");
        },
        props.leaveClass ? props.duration + props.delay : 0
      );
    }
  },
  {
    immediate: true,
  }
);

watch(
  [() => props.duration, () => props.delay],
  nV => {
    durationTime = nV[0] + nV[1];
  },
  {
    immediate: true,
  }
);

const toggleComponent = async (callback: Function): Promise<void> => {
  show.value = false;
  await nextTick();
  let _timer: NodeJS.Timeout = setTimeout(() => {
    callback();
    show.value = true;
    clearTimeout(_timer);
  }, durationTime);
};

defineExpose({
  /**
   * 暴露toggleComponent函数,通过传callback函数达到异步改变插槽内容的目的,即会等到动画结束后再改变插槽的内容
   */
  toggleComponent,
});
</script>

<style lang="scss" scoped>
.transition-animate {
  height: max-content;
}
</style>