dapaijizhang3/components/guide/guide.js

209 lines
5.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 新用户引导组件
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() {}
}
})