|
|
@@ -1,10 +1,13 @@
|
|
|
<template>
|
|
|
- <div class="dock-wrapper"
|
|
|
+ <div class="dock-wrapper"
|
|
|
+ ref="dockWrapper"
|
|
|
:class="[
|
|
|
- autoHide ? 'is-auto-hide' : 'is-always-show',
|
|
|
- customClass
|
|
|
+ autoHide ? 'is-auto-hide' : 'is-always-show',
|
|
|
+ customClass,
|
|
|
+ { 'is-expanded': dockExpanded }
|
|
|
]"
|
|
|
- :style="dockStyles">
|
|
|
+ :style="dockStyles"
|
|
|
+ @mouseleave="handleDockLeave">
|
|
|
|
|
|
<div class="nav-arrow left-arrow" :class="{ 'is-disabled': !canScrollLeft, 'is-active': canScrollLeft }"
|
|
|
@click="scrollList(-1)">
|
|
|
@@ -69,8 +72,9 @@ export default {
|
|
|
return {
|
|
|
activeIndex: -1,
|
|
|
hoverIndex: null,
|
|
|
- canScrollLeft: false,
|
|
|
- canScrollRight: true,
|
|
|
+ dockExpanded: false,
|
|
|
+ canScrollLeft: false,
|
|
|
+ canScrollRight: true,
|
|
|
dockItems: [
|
|
|
{
|
|
|
label: '首页',
|
|
|
@@ -154,10 +158,14 @@ export default {
|
|
|
this.$nextTick(() => {
|
|
|
this.checkScrollState();
|
|
|
window.addEventListener('resize', this.checkScrollState);
|
|
|
+ if (this.autoHide) {
|
|
|
+ document.addEventListener('mousemove', this.handleGlobalMouseMove);
|
|
|
+ }
|
|
|
});
|
|
|
},
|
|
|
beforeDestroy() {
|
|
|
window.removeEventListener('resize', this.checkScrollState);
|
|
|
+ document.removeEventListener('mousemove', this.handleGlobalMouseMove);
|
|
|
},
|
|
|
methods: {
|
|
|
updateActiveIndexByRoute() {
|
|
|
@@ -191,6 +199,24 @@ export default {
|
|
|
this.canScrollLeft = container.scrollLeft > 0;
|
|
|
this.canScrollRight = Math.ceil(container.scrollLeft + container.clientWidth) < container.scrollWidth;
|
|
|
},
|
|
|
+ handleGlobalMouseMove(e) {
|
|
|
+ if (!this.autoHide || this.dockExpanded) return;
|
|
|
+ const el = this.$refs.dockWrapper;
|
|
|
+ if (!el) return;
|
|
|
+ const rect = el.getBoundingClientRect();
|
|
|
+ // 发光线区域:居中,宽度与 CSS clamp(150px, 20vw, 250px) 一致
|
|
|
+ const lineWidth = Math.min(250, Math.max(150, window.innerWidth * 0.2));
|
|
|
+ const centerX = window.innerWidth / 2;
|
|
|
+ const left = centerX - lineWidth / 2;
|
|
|
+ const right = centerX + lineWidth / 2;
|
|
|
+ // 垂直:dock 可见顶部往上延伸 30px(与 ::after 热区一致)
|
|
|
+ if (e.clientX >= left && e.clientX <= right && e.clientY >= rect.top - 30) {
|
|
|
+ this.dockExpanded = true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleDockLeave() {
|
|
|
+ this.dockExpanded = false;
|
|
|
+ },
|
|
|
scrollList(direction) {
|
|
|
if (direction === -1 && !this.canScrollLeft) return;
|
|
|
if (direction === 1 && !this.canScrollRight) return;
|
|
|
@@ -229,24 +255,25 @@ export default {
|
|
|
|
|
|
/* === 状态 A:自动隐藏 === */
|
|
|
.dock-wrapper.is-auto-hide {
|
|
|
- /* 核心改动3:下沉 100%(即自身整个高度),但往回拉出一段可见距离。
|
|
|
- clamp(最小值, 动态值, 最大值):保证小屏幕最少露出 25px,大屏最多露 40px */
|
|
|
- transform: translateY(calc(100% - clamp(25px, 4vh, 40px)));
|
|
|
+ transform: translateY(calc(100% - clamp(15px, 4vh, 20px)));
|
|
|
+ pointer-events: none;
|
|
|
}
|
|
|
-.dock-wrapper.is-auto-hide:hover {
|
|
|
- /* 悬浮时,位移归零,完全升起 */
|
|
|
+.dock-wrapper.is-auto-hide.is-expanded {
|
|
|
transform: translateY(0);
|
|
|
+ pointer-events: auto;
|
|
|
}
|
|
|
|
|
|
-/* 增加隐形触发热区!防止线太细导致小屏幕极难 hover */
|
|
|
+/* 中间触发热区:只覆盖发光线范围,pointer-events 始终开启 */
|
|
|
.dock-wrapper.is-auto-hide::after {
|
|
|
content: '';
|
|
|
position: absolute;
|
|
|
- top: -30px; /* 向上延伸 30px 的隐形热区 */
|
|
|
- left: 0;
|
|
|
- width: 100%;
|
|
|
- height: 60px; /* 覆盖线上下区域 */
|
|
|
+ top: -30px;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ width: clamp(150px, 20vw, 250px);
|
|
|
+ height: 60px;
|
|
|
background: transparent;
|
|
|
+ pointer-events: auto;
|
|
|
}
|
|
|
|
|
|
/* 发光的指示线自适应 */
|
|
|
@@ -263,10 +290,10 @@ export default {
|
|
|
box-shadow: 0 0 10px rgba(0, 229, 255, 0.8);
|
|
|
border-radius: 4px;
|
|
|
transition: opacity 0.3s;
|
|
|
- pointer-events: none;
|
|
|
+ pointer-events: auto;
|
|
|
}
|
|
|
|
|
|
-.dock-wrapper.is-auto-hide:hover::before {
|
|
|
+.dock-wrapper.is-auto-hide.is-expanded::before {
|
|
|
opacity: 0;
|
|
|
}
|
|
|
|