I. Component introduction
The Affix component is used to anchor page elements in a specific visual area.
1.1 attributes
- Position: Specifies the position of the fasteners. The value can be top or bottom. The default value is top
- Offset: Sets the offset distance. The default value is 0
- Target: specifies the container (CSS selector) that keeps the studs inside the container and hidden beyond the range. The default container is
document.documentElement
. - Z-index: specifies the level of fixing. The default value is 100
1.2 event
- Scroll: event triggered when the container is rolling. Parameters are: scrollTop value and status (fixed or not) of the peg.
- Change: triggered when the fixed state changes. The parameter is whether the fixed state is currently fixed
Second, source code analysis
2.1 the template
<template>
<div ref="root" class="el-affix" :style="rootStyle">
<div :class="{'el-affix--fixed': state.fixed}" :style="affixStyle">
<slot></slot>
</div>
</div>
</template>
Copy the code
The Template part is simple, receiving content through slots
2.2 the script
// Part of the core code, code order has been adjusted
setup(props, { emit }) {
// Target container ref
const target = ref(null)
// Fix ref, which works with the ref attribute in the template to get the HTML element
const root = ref(null)
// Scroll the container ref
const scrollContainer = ref(null)
// The state of the nail
const state = reactive({
fixed: false.height: 0.// height of root
width: 0.// width of root
scrollTop: 0.// scrollTop of documentElement
clientHeight: 0.// clientHeight of documentElement
transform: 0,
})
onMounted(() = > {
// Determine the target container based on the target passed in
if (props.target) {
target.value = document.querySelector(props.target)
if(! target.value) {throw new Error(`target is not existed: ${props.target}`)}}else {
target.value = document.documentElement
}
// Look up the scroll container based on the pin element
scrollContainer.value = getScrollContainer(root.value)
// Monitor the scroll event of the scroll container
on(scrollContainer.value, 'scroll', onScroll)
// Listen for the resize event of the pin element
addResizeListener(root.value, updateState)
})
// The response function to the scroll event of the scroll container
const onScroll = () = > {
// Update the stapler status
updateState()
emit('scroll', {
scrollTop: state.scrollTop,
fixed: state.fixed,
})
}
// Update the stapler state function
const updateState = () = > {
const rootRect = root.value.getBoundingClientRect()
const targetRect = target.value.getBoundingClientRect()
state.height = rootRect.height
state.width = rootRect.width
state.scrollTop = scrollContainer.value === window ? document.documentElement.scrollTop : scrollContainer.value.scrollTop
state.clientHeight = document.documentElement.clientHeight
if (props.position === 'top') {
if (props.target) {
const difference = targetRect.bottom - props.offset - state.height
// targetrect. bottom > 0 corresponds to keeping the studs inside the container and hiding them beyond the range
state.fixed = props.offset > rootRect.top && targetRect.bottom > 0
// For processing scenarios: during scrolling, if the target container is not visible enough to display the entire nail, the nail should be offset accordingly and only part of the nail should be displayed
state.transform = difference < 0 ? difference : 0
} else {
state.fixed = props.offset > rootRect.top
}
} else {
if (props.target) {
const difference = state.clientHeight - targetRect.top - props.offset - state.height
state.fixed = state.clientHeight - props.offset < rootRect.bottom && state.clientHeight > targetRect.top
state.transform = difference < 0 ? -difference : 0
} else {
state.fixed = state.clientHeight - props.offset < rootRect.bottom
}
}
}
// Monitor fixed status change and emit change event
watch(() = > state.fixed, () = > {
emit('change', state.fixed)
})
// Calculate the properties to automatically update the style of the stud via the status of the stud
const affixStyle = computed(() = > {
if(! state.fixed) {return
}
const offset = props.offset ? `${props.offset}px` : 0
const transform = state.transform ? `translateY(${state.transform}px)` : ' '
return {
height: `${state.height}px`.width: `${state.width}px`.top: props.position === 'top' ? offset : ' '.bottom: props.position === 'bottom' ? offset : ' '.transform: transform,
zIndex: props.zIndex,
}
})
}
Copy the code
2.3 Realization summary:
-
By listening to the scroll event of the rolling container (and the resize event of the fixation itself);
-
The event response function dynamically obtains the DOM properties of the fixer and the target container and calculates the state of the fixer.
-
Using computational properties to automatically update the style of the stud;