更新DockLayout组件和相关指示器实现
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<div
|
||||
v-if="activeDockZone"
|
||||
class="dock-preview-area"
|
||||
:style="previewAreaStyle"
|
||||
:style="enhancedPreviewAreaStyle"
|
||||
></div>
|
||||
<!-- 1. 定义可复用组件(symbol):封装所有渐变和路径(只写一次) -->
|
||||
<svg width="0" height="0" viewBox="0 0 40 40" aria-hidden="true">
|
||||
@@ -90,6 +90,78 @@
|
||||
clip-rule="evenodd"
|
||||
d="M10 13 L30 13 L30 19 L10 19 Z" />
|
||||
</svg>
|
||||
|
||||
<!-- 右指示:根据activeDockZone状态显示和高亮 -->
|
||||
<svg
|
||||
width="41"
|
||||
height="41"
|
||||
viewBox="0 0 40 40"
|
||||
aria-hidden="true"
|
||||
class="indicator-right"
|
||||
:class="{ 'active': activeDockZone === 'right' }"
|
||||
@mouseenter="handleMouseEnter('right')"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<use xlink:href="#shared-icon" transform="rotate(90 20 20)" />
|
||||
<path
|
||||
fill="#4C5E83"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M19 8 L32 8 L32 31 L31 32 L20 32 L19 31 Z" />
|
||||
<path
|
||||
fill="url(#Area)"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M21 14 L30 14 L30 30 L21 30 Z" />
|
||||
</svg>
|
||||
|
||||
<!-- 下指示:根据activeDockZone状态显示和高亮 -->
|
||||
<svg
|
||||
width="41"
|
||||
height="41"
|
||||
viewBox="0 0 40 40"
|
||||
aria-hidden="true"
|
||||
class="indicator-bottom"
|
||||
:class="{ 'active': activeDockZone === 'bottom' }"
|
||||
@mouseenter="handleMouseEnter('bottom')"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<use xlink:href="#shared-icon" transform="rotate(180 20 20)" />
|
||||
<path
|
||||
fill="#4C5E83"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8 19 L32 19 L32 31 L31 32 L9 32 L8 31 Z" />
|
||||
<path
|
||||
fill="url(#Area)"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10 24 L30 24 L30 30 L10 30 Z" />
|
||||
</svg>
|
||||
|
||||
<!-- 左指示:根据activeDockZone状态显示和高亮 -->
|
||||
<svg
|
||||
width="41"
|
||||
height="41"
|
||||
viewBox="0 0 40 40"
|
||||
aria-hidden="true"
|
||||
class="indicator-left"
|
||||
:class="{ 'active': activeDockZone === 'left' }"
|
||||
@mouseenter="handleMouseEnter('left')"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<use xlink:href="#shared-icon" transform="rotate(270 20 20)" />
|
||||
<path
|
||||
fill="#4C5E83"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8 8 L21 8 L21 31 L19 32 L9 32 L8 31 Z" />
|
||||
<path
|
||||
fill="url(#Area)"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10 14 L19 14 L19 30 L10 30 Z" />
|
||||
</svg>
|
||||
|
||||
|
||||
|
||||
@@ -123,6 +195,9 @@
|
||||
viewBox="0 0 40 40"
|
||||
aria-hidden="false"
|
||||
class="dock-top-area"
|
||||
@mouseenter="handleSubAreaMouseEnter('top-area')"
|
||||
@mouseleave="handleSubAreaMouseLeave"
|
||||
@mousemove="handleSubAreaMouseMove('top-area', $event)"
|
||||
>
|
||||
<use xlink:href="#shared-area" />
|
||||
<path
|
||||
@@ -150,6 +225,9 @@
|
||||
viewBox="0 0 40 40"
|
||||
aria-hidden="false"
|
||||
class="dock-right-area"
|
||||
@mouseenter="handleSubAreaMouseEnter('right-area')"
|
||||
@mouseleave="handleSubAreaMouseLeave"
|
||||
@mousemove="handleSubAreaMouseMove('right-area', $event)"
|
||||
>
|
||||
<use xlink:href="#shared-area" />
|
||||
<path
|
||||
@@ -177,6 +255,9 @@
|
||||
viewBox="0 0 40 40"
|
||||
aria-hidden="false"
|
||||
class="dock-bottom-area"
|
||||
@mouseenter="handleSubAreaMouseEnter('bottom-area')"
|
||||
@mouseleave="handleSubAreaMouseLeave"
|
||||
@mousemove="handleSubAreaMouseMove('bottom-area', $event)"
|
||||
>
|
||||
<use xlink:href="#shared-area" />
|
||||
<path
|
||||
@@ -204,6 +285,9 @@
|
||||
viewBox="0 0 40 40"
|
||||
aria-hidden="false"
|
||||
class="dock-left-area"
|
||||
@mouseenter="handleSubAreaMouseEnter('left-area')"
|
||||
@mouseleave="handleSubAreaMouseLeave"
|
||||
@mousemove="handleSubAreaMouseMove('left-area', $event)"
|
||||
>
|
||||
<use xlink:href="#shared-area" />
|
||||
<path
|
||||
@@ -415,6 +499,122 @@ const handleMouseLeave = () => {
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 处理子区域鼠标进入事件
|
||||
const handleSubAreaMouseEnter = (area) => {
|
||||
// 清除可能存在的离开定时器
|
||||
if (mouseLeaveTimer) {
|
||||
clearTimeout(mouseLeaveTimer)
|
||||
mouseLeaveTimer = null
|
||||
}
|
||||
hoveredZone.value = area
|
||||
}
|
||||
|
||||
// 处理子区域鼠标离开事件
|
||||
const handleSubAreaMouseLeave = () => {
|
||||
// 添加短暂延迟,避免快速进出导致的闪烁
|
||||
mouseLeaveTimer = setTimeout(() => {
|
||||
hoveredZone.value = null
|
||||
mouseLeaveTimer = null
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 处理子区域鼠标移动事件
|
||||
const handleSubAreaMouseMove = (area, event) => {
|
||||
// 更新鼠标位置信息
|
||||
const rect = event.currentTarget.getBoundingClientRect()
|
||||
const mouseX = event.clientX - rect.left
|
||||
const mouseY = event.clientY - rect.top
|
||||
|
||||
// 可以在这里添加更复杂的鼠标位置计算逻辑
|
||||
// 例如根据鼠标在子区域内的位置动态调整依靠区的大小
|
||||
|
||||
// 保持当前悬停状态
|
||||
hoveredZone.value = area
|
||||
}
|
||||
|
||||
// 检测鼠标所在的Area组件
|
||||
const detectMouseArea = (mouseX, mouseY) => {
|
||||
try {
|
||||
// 查找所有包含Area组件的DOM元素
|
||||
const areaElements = document.querySelectorAll('.vs-area')
|
||||
|
||||
for (let element of areaElements) {
|
||||
const rect = element.getBoundingClientRect()
|
||||
|
||||
// 检查鼠标位置是否在当前Area元素内
|
||||
if (mouseX >= rect.left && mouseX <= rect.right &&
|
||||
mouseY >= rect.top && mouseY <= rect.bottom) {
|
||||
return {
|
||||
element: element,
|
||||
rect: rect,
|
||||
id: element.getAttribute('data-area-id') || element.dataset.id || 'unknown'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
} catch (error) {
|
||||
console.warn('检测鼠标区域时出错:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 计算基于鼠标位置的半透明依靠区样式
|
||||
const computeRelianceAreaStyle = (area, zone) => {
|
||||
if (!area || !props.targetRect) return {}
|
||||
|
||||
const { left: targetLeft, top: targetTop, width: targetWidth, height: targetHeight } = props.targetRect
|
||||
const areaRect = area.rect
|
||||
|
||||
// 计算鼠标在Area内容区内的相对位置
|
||||
const mouseRelativeX = props.mousePosition.x - areaRect.left
|
||||
const mouseRelativeY = props.mousePosition.y - areaRect.top
|
||||
|
||||
// 根据不同区域和鼠标位置计算依靠区
|
||||
const threshold = 0.3
|
||||
|
||||
switch (zone) {
|
||||
case 'top-area':
|
||||
return {
|
||||
position: 'absolute',
|
||||
left: `${targetLeft}px`,
|
||||
top: `${targetTop}px`,
|
||||
width: `${targetWidth}px`,
|
||||
height: `${targetHeight * threshold}px`
|
||||
}
|
||||
|
||||
case 'bottom-area':
|
||||
return {
|
||||
position: 'absolute',
|
||||
left: `${targetLeft}px`,
|
||||
top: `${targetTop + targetHeight * (1 - threshold)}px`,
|
||||
width: `${targetWidth}px`,
|
||||
height: `${targetHeight * threshold}px`
|
||||
}
|
||||
|
||||
case 'left-area':
|
||||
return {
|
||||
position: 'absolute',
|
||||
left: `${targetLeft}px`,
|
||||
top: `${targetTop}px`,
|
||||
width: `${targetWidth * threshold}px`,
|
||||
height: `${targetHeight}px`
|
||||
}
|
||||
|
||||
case 'right-area':
|
||||
return {
|
||||
position: 'absolute',
|
||||
left: `${targetLeft + targetWidth * (1 - threshold)}px`,
|
||||
top: `${targetTop}px`,
|
||||
width: `${targetWidth * threshold}px`,
|
||||
height: `${targetHeight}px`
|
||||
}
|
||||
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (mouseLeaveTimer) {
|
||||
@@ -446,7 +646,7 @@ const activeDockZone = computed(() => {
|
||||
return hoveredZone.value
|
||||
})
|
||||
|
||||
// 计算半透明区域框的样式
|
||||
// 计算半透明区域框的基础样式
|
||||
const previewAreaStyle = computed(() => {
|
||||
if (!activeDockZone.value) return {};
|
||||
|
||||
@@ -504,6 +704,27 @@ const previewAreaStyle = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// 增强的半透明区域框样式,支持基于鼠标位置的动态计算
|
||||
const enhancedPreviewAreaStyle = computed(() => {
|
||||
if (!activeDockZone.value) return {};
|
||||
|
||||
// 如果是子区域指示器,使用增强的计算逻辑
|
||||
if (activeDockZone.value.includes('-area')) {
|
||||
// 检测鼠标所在的Area组件
|
||||
const mouseArea = detectMouseArea(props.mousePosition.x, props.mousePosition.y)
|
||||
|
||||
// 使用基于鼠标位置的计算逻辑
|
||||
const relianceStyle = computeRelianceAreaStyle(mouseArea, activeDockZone.value)
|
||||
|
||||
// 合并基础样式和增强样式
|
||||
const basicStyle = previewAreaStyle.value
|
||||
return { ...basicStyle, ...relianceStyle }
|
||||
}
|
||||
|
||||
// 对于非子区域指示器,使用原有的计算逻辑
|
||||
return previewAreaStyle.value
|
||||
})
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits(['zone-active'])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user