Files
WXProgram/miniprogram/components/bottom-modal/bottom-modal.js

143 lines
4.2 KiB
JavaScript
Raw Normal View History

2025-10-16 21:32:16 +08:00
Component({
options: {
// 关键修改:设置样式隔离选项
styleIsolation: 'shared' // 完全共享页面和组件样式
},
properties: {
visible: {
type: Boolean,
value: false,
observer: function (newVal) {
if (newVal) {
this.showModal();
} else {
this.hideModal();
}
}
},
modalContentClass: {
type: String,
value: ''
}
},
data: {
animationClass: 'modal-transition', // 保持过渡效果类
containerHeight: '25vh', // 设置为1/4屏幕高度
initialTranslate: '100%', // 初始在屏幕外
startY: 0, // 触摸起始位置
currentState: 'hidden', // 'hidden', 'bottom', 'half', 'full'
states: {
bottom: { height: '25vh', translate: '0' }, // 1/4屏幕高度设置translate为0使其正确显示在底部
full: { height: '100vh', translate: '0' } // 全屏高度
}
},
methods: {
showModal() {
this.setData({
visible: true,
currentState: 'bottom', // 默认显示1/4屏幕高度
containerHeight: this.data.states['bottom'].height,
initialTranslate: this.data.states['bottom'].translate
});
},
hideModal() {
this.setData({
initialTranslate: '100%', // 设置为底部位置
currentState: 'hidden'
});
setTimeout(() => {
if (this.data.currentState === 'hidden') {
this.setData({ visible: false });
}
}, 300); // 与动画时间一致
},
onHideModal() {
this._triggerCloseEvent();
},
// 遮罩层点击事件(如果有遮罩层的话)
onOverlayTap() {
this._triggerCloseEvent();
},
// 共用的关闭事件触发方法
_triggerCloseEvent() {
this.triggerEvent('close');
},
preventTouchMove() {},
onTouchStart(e) {
this.setData({ startY: e.touches[0].clientY });
this.setData({ animationClass: '' }); // 开始拖动时移除过渡效果,使拖动更跟手
},
onTouchMove(e) {
if (!this.data.visible) return;
const currentY = e.touches[0].clientY;
const deltaY = currentY - this.data.startY;
let newTranslate = '';
// 根据当前状态和拖动方向计算新的位移
if (this.data.currentState === 'full') {
newTranslate = `calc(0px + ${deltaY}px)`; // 从全屏位置开始拖
} else if (this.data.currentState === 'bottom') {
newTranslate = `calc(75vh + ${deltaY}px)`; // 从1/4屏幕高度位置开始拖
}
// 限制拖动范围,不能拖出屏幕顶部或底部之外
if (this.data.currentState === 'full' && parseInt(newTranslate) > 0) {
newTranslate = '0';
} else if (this.data.currentState === 'bottom' && deltaY < -300) {
// 向下拖动不超过屏幕
newTranslate = 'calc(75vh - 300px)';
}
this.setData({ initialTranslate: newTranslate });
},
onTouchEnd(e) {
const endY = e.changedTouches[0].clientY;
const deltaY = endY - this.data.startY;
const threshold = 50; // 判断状态切换的阈值
// 根据拖动距离和方向,以及当前状态,决定下一个状态
let nextState = this.data.currentState;
if (Math.abs(deltaY) > threshold) {
if (deltaY > 0) { // 向下拖
if (this.data.currentState === 'full') {
nextState = 'bottom';
}
} else { // 向上拖
if (this.data.currentState === 'bottom') {
nextState = 'full';
}
}
} else {
// 拖动距离小于阈值,则回到当前状态
nextState = this.data.currentState;
}
// 在切换状态前,添加过渡动画类
this.setData({
animationClass: 'modal-transition'
}, () => {
// 在回调中执行状态切换,确保动画类已经设置
this.switchToState(nextState);
});
},
switchToState(state) {
if (state === 'hidden') {
this.hideModal();
return;
}
const targetState = this.data.states[state];
if (targetState) {
this.setData({
currentState: state,
containerHeight: targetState.height,
initialTranslate: targetState.translate
});
// 通知父组件状态变化
this.triggerEvent('stateChange', { state: state });
}
}
}
});