back-top.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import { onBeforeUnmount, onMounted, ref } from 'vue'
  2. import { isClient } from '@vueuse/core'
  3. import { throttleAndDebounce } from '../utils'
  4. const threshold = 960
  5. const cubic = (value: number): number => value ** 3
  6. const easeInOutCubic = (value: number): number =>
  7. value < 0.5 ? cubic(value * 2) / 2 : 1 - cubic((1 - value) * 2) / 2
  8. export const useBackTop = (offset = 200) => {
  9. const shouldShow = ref(false)
  10. const throttleResize = throttleAndDebounce(onResize, 300)
  11. const throttleScroll = throttleAndDebounce(onScroll, 160)
  12. onMounted(() => {
  13. if (!isClient) return
  14. onResize()
  15. onScroll()
  16. window.addEventListener('resize', throttleResize)
  17. })
  18. onBeforeUnmount(() => {
  19. if (!isClient) return
  20. window.removeEventListener('resize', throttleResize)
  21. window.removeEventListener('scroll', throttleScroll)
  22. })
  23. const scrollToTop = () => {
  24. const beginTime = Date.now()
  25. const beginValue = document.documentElement.scrollTop
  26. const rAF = window.requestAnimationFrame
  27. const frameFunc = () => {
  28. const progress = (Date.now() - beginTime) / 500
  29. if (progress < 1) {
  30. document.documentElement.scrollTop =
  31. beginValue * (1 - easeInOutCubic(progress))
  32. rAF(frameFunc)
  33. } else {
  34. document.documentElement.scrollTop = 0
  35. }
  36. }
  37. rAF(frameFunc)
  38. }
  39. function onResize() {
  40. if (!isClient) return
  41. const { clientWidth } = document.body
  42. if (clientWidth < threshold) {
  43. window.addEventListener('scroll', throttleScroll)
  44. } else {
  45. window.removeEventListener('scroll', throttleScroll)
  46. }
  47. }
  48. function onScroll() {
  49. if (!isClient) return
  50. shouldShow.value = document.documentElement.scrollTop > offset
  51. }
  52. return {
  53. shouldShow,
  54. scrollToTop,
  55. }
  56. }