iter(g113): 吸顶容器支持贴底固定

This commit is contained in:
HIsheR
2026-10-26 14:06:32 +08:00
parent 5c25e88c98
commit ec7aaf0a7d
6 changed files with 34 additions and 8 deletions
+6 -3
View File
@@ -2,10 +2,11 @@ import { h } from '@ui-arch/frame';
import type { AffixProps } from './types.js';
import { watchViewport } from './watch.js';
/*** 固钉:内容滚动越过阈值后转 fixed 贴住视口顶部,外层保留占位高度避免布局跳动 ***/
/*** 固钉:内容滚动越过阈值后转 fixed 贴住视口边缘(top / bottom),外层保留占位高度避免布局跳动 ***/
export function affix(props: AffixProps, ...children: unknown[]): HTMLElement {
// —— 状态集中声明 ——
const offset = props.offset ?? 0; // 触发固定的阈值
const atBottom = props.position === 'bottom'; // 贴底还是贴顶(底部适合操作条)
const scroller = props.target ?? window; // 滚动容器
const inner = h('div', { class: 'ua-affix-inner' }, ...children) as HTMLElement; // 真正会被固定的内容
const wrap = h('div', { class: 'ua-affix' }, inner) as HTMLElement; // 占位容器,固定时撑住原位
@@ -14,7 +15,7 @@ export function affix(props: AffixProps, ...children: unknown[]): HTMLElement {
/*** 同步固定态:越过阈值即 fixed + 撑占位高度,回到阈值以上就还原 ***/
const sync = (): void => {
const box = wrap.getBoundingClientRect(); // 占位容器几何:left / width 决定固定后的横向位置
const should = box.top <= offset; // 已滚到阈值之上
const should = atBottom ? box.bottom >= window.innerHeight - offset : box.top <= offset; // 贴底:占位落到阈值线下即固定
if (should === fixed) {
if (fixed) { // 固定中:宽度与横向位置跟随容器(窗口缩放 / 父容器变宽)
inner.style.width = `${box.width}px`;
@@ -26,12 +27,14 @@ export function affix(props: AffixProps, ...children: unknown[]): HTMLElement {
if (fixed) {
wrap.style.height = `${inner.offsetHeight}px`; // 先撑住占位,再让 inner 脱离文档流
inner.classList.add('is-fixed');
inner.style.top = `${offset}px`;
if (atBottom) inner.style.bottom = `${offset}px`; // 贴底:固定边距写 bottom,top 保持空
else inner.style.top = `${offset}px`;
inner.style.left = `${box.left}px`;
inner.style.width = `${box.width}px`;
} else {
inner.classList.remove('is-fixed');
inner.style.top = '';
inner.style.bottom = '';
inner.style.width = '';
wrap.style.height = '';
}
+2 -1
View File
@@ -664,7 +664,8 @@ export interface CheckboxGroupProps {
/*** 固钉入参 ***/
export interface AffixProps {
offset?: number; // 距视口顶部多少像素后转为固定;缺省 0
offset?: number; // 距视口边缘多少像素后转为固定;缺省 0
position?: 'top' | 'bottom'; // 固定在顶部还是底部(底部适合操作条);缺省 top
target?: HTMLElement | Window; // 监听的滚动容器;缺省 window
}
+7 -1
View File
@@ -2684,6 +2684,11 @@
"t": "number",
"d": "距视口顶部多少像素后转为固定;缺省 0"
},
{
"n": "position",
"t": "'top' | 'bottom'",
"d": "固定边:贴顶放头部工具栏,贴底放底部操作条;缺省 top"
},
{
"n": "target",
"t": "HTMLElement | Window",
@@ -2700,7 +2705,8 @@
"把要贴住的内容作为实参传入;组件内部包两层:外层占位、内层负责 fixed",
"滚动到 offset 以内(默认贴视口顶)时内层转 fixed,外层同步撑出等高占位,页面不会塌跳",
"固定期间宽度与横向位置跟随占位容器,窗口缩放或父容器变宽都自动贴合",
"内滚容器场景把该元素传给 target,判定改为基于它的滚动位置"
"内滚容器场景把该元素传给 target,判定改为基于它的滚动位置",
"底部操作条传 position: 'bottom':占位容器底边越过「视口底 - offset」时才固定,对应的内联边距写 bottom"
]
},
{