feat: 添加 SpotlightOverlay 组件,更新引导步骤组件以支持高亮功能,调整样式和路由配置
This commit is contained in:
1 parent
aeec7fceed
commit
2b36342304
36 files changed
+863
-29
No files matched your search
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div v-if="visible" class="spotlight-overlay">
|
||||
<div
|
||||
class="spotlight-hole"
|
||||
:style="{
|
||||
top: rect.top + 'px',
|
||||
left: rect.left + 'px',
|
||||
width: rect.width + 'px',
|
||||
height: rect.height + 'px',
|
||||
borderRadius: borderRadius + 'px',
|
||||
boxShadow: `0 0 0 9999px rgba(0,0,0,0.6)`
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, watch, onMounted, onBeforeUnmount } from 'vue'
|
||||
|
||||
interface Props {
|
||||
/** 是否可见 */
|
||||
visible?: boolean
|
||||
/** 目标元素选择器 */
|
||||
targetSelector?: string | null
|
||||
/** 额外内边距 */
|
||||
padding?: number
|
||||
/** 圆角 */
|
||||
borderRadius?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
targetSelector: null,
|
||||
padding: 8,
|
||||
borderRadius: 16
|
||||
})
|
||||
|
||||
const rect = reactive({ top: 0, left: 0, width: 0, height: 0 })
|
||||
|
||||
const measure = () => {
|
||||
if (!props.visible || !props.targetSelector) return
|
||||
const el = document.querySelector(props.targetSelector) as HTMLElement | null
|
||||
if (!el) return
|
||||
const r = el.getBoundingClientRect()
|
||||
const pad = Math.max(0, props.padding)
|
||||
// 使用视口坐标(fixed层),不叠加滚动偏移
|
||||
rect.top = r.top - pad
|
||||
rect.left = r.left - pad
|
||||
rect.width = r.width + pad * 2
|
||||
rect.height = r.height + pad * 2
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.visible, props.targetSelector, props.padding],
|
||||
() => {
|
||||
requestAnimationFrame(measure)
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const onResize = () => measure()
|
||||
const onScroll = () => measure()
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', onResize)
|
||||
window.addEventListener('scroll', onScroll, { passive: true })
|
||||
measure()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', onResize)
|
||||
window.removeEventListener('scroll', onScroll)
|
||||
})
|
||||
|
||||
const borderRadius = props.borderRadius
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.spotlight-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 100000;
|
||||
}
|
||||
|
||||
.spotlight-hole {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user