|
|
@@ -9,16 +9,32 @@
|
|
|
:style="dockStyles"
|
|
|
@mouseleave="handleDockLeave">
|
|
|
|
|
|
- <div class="nav-arrow left-arrow" :class="{ 'is-disabled': !canScrollLeft, 'is-active': canScrollLeft }"
|
|
|
- @click="scrollList(-1)">
|
|
|
- <img v-if="canScrollLeft" src="@/assets/main/main-right.png" class="arrow-img left-facing" />
|
|
|
- <img v-else src="@/assets/main/main-left.png" class="arrow-img" />
|
|
|
+ <div class="nav-arrow left-arrow"
|
|
|
+ :class="{ 'is-disabled': mode === 'linear' && !canScrollLeft, 'is-active': mode === 'ellipse' || canScrollLeft }"
|
|
|
+ @click="mode === 'ellipse' ? rotateMenu(-1) : scrollList(-1)"
|
|
|
+ @mouseenter="pauseAutoRotate"
|
|
|
+ @mouseleave="resumeAutoRotate">
|
|
|
+ <img v-if="mode === 'ellipse' || canScrollLeft" src="@/assets/main/main-right.png" class="arrow-img left-facing" draggable="false" />
|
|
|
+ <img v-else src="@/assets/main/main-left.png" class="arrow-img" draggable="false" />
|
|
|
</div>
|
|
|
|
|
|
- <div class="dock-list-container" ref="listContainer" @scroll="checkScrollState">
|
|
|
- <div class="dock-list">
|
|
|
+ <div class="dock-list-container"
|
|
|
+ :class="{ 'is-ellipse-mode': mode === 'ellipse' }"
|
|
|
+ ref="listContainer"
|
|
|
+ @scroll="checkScrollState"
|
|
|
+ @mousedown.prevent="handleDragStart"
|
|
|
+ @touchstart.passive="handleDragStart"
|
|
|
+ @mouseenter="pauseAutoRotate"
|
|
|
+ @mouseleave="resumeAutoRotate">
|
|
|
+
|
|
|
+ <div class="dock-list" :class="{ 'is-ellipse-mode': mode === 'ellipse' }">
|
|
|
<div v-for="(item, index) in dockItems" :key="index" class="dock-item"
|
|
|
- :class="{ 'is-active': activeIndex === index, [`theme-${item.theme}`]: item.theme}"
|
|
|
+ :class="{
|
|
|
+ 'is-active': activeIndex === index,
|
|
|
+ [`theme-${item.theme}`]: item.theme,
|
|
|
+ 'is-front': mode === 'ellipse' && frontIndex === index
|
|
|
+ }"
|
|
|
+ :style="mode === 'ellipse' ? getEllipseStyle(index) : {}"
|
|
|
@click="handleSelect(index, item)"
|
|
|
@mouseenter="hoverIndex = index"
|
|
|
@mouseleave="hoverIndex = null"
|
|
|
@@ -26,8 +42,7 @@
|
|
|
<div class="item-icon">
|
|
|
<img v-if="item.imgUrl"
|
|
|
:src="(activeIndex === index || hoverIndex === index) && item.activeImgUrl ? item.activeImgUrl : item.imgUrl"
|
|
|
- class="custom-icon" />
|
|
|
-
|
|
|
+ class="custom-icon" draggable="false" />
|
|
|
<i v-else :class="item.iconClass"></i>
|
|
|
</div>
|
|
|
<div class="item-label">{{ item.label }}</div>
|
|
|
@@ -35,10 +50,13 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="nav-arrow right-arrow" :class="{ 'is-disabled': !canScrollRight, 'is-active': canScrollRight }"
|
|
|
- @click="scrollList(1)">
|
|
|
- <img v-if="canScrollRight" src="@/assets/main/main-right.png" class="arrow-img" />
|
|
|
- <img v-else src="@/assets/main/main-left.png" class="arrow-img left-facing" />
|
|
|
+ <div class="nav-arrow right-arrow"
|
|
|
+ :class="{ 'is-disabled': mode === 'linear' && !canScrollRight, 'is-active': mode === 'ellipse' || canScrollRight }"
|
|
|
+ @click="mode === 'ellipse' ? rotateMenu(1) : scrollList(1)"
|
|
|
+ @mouseenter="pauseAutoRotate"
|
|
|
+ @mouseleave="resumeAutoRotate">
|
|
|
+ <img v-if="mode === 'ellipse' || canScrollRight" src="@/assets/main/main-right.png" class="arrow-img" draggable="false" />
|
|
|
+ <img v-else src="@/assets/main/main-left.png" class="arrow-img left-facing" draggable="false" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -47,26 +65,16 @@
|
|
|
export default {
|
|
|
name: 'BottomDock',
|
|
|
props: {
|
|
|
- // 是否自动隐藏 (默认 true: 悬浮升起; false: 常驻显示)
|
|
|
- autoHide: {
|
|
|
- type: Boolean,
|
|
|
- default: true
|
|
|
- },
|
|
|
- // 距离屏幕底部的偏移量 (单位 px,正数代表往上抬高)
|
|
|
- bottomOffset: {
|
|
|
- type: Number,
|
|
|
- default: 0
|
|
|
- },
|
|
|
- // 允许外部传入的自定义容器样式 (如背景色、宽度等)
|
|
|
- customStyle: {
|
|
|
- type: Object,
|
|
|
- default: () => ({})
|
|
|
- },
|
|
|
- // 允许外部传入自定义 class (支持字符串、数组、对象)
|
|
|
- customClass: {
|
|
|
- type: [String, Array, Object],
|
|
|
- default: ''
|
|
|
- }
|
|
|
+ autoHide: { type: Boolean, default: true },
|
|
|
+ bottomOffset: { type: Number, default: 0 },
|
|
|
+ customStyle: { type: Object, default: () => ({}) },
|
|
|
+ customClass: { type: [String, Array, Object], default: '' },
|
|
|
+
|
|
|
+ mode: { type: String, default: 'linear' },
|
|
|
+ autoRotate: { type: Boolean, default: true },
|
|
|
+ autoRotateSpeed: { type: Number, default: 2500 },
|
|
|
+ radiusX: { type: Number, default: 380 },
|
|
|
+ radiusY: { type: Number, default: 60 },
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
@@ -75,71 +83,34 @@ export default {
|
|
|
dockExpanded: false,
|
|
|
canScrollLeft: false,
|
|
|
canScrollRight: true,
|
|
|
+
|
|
|
+ ellipseRotation: Math.PI / 2,
|
|
|
+ frontIndex: 0,
|
|
|
+
|
|
|
+ // --- 拖拽交互状态 ---
|
|
|
+ isDragging: false,
|
|
|
+ hasDragged: false,
|
|
|
+ startX: 0,
|
|
|
+ currentX: 0, // 记录实时拖动位置
|
|
|
+ startRotation: 0,
|
|
|
+ startFrontIndex: 0, // 记录拖拽开始时的正前方项目
|
|
|
+ rotateTimer: null,
|
|
|
+
|
|
|
dockItems: [
|
|
|
- {
|
|
|
- label: '首页',
|
|
|
- imgUrl: require('@/assets/main/main-home.png'),
|
|
|
- activeImgUrl: require('@/assets/main/main-home-hover.png'),
|
|
|
- route: '/home',
|
|
|
- theme: 'blue',
|
|
|
- },
|
|
|
- {
|
|
|
- label: '状态监控',
|
|
|
- imgUrl: require('@/assets/main/main-surve.png'),
|
|
|
- activeImgUrl: require('@/assets/main/main-surve-hover.png'),
|
|
|
- route: '/surve',
|
|
|
- theme: 'blue',
|
|
|
- },
|
|
|
- {
|
|
|
- label: '勤务管理',
|
|
|
- imgUrl: require('@/assets/main/main-security.png'),
|
|
|
- activeImgUrl: require('@/assets/main/main-security-hover.png'),
|
|
|
- route: '/security',
|
|
|
- theme: 'gold',
|
|
|
- },
|
|
|
- {
|
|
|
- label: '干线协调',
|
|
|
- imgUrl: require('@/assets/main/main-coor.png'),
|
|
|
- activeImgUrl: require('@/assets/main/main-coor-hover.png'),
|
|
|
- route: '/coor',
|
|
|
- theme: 'blue',
|
|
|
- },
|
|
|
- {
|
|
|
- label: '数据分析',
|
|
|
- imgUrl: require('@/assets/main/main-watch.png'),
|
|
|
- activeImgUrl: require('@/assets/main/main-watch-hover.png'),
|
|
|
- route: '/watch',
|
|
|
- theme: 'blue',
|
|
|
- },
|
|
|
- {
|
|
|
- label: '系统设置',
|
|
|
- imgUrl: require('@/assets/main/main-setting.png'),
|
|
|
- activeImgUrl: require('@/assets/main/main-setting-hover.png'),
|
|
|
- route: '/setting',
|
|
|
- theme: 'blue',
|
|
|
- },
|
|
|
- {
|
|
|
- label: '测试1',
|
|
|
- imgUrl: require('@/assets/main/main-home.png'),
|
|
|
- theme: 'blue',
|
|
|
- },
|
|
|
- {
|
|
|
- label: '测试2',
|
|
|
- imgUrl: require('@/assets/main/main-surve.png'),
|
|
|
- theme: 'blue',
|
|
|
- },
|
|
|
- {
|
|
|
- label: '测试3',
|
|
|
- imgUrl: require('@/assets/main/main-security.png'),
|
|
|
- theme: 'blue',
|
|
|
- },
|
|
|
+ { label: '首页', imgUrl: require('@/assets/main/main-home.png'), activeImgUrl: require('@/assets/main/main-home-hover.png'), route: '/home', theme: 'blue' },
|
|
|
+ { label: '状态监控', imgUrl: require('@/assets/main/main-surve.png'), activeImgUrl: require('@/assets/main/main-surve-hover.png'), route: '/surve', theme: 'blue' },
|
|
|
+ { label: '勤务管理', imgUrl: require('@/assets/main/main-security.png'), activeImgUrl: require('@/assets/main/main-security-hover.png'), route: '/security', theme: 'gold' },
|
|
|
+ { label: '干线协调', imgUrl: require('@/assets/main/main-coor.png'), activeImgUrl: require('@/assets/main/main-coor-hover.png'), route: '/coor', theme: 'blue' },
|
|
|
+ { label: '数据分析', imgUrl: require('@/assets/main/main-watch.png'), activeImgUrl: require('@/assets/main/main-watch-hover.png'), route: '/watch', theme: 'blue' },
|
|
|
+ { label: '系统设置', imgUrl: require('@/assets/main/main-setting.png'), activeImgUrl: require('@/assets/main/main-setting-hover.png'), route: '/setting', theme: 'blue' },
|
|
|
+ // { label: '测试1', imgUrl: require('@/assets/main/main-home.png'), activeImgUrl: require('@/assets/main/main-home-hover.png'), theme: 'blue' },
|
|
|
+ // { label: '测试2', imgUrl: require('@/assets/main/main-surve.png'), activeImgUrl: require('@/assets/main/main-surve-hover.png'), theme: 'blue' },
|
|
|
+ // { label: '测试3', imgUrl: require('@/assets/main/main-security.png'), activeImgUrl: require('@/assets/main/main-security-hover.png'), theme: 'blue' },
|
|
|
]
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
- // 动态计算 CSS 变量和合并自定义样式
|
|
|
dockStyles() {
|
|
|
- // 统一只传一个基础 bottom 偏移量,动画交由 CSS transform 处理
|
|
|
return {
|
|
|
'--dock-bottom': `${this.bottomOffset}px`,
|
|
|
...this.customStyle
|
|
|
@@ -156,16 +127,29 @@ export default {
|
|
|
},
|
|
|
mounted() {
|
|
|
this.$nextTick(() => {
|
|
|
- this.checkScrollState();
|
|
|
- window.addEventListener('resize', this.checkScrollState);
|
|
|
+ if (this.mode === 'linear') {
|
|
|
+ this.checkScrollState();
|
|
|
+ window.addEventListener('resize', this.checkScrollState);
|
|
|
+ } else {
|
|
|
+ this.resumeAutoRotate();
|
|
|
+ }
|
|
|
if (this.autoHide) {
|
|
|
document.addEventListener('mousemove', this.handleGlobalMouseMove);
|
|
|
}
|
|
|
+ window.addEventListener('mousemove', this.handleDragging);
|
|
|
+ window.addEventListener('mouseup', this.handleDragEnd);
|
|
|
+ window.addEventListener('touchmove', this.handleDragging, { passive: false });
|
|
|
+ window.addEventListener('touchend', this.handleDragEnd);
|
|
|
});
|
|
|
},
|
|
|
beforeDestroy() {
|
|
|
window.removeEventListener('resize', this.checkScrollState);
|
|
|
document.removeEventListener('mousemove', this.handleGlobalMouseMove);
|
|
|
+ window.removeEventListener('mousemove', this.handleDragging);
|
|
|
+ window.removeEventListener('mouseup', this.handleDragEnd);
|
|
|
+ window.removeEventListener('touchmove', this.handleDragging);
|
|
|
+ window.removeEventListener('touchend', this.handleDragEnd);
|
|
|
+ this.pauseAutoRotate();
|
|
|
},
|
|
|
methods: {
|
|
|
updateActiveIndexByRoute() {
|
|
|
@@ -176,10 +160,23 @@ export default {
|
|
|
|
|
|
if (matchIndex !== -1) {
|
|
|
this.activeIndex = matchIndex;
|
|
|
+ if (this.mode === 'ellipse') {
|
|
|
+ this.rotateTo(matchIndex);
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
handleSelect(index, item) {
|
|
|
- if (this.activeIndex === index) return;
|
|
|
+ if (this.hasDragged) {
|
|
|
+ this.hasDragged = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.mode === 'ellipse') {
|
|
|
+ this.rotateTo(index);
|
|
|
+ } else if (this.activeIndex === index) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
this.activeIndex = index;
|
|
|
|
|
|
if (item.route) {
|
|
|
@@ -189,10 +186,10 @@ export default {
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
this.$emit('change', item);
|
|
|
},
|
|
|
checkScrollState() {
|
|
|
+ if (this.mode !== 'linear') return;
|
|
|
const container = this.$refs.listContainer;
|
|
|
if (!container) return;
|
|
|
|
|
|
@@ -204,12 +201,10 @@ export default {
|
|
|
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;
|
|
|
}
|
|
|
@@ -230,13 +225,125 @@ export default {
|
|
|
behavior: 'smooth'
|
|
|
});
|
|
|
}
|
|
|
+ },
|
|
|
+ getEllipseStyle(index) {
|
|
|
+ const total = this.dockItems.length;
|
|
|
+ const baseAngle = (index / total) * Math.PI * 2;
|
|
|
+ const finalAngle = baseAngle + this.ellipseRotation;
|
|
|
+
|
|
|
+ const x = Math.cos(finalAngle) * this.radiusX;
|
|
|
+ const y = Math.sin(finalAngle) * this.radiusY;
|
|
|
+
|
|
|
+ const sinVal = Math.sin(finalAngle);
|
|
|
+ const normalizedDepth = (sinVal + 1) / 2;
|
|
|
+
|
|
|
+ let scale = 0.5 + (normalizedDepth * 0.7);
|
|
|
+ if (this.hoverIndex === index) scale *= 1.2;
|
|
|
+
|
|
|
+ const opacity = 0.3 + (normalizedDepth * 0.7);
|
|
|
+ const zIndex = Math.round(normalizedDepth * 100);
|
|
|
+
|
|
|
+ return {
|
|
|
+ position: 'absolute',
|
|
|
+ left: '50%',
|
|
|
+ top: '50%',
|
|
|
+ transform: `translate(-50%, -50%) translate(${x}px, ${y}px) scale(${scale})`,
|
|
|
+ opacity: opacity,
|
|
|
+ zIndex: zIndex,
|
|
|
+ transition: this.isDragging ? 'none' : 'transform 0.6s cubic-bezier(0.25, 1, 0.5, 1), opacity 0.6s'
|
|
|
+ };
|
|
|
+ },
|
|
|
+ rotateMenu(direction) {
|
|
|
+ const total = this.dockItems.length;
|
|
|
+ const stepAngle = (Math.PI * 2) / total;
|
|
|
+ this.ellipseRotation -= direction * stepAngle;
|
|
|
+ this.updateFrontIndex();
|
|
|
+ },
|
|
|
+ rotateTo(index) {
|
|
|
+ const total = this.dockItems.length;
|
|
|
+ let diff = this.frontIndex - index;
|
|
|
+ if (diff > total / 2) diff -= total;
|
|
|
+ if (diff < -total / 2) diff += total;
|
|
|
+
|
|
|
+ const stepAngle = (Math.PI * 2) / total;
|
|
|
+ this.ellipseRotation += diff * stepAngle;
|
|
|
+ this.frontIndex = index;
|
|
|
+ },
|
|
|
+ updateFrontIndex() {
|
|
|
+ const total = this.dockItems.length;
|
|
|
+ const stepAngle = (Math.PI * 2) / total;
|
|
|
+ const currentRot = ((this.ellipseRotation % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2);
|
|
|
+ let targetIndex = Math.round((Math.PI / 2 - currentRot) / stepAngle);
|
|
|
+ this.frontIndex = ((targetIndex % total) + total) % total;
|
|
|
+ },
|
|
|
+
|
|
|
+ // ================= 拖拽逻辑核心修改区 =================
|
|
|
+ handleDragStart(e) {
|
|
|
+ if (this.mode !== 'ellipse') return;
|
|
|
+ this.isDragging = true;
|
|
|
+ this.hasDragged = false;
|
|
|
+ this.pauseAutoRotate();
|
|
|
+ this.startX = e.clientX || (e.touches && e.touches[0].clientX);
|
|
|
+ this.currentX = this.startX;
|
|
|
+
|
|
|
+ // 记录拖拽前的完美状态
|
|
|
+ this.startRotation = this.ellipseRotation;
|
|
|
+ this.startFrontIndex = this.frontIndex;
|
|
|
+ },
|
|
|
+ handleDragging(e) {
|
|
|
+ if (!this.isDragging) return;
|
|
|
+ this.currentX = e.clientX || (e.touches && e.touches[0].clientX);
|
|
|
+ const diffX = this.currentX - this.startX;
|
|
|
+ if (Math.abs(diffX) > 5) this.hasDragged = true;
|
|
|
+
|
|
|
+ // 拖动时,视觉上跟随鼠标
|
|
|
+ this.ellipseRotation = this.startRotation - (diffX / 350);
|
|
|
+ this.updateFrontIndex(); // 让中间的图标实时发光
|
|
|
+ },
|
|
|
+ handleDragEnd() {
|
|
|
+ if (!this.isDragging) return;
|
|
|
+ this.isDragging = false;
|
|
|
+
|
|
|
+ const diffX = this.currentX - this.startX;
|
|
|
+ const threshold = 40; // 触发切换的距离阈值(滑动超过 40px 就切换)
|
|
|
+
|
|
|
+ // 【关键】无论拖动多远,先把底层状态恢复到起点
|
|
|
+ // 配合 isDragging = false 时的 transition 过渡,这能保证完美的滑动动画
|
|
|
+ this.ellipseRotation = this.startRotation;
|
|
|
+ this.frontIndex = this.startFrontIndex;
|
|
|
+
|
|
|
+ if (diffX < -threshold) {
|
|
|
+ // 向左滑:精准切换到“下一个”
|
|
|
+ this.rotateMenu(1);
|
|
|
+ } else if (diffX > threshold) {
|
|
|
+ // 向右滑:精准切换到“上一个”
|
|
|
+ this.rotateMenu(-1);
|
|
|
+ } else {
|
|
|
+ // 滑动距离不够,原地吸附回正(无操作,因为上面已经还原了 startRotation)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.resumeAutoRotate();
|
|
|
+ },
|
|
|
+ // ======================================================
|
|
|
+
|
|
|
+ resumeAutoRotate() {
|
|
|
+ if (this.mode !== 'ellipse' || !this.autoRotate || this.rotateTimer) return;
|
|
|
+ this.rotateTimer = setInterval(() => {
|
|
|
+ this.rotateMenu(-1);
|
|
|
+ }, this.autoRotateSpeed);
|
|
|
+ },
|
|
|
+ pauseAutoRotate() {
|
|
|
+ if (this.rotateTimer) {
|
|
|
+ clearInterval(this.rotateTimer);
|
|
|
+ this.rotateTimer = null;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-/* ================= 整体容器布局 ================= */
|
|
|
+/* ================= 以下是你的原版 CSS (原封不动) ================= */
|
|
|
.dock-wrapper {
|
|
|
display: flex !important;
|
|
|
flex-direction: row !important;
|
|
|
@@ -253,7 +360,6 @@ export default {
|
|
|
pointer-events: auto !important;
|
|
|
}
|
|
|
|
|
|
-/* === 状态 A:自动隐藏 === */
|
|
|
.dock-wrapper.is-auto-hide {
|
|
|
transform: translateY(calc(100% - clamp(15px, 4vh, 20px)));
|
|
|
pointer-events: none;
|
|
|
@@ -263,7 +369,6 @@ export default {
|
|
|
pointer-events: auto;
|
|
|
}
|
|
|
|
|
|
-/* 中间触发热区:只覆盖发光线范围,pointer-events 始终开启 */
|
|
|
.dock-wrapper.is-auto-hide::after {
|
|
|
content: '';
|
|
|
position: absolute;
|
|
|
@@ -276,14 +381,12 @@ export default {
|
|
|
pointer-events: auto;
|
|
|
}
|
|
|
|
|
|
-/* 发光的指示线自适应 */
|
|
|
.dock-wrapper.is-auto-hide::before {
|
|
|
content: '';
|
|
|
position: absolute;
|
|
|
top: 0;
|
|
|
left: 50%;
|
|
|
transform: translateX(-50%);
|
|
|
- /* 宽度和厚度也做一点自适应,小屏自动变短点 */
|
|
|
width: clamp(150px, 20vw, 250px);
|
|
|
height: clamp(3px, 0.5vh, 5px);
|
|
|
background: rgba(0, 229, 255, 0.6);
|
|
|
@@ -297,7 +400,6 @@ export default {
|
|
|
opacity: 0;
|
|
|
}
|
|
|
|
|
|
-/* === 状态 B:常驻显示 === */
|
|
|
.dock-wrapper.is-always-show {
|
|
|
transform: translateY(0);
|
|
|
}
|
|
|
@@ -306,7 +408,6 @@ export default {
|
|
|
display: none;
|
|
|
}
|
|
|
|
|
|
-/* ================= 内部列表与滚动容器 ================= */
|
|
|
.dock-list-container {
|
|
|
width: 750px;
|
|
|
height: 160px;
|
|
|
@@ -333,7 +434,6 @@ export default {
|
|
|
gap: 30px;
|
|
|
}
|
|
|
|
|
|
-/* ================= 左右控制箭头 ================= */
|
|
|
.nav-arrow {
|
|
|
flex-shrink: 0;
|
|
|
width: 40px;
|
|
|
@@ -374,7 +474,6 @@ export default {
|
|
|
opacity: 0.6;
|
|
|
}
|
|
|
|
|
|
-/* ================= 单个导航项 ================= */
|
|
|
.dock-item {
|
|
|
flex-shrink: 0;
|
|
|
position: relative;
|
|
|
@@ -404,7 +503,6 @@ export default {
|
|
|
letter-spacing: 1px;
|
|
|
}
|
|
|
|
|
|
-/* ================= 交互状态:悬浮与选中 ================= */
|
|
|
.dock-item:hover {
|
|
|
transform: translateY(-15px) scale(1.15);
|
|
|
}
|
|
|
@@ -454,4 +552,41 @@ export default {
|
|
|
.dock-item.is-active .custom-icon {
|
|
|
filter: drop-shadow(0 0 8px rgba(0, 229, 255, 0.8));
|
|
|
}
|
|
|
+
|
|
|
+/* ================= 针对 3D 椭圆模式的样式覆盖 ================= */
|
|
|
+.dock-list-container.is-ellipse-mode {
|
|
|
+ overflow: visible !important;
|
|
|
+ cursor: grab;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.dock-list-container.is-ellipse-mode:active {
|
|
|
+ cursor: grabbing;
|
|
|
+}
|
|
|
+
|
|
|
+.dock-list.is-ellipse-mode {
|
|
|
+ position: relative !important;
|
|
|
+ width: 0 !important;
|
|
|
+ height: 0 !important;
|
|
|
+ min-width: 0 !important;
|
|
|
+ padding-bottom: 0 !important;
|
|
|
+ gap: 0 !important;
|
|
|
+}
|
|
|
+
|
|
|
+.dock-list.is-ellipse-mode .dock-item {
|
|
|
+ position: absolute;
|
|
|
+}
|
|
|
+.dock-list.is-ellipse-mode .dock-item:hover {
|
|
|
+ transform: none;
|
|
|
+}
|
|
|
+
|
|
|
+.dock-list.is-ellipse-mode .dock-item.is-front .item-label {
|
|
|
+ color: #ffffff;
|
|
|
+ font-weight: bold;
|
|
|
+ text-shadow: 0 0 10px #00e5ff;
|
|
|
+}
|
|
|
+.dock-list.is-ellipse-mode .dock-item.is-front .custom-icon {
|
|
|
+ filter: drop-shadow(0 0 10px rgba(0, 229, 255, 0.8));
|
|
|
+}
|
|
|
</style>
|