当前位置:网站首页>[day ui] affix component learning
[day ui] affix component learning
2022-06-24 12:44:00 【Uncertainty】
A fastener component is a component that compares an element of a page to a page HTML Or a dom Internal positioning display , For example, fix the top of the page / The bottom shows , Page width and height change will also keep the original position . If scrolling , Beyond the defined range, it will be fixed , Otherwise, it will scroll with the page
In the last section, we introduced DButton and DIcon The implementation of the , So new affix We will not introduce the file directory structure more . We will mainly learn about the internal implementation , The essence is location , Let's see what judgments and third-party libraries are used , If there is anything wrong, please correct me .
Effect analysis
- The first case is that no container is set , According to
positionPosition setting fixed positioning , If the position is settop, So when listening to page scrolling , If the current element'stopThe value is less than the set offset , Set upfixedlocation ( converselybottomIs the morebottomThe value is greater than the difference between the page height and the offsetfixedlocation ) - The second case is to set the container , that
top / bottomIs displayed only in the container , The container is not behind the page , The positioning element disappears . If thetopvalue , So when the current elementtopThe value is less than the offset and the container'sbottomGreater than 0, Elementsfixedlocation ( converselybottomThe offset needs to calculate the page height andbottomWorth comparing ). I learned recently that ,fixedPositioning is relative to the window by default , But if you define attributes for the parent nodetransform、filter、perspective,fixedPositioning is relative to the parent set , If you are interested, you can check by yourself .
The code analysis
dom structure
<template>
<div ref="root" class="d-affix" :style="rootStyle">
<!-- Positioning elements Listen while scrolling root The relationship between the position and the viewable area of the page is set fixed, Set the style when positioning -->
<div :class="{ 'd-affix--fixed': state.fixed }" :style="affixStyle">
<slot></slot>
</div>
</div>
</template> Outer layer definition d-affix class , The height is the same as the internal element , To be an internal element fixed When locating out of document flow , The page placeholder structure remains unchanged ; At the same time, we need to compare d-affix Of top and bottom Value to determine when an element leaves the document , When to reset .
attribute
props: {
// Locate the level of the element
zIndex: {
type: Number,
default: 100
},
// In which container , No transmission is the view
target: {
type: String,
default: ''
},
// Up and down offset
offset: {
type: Number,
default: 0
},
// From top to bottom
position: {
type: String,
default: 'top'
}
},
// There are two ways to expose to the outside world , Monitor scrolling and fixed State change
emits: ['scroll', 'change'],setUp The core
// Locate element attributes
const state = reactive({
fixed: false,
height: 0, // height of target Get assignment when scrolling
width: 0, // width of target
scrollTop: 0, // scrollTop of documentElement
clientHeight: 0, // Window height
transform: 0 // The elements are in target When positioning y direction
})
// Compute properties , Only when scrolling can we get the specific
// d-affix Class always exists in the document stream , As long as width and height , Scroll position to determine whether fixed
const rootStyle = computed(() => {
return {
height: state.fixed ? `${state.height}px` : '',
width: state.fixed ? `${state.width}px` : ''
}
})
// Locate element attributes
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
}
})Judgment of positioning attribute during scrolling :
const updateState = () => {
// obtain d-affix Node information
const rootRect = root.value.getBoundingClientRect()
// obtain target Node information
const targetRect = target.value.getBoundingClientRect()
state.height = rootRect.height
state.width = rootRect.width
// No, target take html Of scrollTOP( Yes target stay target Middle scroll )
state.scrollTop =
scrollContainer.value === window
? document.documentElement.scrollTop
: scrollContainer.value.scrollTop
state.clientHeight = document.documentElement.clientHeight
// Set the top margin
if (props.position === 'top') {
if (props.target) {
// Locate the element in target Sliding distance in element ,bottom Continuous change
const difference = targetRect.bottom - props.offset - state.height
// target Elements top Outside the viewing area ,bottom Locate in the visible area
state.fixed = props.offset > rootRect.top && targetRect.bottom > 0
state.transform = difference < 0 ? difference : 0
} else {
// With html Is a relative container , Page scrolling , Fixed position (d-affix Outside the visual area )
state.fixed = props.offset > rootRect.top
}
} else {
// Set bottom margin
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 {
// offset + bottom > View height , Elements are positioned
state.fixed = state.clientHeight - props.offset < rootRect.bottom
}
}
}const onScroll = () => {
updateState()
emit('scroll', {
scrollTop: state.scrollTop,
fixed: state.fixed
})
}
watch(
() => state.fixed,
() => {
emit('change', state.fixed)
}
)
// When the page is mounted
onMounted(() => {
if (props.target) {
// Pay attention to the format
target.value = document.querySelector(props.target)
if (!target.value) {
throw new Error(`target is not existed: ${props.target}`)
}
} else {
target.value = document.documentElement // html
}
// Let's analyze the auxiliary function
scrollContainer.value = getScrollContainer(root.value)
// Functional programming ,on Rewritten addEventListener
on(scrollContainer.value, 'scroll', onScroll)
addResizeListener(root.value, updateState)
})
// The page is about to close. Cancel listening and remove
onBeforeMount(() => {
off(scrollContainer.value, 'scroll', onScroll)
removeResizeListener(root.value, updateState)
})Auxiliary function
- on// Functional programming handles element listening export const on = function(element, event, handler, useCapture = false) { if (element && event && handler) { element.addEventListener(event, handler, useCapture) } }export const off = function(element, event, handler, useCapture = false) { if (element && event && handler) { element.removeEventListener(event, handler, useCapture) } }/** * Get the scroll container * @param {*} el Rolling container * @param {*} isVertical Scroll vertically or horizontally * @returns */ export const getScrollContainer = (el, isVertical) => { if (isServer) return let parent = el while (parent) { // None, just window if ([window, document, document.documentElement].includes(parent)) { return window } // Whether the container is scrollable if (isScroll(parent, isVertical)) { return parent } parent = parent.parentNode } return parent }export default typeof window === 'undefined'/** * * @param {*} el * @param {*} isVertical Is it vertical overflow-y * @returns */ export const isScroll = (el, isVertical) => { if (isServer) return const determineDirection = isVertical === null || isVertical === undefined const overflow = determineDirection ? getStyle(el, 'overflow') : isVertical ? getStyle(el, 'overflow-y') : getStyle(el, 'overflow-x') return overflow.match(/(scroll|auto)/) }// Gets the attribute value of the element export const getStyle = function(element, styleName) { if (isServer) return if (!element || !styleName) return null styleName = camelize(styleName) if (styleName === 'float') { /** * ie6~8 Next :style.styleFloat FF/chrome as well as ie9 above :style.cssFloat */ styleName = 'cssFloat' // FF/chrome as well as ie9 above float Compatibility writing } try { const style = element.style[styleName] if (style) return style // obtain window object , firefox Low version 3.6 Can be used getComputed Method ,iframe pupup extension window === document.defaultView, Otherwise, the point is wrong // https://www.cnblogs.com/yuan-shuai/p/4125511.html const computed = document.defaultView.getComputedStyle(element, '') return computed ? computed[styleName] : '' } catch (e) { return element.style[styleName] } }resize-observer-polyfill This library is the first time I have seen , If you don't know the source code . I think it's very interesting , Here is a brief introduction .
- off
- getScrollContainer
- isSserver
- isScroll
- getStyle
The main function of this library is to listen for elements size change . Usually, we can only use to monitor the size change window.size perhaps window.orientationchange( The mobile terminal screen displays horizontally and vertically ).resize The event will be in 1s Internal trigger 60 Times or so , Therefore, it is easy to cause performance problems when changing the window size , So when we listen for changes in an element, it seems a bit wasteful .
ResizeObserver API It's new , There is also compatibility in some browsers , This library is very compatible .ResizeObserver Using observer mode , When element size Trigger when a change occurs ( The presence of hidden nodes will also trigger ).
usage
const observer = new ResizeObserver(entries => {
entries.forEach(entry => {
console.log(' Size position ', entry.contentRect)
console.log(' Monitoring dom', entry.target)
})
})
// The object of listening is body, You can change the size of the browser window to see the printing effect
observer.observe(document.body)// dom node , It's not a class name id name width: Refers to the width of the element itself , It doesn't containpadding,bordervalueheight: Refers to the height of the element itself , It doesn't containpadding,bordervaluetop: fingerpadidng-topValueleft: fingerpadding-leftValueright: fingerleft + widthValuebottom: valuetop + heightValue
Method
ResizeObserver.disconnect()Cancel listening for all elementsResizeObserver.observe()Monitor elementsResizeObserver.unobserve()End listening for an element
Components use
We are onMounted Chinese vs root Element listening . Listen while scrolling , Element size changes should also be monitored
import ResizeObserver from 'resize-observer-polyfill'
import isServer from './isServer'
const resizeHandler = function(entries) {
for (const entry of entries) {
/**
* const {left, top, width, height} = entry.contentRect;
* 'Element:', entry.target
Element's size: ${ width }px x ${ height }px`
Element's paddings: ${ top }px ; ${ left }px`
*/
const listeners = entry.target.__resizeListeners__ || []
if (listeners.length) {
// Element changes the direct execution method
listeners.forEach(fn => fn())
}
}
}
// monitor element Elements size change , perform fn
export const addResizeListener = function(element, fn) {
if (isServer || !element) return
if (!element.__resizeListeners__) {
element.__resizeListeners__ = []
/**
* https://github.com/que-etc/resize-observer-polyfill
*
*/
element.__ro__ = new ResizeObserver(resizeHandler)
// The object of observation
element.__ro__.observe(element)
}
element.__resizeListeners__.push(fn)
}
// Exit remove listening
export const removeResizeListener = function(element, fn) {
if (!element || !element.__resizeListeners__) return
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1)
if (!element.__resizeListeners__.length) {
// Cancel monitoring
element.__ro__.disconnect()
}
} That's right affix Component learning . If there is any mistake, please correct it . Next, let's go to alert Component learning . If the article helps you , Welcome to the official account Touch the front end , Or add wechat :wajh123654789, Learning together .
边栏推荐
- How do websites and we media tap user needs? Deeply expose the secrets behind the keywords!
- How to do research on plant endophytes? Special topic on Microbiology
- 简述聚类分析
- A scheme for crawlers to collect public opinion data
- How to calculate the bandwidth of video transmission? How much bandwidth is required to transmit 4K video?
- [live broadcast of celebrities] elastic observability workshop
- Continuous testing | test process improvement: practice continuous testing within iterations in coding
- 解析nc格式文件,GRB格式文件的依赖包edu.ucar.netcdfAll的api 学习
- Kubernetes best practice: graceful termination
- Cryptography series: collision defense and collision attack
猜你喜欢

Opencv learning notes - regions of interest (ROI) and image blending

【概率论期末抱佛脚】概念+公式(不含参数估计)

MySQL 外键影响
Deep parsing and implementation of redis pub/sub publish subscribe mode message queue

一文讲透植物内生菌研究怎么做 | 微生物专题

Opencv learning notes -- Separation of color channels and multi-channel mixing

WPF from zero to 1 tutorial details, suitable for novices on the road

A hero's note stirred up a thousand waves across 10 countries, and the first-line big factories sent people here- Gwei 2022 Singapore

Codereview tool chain for micro medicine

《回归故里》阅读笔记
随机推荐
How to do research on plant endophytes? Special topic on Microbiology
MySQL foreign key impact
文本转语音功能上线,可以体验专业播音员的服务,诚邀试用
A "full cloud" journey of a quasi financial system
Continuous testing | test process improvement: practice continuous testing within iterations in coding
Simple and flexible permission design?
Cloud native database: the outlet of the database, you can also take off
Pinduoduo press the user accelerator key
How does the video networking / network penetration tool easynts permanently delete one of the devices?
Hardware enterprise website ranking, 8 commonly used processes
Tencent security monthly report - zero trust development trend forum, digital Expo Technology Award, Mercedes Benz security research results
Install MySQL in docker and modify my CNF profile
IOMMU (VII) -vfio and mdev
About Adobe Photoshop adjusting selection
Process of solving easydss virtual live video jam and instability problems by replacing push-pull stream Library
[live broadcast of celebrities] elastic observability workshop
[redisson] analysis of semaphore lock source code
Kubernetes practical skills: use cert manager to issue free certificates for DNSPod domain names
申请MIMIC数据库失败怎么办?从失败到成功的经验分享给你~
How to configure the national standard platform easygbs neutral version?