element-ui element-plus backtop - 分析

源代码地址

version:element-plus 1.0.1-beta.0

<template>
  <transition name="el-fade-in">
    <div
      v-if="visible"
      :style="{
        'right': styleRight,
        'bottom': styleBottom
      }"
      class="el-backtop"
      @click.stop="handleClick"
    >
      <slot>
        <i class="el-icon-caret-top"></i>
      </slot>
    </div>
  </transition>
</template>

引入的函数

interface EventListenerObject {
    handleEvent(evt: Event): void;
}

declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;

export const on = function(
  element: HTMLElement | Document | Window,
  event: string,
  handler: EventListenerOrEventListenerObject,
  useCapture = false,
): void {
  if (element && event && handler) {
    element.addEventListener(event, handler, useCapture)
  }
}

export const off = function(
  element: HTMLElement | Document | Window,
  event: string,
  handler: EventListenerOrEventListenerObject,
): void {
  if (element && event && handler) {
    element.removeEventListener(event, handler, false)
  }
}

export const easeInOutCubic = (value: number): number => value < 0.5
  ? cubic(value * 2) / 2
  : 1 - cubic((1 - value) * 2) / 2

<script lang="ts">
import { defineComponent, ref, computed, onMounted, onBeforeUnmount } from 'vue'
import throttle from 'lodash/throttle'
import { on, off } from '@element-plus/utils/dom'
import { easeInOutCubic } from '@element-plus/utils/animation'

// 传入props类型限制
interface IElBacktopProps {
  visibilityHeight: number // 滚动高度达到此参数值才出现
  target: string // 触发滚动的对象 一般指向页面整体容器的类名
  right: number // 控制其显示位置, 距离页面右边距
  bottom: number // 控制其显示位置, 距离页面底部距离
}

export default defineComponent({
  name: 'ElBacktop',
  props: {
    visibilityHeight: {
      type: Number,
      default: 200,
    },
    target: {
      type: String,
      default: '',
    },
    right: {
      type: Number,
      default: 40,
    },
    bottom: {
      type: Number,
      default: 40,
    },
  },
  emits: ['click'],
  setup(props: IElBacktopProps, ctx) {
    const el = ref(null)
    const container = ref(null)
    const visible = ref(false)
    const styleBottom = computed(() => `${props.bottom}px`)
    const styleRight = computed(() => `${props.right}px`)

    const scrollToTop = () => {
      const beginTime = Date.now()
      const beginValue = el.value.scrollTop
      // 没有就模拟帧函数
      const rAF = window.requestAnimationFrame || (func => setTimeout(func, 16))
      const frameFunc = () => {
        const progress = (Date.now() - beginTime) / 500
        // 判断是否隔了500ms
        if (progress < 1) {
          el.value.scrollTop = beginValue * (1 - easeInOutCubic(progress))
          // 继续调用
          rAF(frameFunc)
        } else {
          el.value.scrollTop = 0
        }
      }
      rAF(frameFunc)
    }

    // 控制是否显示 按钮
    const onScroll = () => {
      visible.value = el.value.scrollTop >= props.visibilityHeight
    }
    // 点击触发的回调
    const handleClick = event => {
      scrollToTop()
      ctx.emit('click', event) // 回调
    }

    // 节流触发
    const throttledScrollHandler = throttle(onScroll, 300)
    // 挂载时
    onMounted(() => {
      container.value = document
      el.value = document.documentElement
      if (props.target) {
        // el 指向 找到的元素
        el.value = document.querySelector(props.target)
        if (!el.value) {
          // 没找到 丢出 错误
          throw new Error(`target is not existed: ${props.target}`)
        }
        container.value = el.value
      }
      // 事件监听 scroll 节流
      on(container.value, 'scroll', throttledScrollHandler)
    })
    // 页面销毁之前
    onBeforeUnmount(() => {
      off(container.value, 'scroll', throttledScrollHandler)
    })

    return {
      el,
      container,
      visible,
      styleBottom,
      styleRight,
      handleClick,
    }
  },
})
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值