dapaijizhang3/components/guide/guide.js

209 lines
5.5 KiB
JavaScript
Raw Permalink Normal View History

2025-11-20 16:42:59 +08:00
// 新用户引导组件
Component({
properties: {
show: {
type: Boolean,
value: false
},
steps: {
type: Array,
value: []
}
},
data: {
currentStep: 0,
totalSteps: 0,
title: '',
description: '',
spotlightStyle: {},
contentStyle: '',
arrowDirection: 'top',
showArrow: true
},
observers: {
'show': function(show) {
if (show && this.data.steps.length > 0) {
this.setData({
currentStep: 0,
totalSteps: this.data.steps.length
})
this.showStep(0)
}
}
},
methods: {
// 显示指定步骤
showStep(stepIndex) {
if (stepIndex < 0 || stepIndex >= this.data.steps.length) {
return
}
const step = this.data.steps[stepIndex]
this.setData({
currentStep: stepIndex,
title: step.title,
description: step.description,
showArrow: step.showArrow !== false
})
// 延迟获取元素位置,确保页面已渲染
setTimeout(() => {
this.updateSpotlight(step)
}, 50)
},
// 更新高亮区域
updateSpotlight(step) {
const query = wx.createSelectorQuery().in(this)
// 查询目标元素(在页面中)
const pageQuery = wx.createSelectorQuery()
pageQuery.select(step.selector).boundingClientRect()
pageQuery.selectViewport().scrollOffset()
pageQuery.exec((res) => {
if (!res || !res[0]) {
console.warn('未找到目标元素:', step.selector)
return
}
const rect = res[0]
const scroll = res[1]
// 计算高亮区域样式添加padding扩展区域
const padding = step.padding || 8
const spotlightStyle = {
top: rect.top - padding,
left: rect.left - padding,
width: rect.width + padding * 2,
height: rect.height + padding * 2,
borderRadius: step.borderRadius || 16
}
// 计算提示内容位置
const contentStyle = this.calculateContentPosition(spotlightStyle, step.position || 'bottom')
this.setData({
spotlightStyle,
contentStyle: contentStyle.style,
arrowDirection: contentStyle.arrowDirection
})
})
},
// 计算提示内容位置
calculateContentPosition(spotlightStyle, preferredPosition) {
const windowInfo = wx.getWindowInfo()
const screenHeight = windowInfo.windowHeight
const screenWidth = windowInfo.windowWidth
const contentWidth = 600 // rpx转换为px约300
const contentMaxHeight = 400 // 估算最大高度
let position = preferredPosition
let top = 0
let left = 0
let arrowDirection = 'top'
// 计算中心点
const spotlightCenterX = spotlightStyle.left + spotlightStyle.width / 2
const spotlightCenterY = spotlightStyle.top + spotlightStyle.height / 2
// 根据位置偏好计算
switch (position) {
case 'top':
top = spotlightStyle.top - contentMaxHeight - 40
left = Math.max(20, Math.min(spotlightCenterX - contentWidth / 2, screenWidth - contentWidth - 20))
arrowDirection = 'bottom'
// 如果顶部空间不足,改为底部
if (top < 20) {
position = 'bottom'
}
break
case 'bottom':
top = spotlightStyle.top + spotlightStyle.height + 20
left = Math.max(20, Math.min(spotlightCenterX - contentWidth / 2, screenWidth - contentWidth - 20))
arrowDirection = 'top'
// 如果底部空间不足,改为顶部
if (top + contentMaxHeight > screenHeight - 20) {
position = 'top'
top = spotlightStyle.top - contentMaxHeight - 40
arrowDirection = 'bottom'
}
break
case 'left':
left = spotlightStyle.left - contentWidth - 20
top = Math.max(20, Math.min(spotlightCenterY - 100, screenHeight - contentMaxHeight - 20))
arrowDirection = 'right'
// 如果左侧空间不足,改为右侧
if (left < 20) {
position = 'right'
}
break
case 'right':
left = spotlightStyle.left + spotlightStyle.width + 20
top = Math.max(20, Math.min(spotlightCenterY - 100, screenHeight - contentMaxHeight - 20))
arrowDirection = 'left'
// 如果右侧空间不足,改为左侧
if (left + contentWidth > screenWidth - 20) {
position = 'left'
left = spotlightStyle.left - contentWidth - 20
arrowDirection = 'right'
}
break
}
// 如果重新计算了位置,递归调用
if (position !== preferredPosition) {
return this.calculateContentPosition(spotlightStyle, position)
}
return {
style: `top: ${top}px; left: ${left}px;`,
arrowDirection
}
},
// 下一步
handleNext() {
const nextStep = this.data.currentStep + 1
if (nextStep >= this.data.steps.length) {
// 引导完成
this.handleComplete()
} else {
// 显示下一步
this.showStep(nextStep)
}
},
// 跳过引导
handleSkip() {
this.handleComplete()
},
// 完成引导
handleComplete() {
this.triggerEvent('complete')
},
// 点击遮罩(不做任何操作,防止穿透)
handleMaskTap() {
// 可选:点击遮罩也进入下一步
// this.handleNext()
},
// 阻止滚动穿透
preventMove() {}
}
})