457 lines
10 KiB
JavaScript
457 lines
10 KiB
JavaScript
|
|
// 🎬 页面过渡组件逻辑
|
||
|
|
const animationManager = require('../../utils/animation-manager.js');
|
||
|
|
|
||
|
|
Component({
|
||
|
|
properties: {
|
||
|
|
// 过渡类型
|
||
|
|
transitionType: {
|
||
|
|
type: String,
|
||
|
|
value: 'fade',
|
||
|
|
observer: 'onTransitionTypeChange'
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否显示
|
||
|
|
visible: {
|
||
|
|
type: Boolean,
|
||
|
|
value: true,
|
||
|
|
observer: 'onVisibleChange'
|
||
|
|
},
|
||
|
|
|
||
|
|
// 动画时长
|
||
|
|
duration: {
|
||
|
|
type: Number,
|
||
|
|
value: 300
|
||
|
|
},
|
||
|
|
|
||
|
|
// 缓动函数
|
||
|
|
easing: {
|
||
|
|
type: String,
|
||
|
|
value: 'ease-out'
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否显示遮罩
|
||
|
|
showOverlay: {
|
||
|
|
type: Boolean,
|
||
|
|
value: false
|
||
|
|
},
|
||
|
|
|
||
|
|
// 遮罩类型
|
||
|
|
overlayType: {
|
||
|
|
type: String,
|
||
|
|
value: 'fade-black'
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否显示加载
|
||
|
|
showLoading: {
|
||
|
|
type: Boolean,
|
||
|
|
value: false
|
||
|
|
},
|
||
|
|
|
||
|
|
// 加载文本
|
||
|
|
loadingText: {
|
||
|
|
type: String,
|
||
|
|
value: '加载中...'
|
||
|
|
},
|
||
|
|
|
||
|
|
// 是否启用硬件加速
|
||
|
|
hardwareAccelerated: {
|
||
|
|
type: Boolean,
|
||
|
|
value: true
|
||
|
|
},
|
||
|
|
|
||
|
|
// 调试模式
|
||
|
|
debug: {
|
||
|
|
type: Boolean,
|
||
|
|
value: false
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
data: {
|
||
|
|
// 过渡状态
|
||
|
|
transitionState: 'idle', // idle, entering, entered, leaving, left
|
||
|
|
|
||
|
|
// 动画数据
|
||
|
|
animationData: null,
|
||
|
|
loadingAnimation: null,
|
||
|
|
|
||
|
|
// 样式
|
||
|
|
animationStyle: '',
|
||
|
|
overlayStyle: '',
|
||
|
|
|
||
|
|
// CSS类
|
||
|
|
transitionClass: '',
|
||
|
|
overlayClass: ''
|
||
|
|
},
|
||
|
|
|
||
|
|
lifetimes: {
|
||
|
|
attached() {
|
||
|
|
console.log('🎬 页面过渡组件加载');
|
||
|
|
this.initComponent();
|
||
|
|
},
|
||
|
|
|
||
|
|
ready() {
|
||
|
|
console.log('🎬 页面过渡组件就绪');
|
||
|
|
this.setupInitialState();
|
||
|
|
},
|
||
|
|
|
||
|
|
detached() {
|
||
|
|
console.log('🎬 页面过渡组件卸载');
|
||
|
|
this.cleanup();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
methods: {
|
||
|
|
// 初始化组件
|
||
|
|
initComponent() {
|
||
|
|
// 设置初始过渡类
|
||
|
|
this.updateTransitionClass();
|
||
|
|
|
||
|
|
// 设置遮罩类
|
||
|
|
this.updateOverlayClass();
|
||
|
|
|
||
|
|
// 创建加载动画
|
||
|
|
this.createLoadingAnimation();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 设置初始状态
|
||
|
|
setupInitialState() {
|
||
|
|
if (this.properties.visible) {
|
||
|
|
this.enter();
|
||
|
|
} else {
|
||
|
|
this.setData({
|
||
|
|
transitionState: 'left'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 过渡类型变化处理
|
||
|
|
onTransitionTypeChange(newType, oldType) {
|
||
|
|
if (newType !== oldType) {
|
||
|
|
this.updateTransitionClass();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 可见性变化处理
|
||
|
|
onVisibleChange(visible, wasVisible) {
|
||
|
|
if (visible === wasVisible) return;
|
||
|
|
|
||
|
|
if (visible) {
|
||
|
|
this.enter();
|
||
|
|
} else {
|
||
|
|
this.leave();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎭 ===== 过渡控制 =====
|
||
|
|
|
||
|
|
// 进入动画
|
||
|
|
async enter() {
|
||
|
|
if (this.data.transitionState === 'entering' || this.data.transitionState === 'entered') {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('🎬 开始进入动画:', this.properties.transitionType);
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
transitionState: 'entering'
|
||
|
|
});
|
||
|
|
|
||
|
|
this.triggerEvent('transitionstart', {
|
||
|
|
type: 'enter',
|
||
|
|
transitionType: this.properties.transitionType
|
||
|
|
});
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 创建进入动画
|
||
|
|
const animation = this.createEnterAnimation();
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
animationData: animation.export()
|
||
|
|
});
|
||
|
|
|
||
|
|
// 等待动画完成
|
||
|
|
await this.waitForAnimation();
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
transitionState: 'entered'
|
||
|
|
});
|
||
|
|
|
||
|
|
this.triggerEvent('transitionend', {
|
||
|
|
type: 'enter',
|
||
|
|
transitionType: this.properties.transitionType
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('✅ 进入动画完成');
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
console.error('❌ 进入动画失败:', error);
|
||
|
|
this.setData({
|
||
|
|
transitionState: 'entered'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 退出动画
|
||
|
|
async leave() {
|
||
|
|
if (this.data.transitionState === 'leaving' || this.data.transitionState === 'left') {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('🎬 开始退出动画:', this.properties.transitionType);
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
transitionState: 'leaving'
|
||
|
|
});
|
||
|
|
|
||
|
|
this.triggerEvent('transitionstart', {
|
||
|
|
type: 'leave',
|
||
|
|
transitionType: this.properties.transitionType
|
||
|
|
});
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 创建退出动画
|
||
|
|
const animation = this.createLeaveAnimation();
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
animationData: animation.export()
|
||
|
|
});
|
||
|
|
|
||
|
|
// 等待动画完成
|
||
|
|
await this.waitForAnimation();
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
transitionState: 'left'
|
||
|
|
});
|
||
|
|
|
||
|
|
this.triggerEvent('transitionend', {
|
||
|
|
type: 'leave',
|
||
|
|
transitionType: this.properties.transitionType
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('✅ 退出动画完成');
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
console.error('❌ 退出动画失败:', error);
|
||
|
|
this.setData({
|
||
|
|
transitionState: 'left'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎨 ===== 动画创建 =====
|
||
|
|
|
||
|
|
// 创建进入动画
|
||
|
|
createEnterAnimation() {
|
||
|
|
const transitionType = this.properties.transitionType;
|
||
|
|
|
||
|
|
switch (transitionType) {
|
||
|
|
case 'slideLeft':
|
||
|
|
return animationManager.slideIn('left', {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'slideRight':
|
||
|
|
return animationManager.slideIn('right', {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'slideUp':
|
||
|
|
return animationManager.slideIn('up', {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'slideDown':
|
||
|
|
return animationManager.slideIn('down', {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'scale':
|
||
|
|
return animationManager.scale(1, {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'bounce':
|
||
|
|
return animationManager.bounceIn({
|
||
|
|
duration: this.properties.duration
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'fade':
|
||
|
|
default:
|
||
|
|
return animationManager.fadeIn({
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 创建退出动画
|
||
|
|
createLeaveAnimation() {
|
||
|
|
const transitionType = this.properties.transitionType;
|
||
|
|
|
||
|
|
switch (transitionType) {
|
||
|
|
case 'slideLeft':
|
||
|
|
return animationManager.slideOut('left', '100%', {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'slideRight':
|
||
|
|
return animationManager.slideOut('right', '100%', {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'slideUp':
|
||
|
|
return animationManager.slideOut('up', '100%', {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'slideDown':
|
||
|
|
return animationManager.slideOut('down', '100%', {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'scale':
|
||
|
|
return animationManager.scale(0, {
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'bounce':
|
||
|
|
return animationManager.bounceOut({
|
||
|
|
duration: this.properties.duration
|
||
|
|
});
|
||
|
|
|
||
|
|
case 'fade':
|
||
|
|
default:
|
||
|
|
return animationManager.fadeOut({
|
||
|
|
duration: this.properties.duration,
|
||
|
|
timingFunction: this.properties.easing
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 创建加载动画
|
||
|
|
createLoadingAnimation() {
|
||
|
|
const loadingAnimation = animationManager.loadingSpinner({
|
||
|
|
duration: 1000
|
||
|
|
});
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
loadingAnimation: loadingAnimation.export()
|
||
|
|
});
|
||
|
|
|
||
|
|
// 循环播放加载动画
|
||
|
|
if (this.properties.showLoading) {
|
||
|
|
this.startLoadingLoop();
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 开始加载循环
|
||
|
|
startLoadingLoop() {
|
||
|
|
this.loadingTimer = setInterval(() => {
|
||
|
|
if (this.properties.showLoading) {
|
||
|
|
const loadingAnimation = animationManager.loadingSpinner({
|
||
|
|
duration: 1000
|
||
|
|
});
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
loadingAnimation: loadingAnimation.export()
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
this.stopLoadingLoop();
|
||
|
|
}
|
||
|
|
}, 1000);
|
||
|
|
},
|
||
|
|
|
||
|
|
// 停止加载循环
|
||
|
|
stopLoadingLoop() {
|
||
|
|
if (this.loadingTimer) {
|
||
|
|
clearInterval(this.loadingTimer);
|
||
|
|
this.loadingTimer = null;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🎯 ===== 样式管理 =====
|
||
|
|
|
||
|
|
// 更新过渡类
|
||
|
|
updateTransitionClass() {
|
||
|
|
let transitionClass = this.properties.transitionType;
|
||
|
|
|
||
|
|
if (this.properties.hardwareAccelerated) {
|
||
|
|
transitionClass += ' hardware-accelerated';
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.properties.debug) {
|
||
|
|
transitionClass += ' debug';
|
||
|
|
}
|
||
|
|
|
||
|
|
this.setData({
|
||
|
|
transitionClass: transitionClass
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 更新遮罩类
|
||
|
|
updateOverlayClass() {
|
||
|
|
this.setData({
|
||
|
|
overlayClass: this.properties.overlayType
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 🔧 ===== 工具方法 =====
|
||
|
|
|
||
|
|
// 等待动画完成
|
||
|
|
waitForAnimation() {
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
setTimeout(() => {
|
||
|
|
resolve();
|
||
|
|
}, this.properties.duration + 50); // 添加50ms缓冲
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 手动触发进入
|
||
|
|
triggerEnter() {
|
||
|
|
this.enter();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 手动触发退出
|
||
|
|
triggerLeave() {
|
||
|
|
this.leave();
|
||
|
|
},
|
||
|
|
|
||
|
|
// 重置状态
|
||
|
|
reset() {
|
||
|
|
this.setData({
|
||
|
|
transitionState: 'idle',
|
||
|
|
animationData: null
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 获取当前状态
|
||
|
|
getState() {
|
||
|
|
return {
|
||
|
|
transitionState: this.data.transitionState,
|
||
|
|
transitionType: this.properties.transitionType,
|
||
|
|
visible: this.properties.visible
|
||
|
|
};
|
||
|
|
},
|
||
|
|
|
||
|
|
// 清理资源
|
||
|
|
cleanup() {
|
||
|
|
this.stopLoadingLoop();
|
||
|
|
|
||
|
|
if (this.animationTimer) {
|
||
|
|
clearTimeout(this.animationTimer);
|
||
|
|
this.animationTimer = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|