feat(dividing): sticky

这个提交包含在:
rohow
2026-01-30 18:17:12 +08:00
未验证
父节点 178af5c363
当前提交 de5564b19f
修改 5 个文件,包含 88 行新增9 行删除
+35
查看文件
@@ -0,0 +1,35 @@
export const headerDividing = () => ({
isSticky: false,
$el: null as HTMLElement | null,
_observer: null as IntersectionObserver | null,
init() {
const centerEl = this.$el as HTMLElement;
if (!centerEl) return;
const header = centerEl.closest('.header') as HTMLElement;
if (!header) return;
// 直接观察 header 元素本身
this._observer = new IntersectionObserver(
(entries) => {
const entry = entries[0];
this.isSticky = entry.boundingClientRect.top < 0 && !entry.isIntersecting;
},
{
threshold: 0, // 只要有任何部分离开就触发
rootMargin: '0px' // 无偏移
}
);
this._observer.observe(header);
},
destroy() {
if (this._observer) {
this._observer.disconnect();
this._observer = null;
}
}
});
@@ -3,7 +3,7 @@ interface MenuState {
handleToggleMenu(): void;
}
export const menu = (): MenuState => ({
export const headerMenu = (): MenuState => ({
isOpen: false,
handleToggleMenu() {
+4 -2
查看文件
@@ -5,18 +5,20 @@ import "./styles/font-pixel.scss";
import Alpine from 'alpinejs'
import {upvote} from './alpine/upvote'
import {themeMode} from './alpine/theme-mode'
import {menu} from './alpine/menu'
import {postLineNum} from './alpine/post-line-num'
import {postToc} from './alpine/post-toc'
import {headerDividing} from './alpine/header-dividing'
import {headerMenu} from './alpine/header-menu'
import {typewriterEffect} from './utils'
window.Alpine = Alpine
Alpine.data('upvote', upvote)
Alpine.data('themeMode', themeMode)
Alpine.data('menu', menu)
Alpine.data('postLineNum', postLineNum)
Alpine.data('postToc', postToc)
Alpine.data('headerMenu', headerMenu)
Alpine.data('headerDividing', headerDividing)
Alpine.start()
+44 -4
查看文件
@@ -11,12 +11,21 @@
justify-content: space-between;
}
&__center {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
}
.dividing {
flex: 1;
display: block;
width: 100%;
height: 25px;
position: relative;
--dash-spacing: 10px;
--dash-width: 2px;
// 虚线背景层 - 带遮罩
&::before {
@@ -29,11 +38,11 @@
background: repeating-linear-gradient(
90deg,
var(--foreground),
var(--foreground) 2px,
var(--foreground) var(--dash-width),
transparent 0,
transparent 10px
transparent var(--dash-spacing)
);
background-size: 10px 100%;
background-size: var(--dash-spacing) 100%;
animation: line-flow 3s linear infinite;
// 遮罩层实现虚线淡入淡出效果
@@ -73,6 +82,37 @@
animation: scan-line 5s ease-in-out infinite;
pointer-events: none;
z-index: 10;
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
&.sticky {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 5px;
z-index: 100;
--dash-spacing: 6px;
--dash-width: 2px;
background: var(--background);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
&::before {
animation: line-flow 1.5s linear infinite;
mask-image: none;
-webkit-mask-image: none;
top: calc(50% - 1px);
height: 2px;
}
&::after {
animation: scan-line 2.5s ease-in-out infinite;
opacity: 0.6;
filter: blur(3px);
// 调整扫描线高度以适应 5px 容器
height: 200%;
width: 25%;
}
}
}
@@ -82,7 +122,7 @@
background-position: 0 0;
}
100% {
background-position: 10px 0;
background-position: var(--dash-spacing) 0;
}
}
+4 -2
查看文件
@@ -5,7 +5,9 @@
<img class="icon" th:if="${not #strings.isEmpty(theme.config.basic.logo)}" th:src="${theme.config.basic.logo}" alt="Logo" />
<div class="text" th:if="${not #strings.isEmpty(theme.config.basic.title)}" th:text="${theme.config.basic.title}"></div>
</a>
<div class="dividing"></div>
<div class="header__center" x-data="headerDividing()">
<div class="dividing" :class="{ 'sticky': isSticky }"></div>
</div>
<button
type="button"
class="button"
@@ -47,7 +49,7 @@
</div>
<nav class="menu">
<ul th:if="${menu != null} and ${not #lists.isEmpty(menu.menuItems)}" class="menu__inner">
<li th:each="menuItem : ${menu.menuItems}" x-data="menu()" @mouseenter="handleToggleMenu()" @mouseleave="handleToggleMenu()">
<li th:each="menuItem : ${menu.menuItems}" x-data="headerMenu()" @mouseenter="handleToggleMenu()" @mouseleave="handleToggleMenu()">
<a
class="text-gray-600 hover:text-blue-600"
th:href="${menuItem.status.href}"