refactor(components): [backtop] (#10151)

* refactor(components): [backtop]

* Extract logic code from component itself.
* Reorganize code for better readability.

* chore: remove handleScrollThrottled from being returned
This commit is contained in:
Jeremy 2022-10-20 14:02:56 +08:00 committed by GitHub
parent 79982b8610
commit 84a867f847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 51 deletions

View File

@ -6,3 +6,4 @@ export const ElBacktop = withInstall(Backtop)
export default ElBacktop
export * from './src/backtop'
export type { BacktopInstance } from './src/instance'

View File

@ -1,5 +1,4 @@
import type { ExtractPropTypes } from 'vue'
import type Backtop from './backtop.vue'
export const backtopProps = {
visibilityHeight: {
@ -25,5 +24,3 @@ export const backtopEmits = {
click: (evt: MouseEvent) => evt instanceof MouseEvent,
}
export type BacktopEmits = typeof backtopEmits
export type BacktopInstance = InstanceType<typeof Backtop>

View File

@ -14,15 +14,15 @@
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, shallowRef } from 'vue'
import { useEventListener, useThrottleFn } from '@vueuse/core'
import { computed } from 'vue'
import { ElIcon } from '@element-plus/components/icon'
import { easeInOutCubic, throwError } from '@element-plus/utils'
import { CaretTop } from '@element-plus/icons-vue'
import { useNamespace } from '@element-plus/hooks'
import { backtopEmits, backtopProps } from './backtop'
import { useBackTop } from './use-backtop'
const COMPONENT_NAME = 'ElBacktop'
defineOptions({
name: COMPONENT_NAME,
})
@ -31,54 +31,11 @@ const props = defineProps(backtopProps)
const emit = defineEmits(backtopEmits)
const ns = useNamespace('backtop')
const el = shallowRef<HTMLElement>()
const container = shallowRef<Document | HTMLElement>()
const visible = ref(false)
const { handleClick, visible } = useBackTop(props, emit, COMPONENT_NAME)
const backTopStyle = computed(() => ({
right: `${props.right}px`,
bottom: `${props.bottom}px`,
}))
const scrollToTop = () => {
// TODO: use https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo, with behavior: 'smooth'
if (!el.value) return
const beginTime = Date.now()
const beginValue = el.value.scrollTop
const frameFunc = () => {
if (!el.value) return
const progress = (Date.now() - beginTime) / 500
if (progress < 1) {
el.value.scrollTop = beginValue * (1 - easeInOutCubic(progress))
requestAnimationFrame(frameFunc)
} else {
el.value.scrollTop = 0
}
}
requestAnimationFrame(frameFunc)
}
const handleScroll = () => {
if (el.value) visible.value = el.value.scrollTop >= props.visibilityHeight
}
const handleClick = (event: MouseEvent) => {
scrollToTop()
emit('click', event)
}
const handleScrollThrottled = useThrottleFn(handleScroll, 300, true)
useEventListener(container, 'scroll', handleScrollThrottled)
onMounted(() => {
container.value = document
el.value = document.documentElement
if (props.target) {
el.value = document.querySelector<HTMLElement>(props.target) ?? undefined
if (!el.value) {
throwError(COMPONENT_NAME, `target is not existed: ${props.target}`)
}
container.value = el.value
}
})
</script>

View File

@ -0,0 +1,3 @@
import type Backtop from './backtop.vue'
export type BacktopInstance = InstanceType<typeof Backtop>

View File

@ -0,0 +1,64 @@
import { onMounted, ref, shallowRef } from 'vue'
import { useEventListener, useThrottleFn } from '@vueuse/core'
import { easeInOutCubic, throwError } from '@element-plus/utils'
import type { SetupContext } from 'vue'
import type { BacktopEmits, BacktopProps } from './backtop'
export const useBackTop = (
props: BacktopProps,
emit: SetupContext<BacktopEmits>['emit'],
componentName: string
) => {
const el = shallowRef<HTMLElement>()
const container = shallowRef<Document | HTMLElement>()
const visible = ref(false)
const scrollToTop = () => {
// TODO: use https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo, with behavior: 'smooth'
if (!el.value) return
const beginTime = Date.now()
const beginValue = el.value.scrollTop
const frameFunc = () => {
if (!el.value) return
const progress = (Date.now() - beginTime) / 500
if (progress < 1) {
el.value.scrollTop = beginValue * (1 - easeInOutCubic(progress))
requestAnimationFrame(frameFunc)
} else {
el.value.scrollTop = 0
}
}
requestAnimationFrame(frameFunc)
}
const handleScroll = () => {
if (el.value) visible.value = el.value.scrollTop >= props.visibilityHeight
}
const handleClick = (event: MouseEvent) => {
scrollToTop()
emit('click', event)
}
const handleScrollThrottled = useThrottleFn(handleScroll, 300, true)
useEventListener(container, 'scroll', handleScrollThrottled)
onMounted(() => {
container.value = document
el.value = document.documentElement
if (props.target) {
el.value = document.querySelector<HTMLElement>(props.target) ?? undefined
if (!el.value) {
throwError(componentName, `target does not exist: ${props.target}`)
}
container.value = el.value
}
})
return {
visible,
handleClick,
}
}