Initial Commit

This commit is contained in:
Rajuahamedkst 2025-09-12 16:08:17 +08:00
commit 1d71a02738
237 changed files with 64293 additions and 0 deletions

View file

@ -0,0 +1,456 @@
// 🎬 页面过渡组件逻辑
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;
}
}
}
});

View file

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View file

@ -0,0 +1,20 @@
<!-- 🎬 页面过渡组件 -->
<view class="page-transition-container {{transitionClass}}"
style="{{animationStyle}}"
animation="{{animationData}}">
<!-- 页面内容插槽 -->
<slot></slot>
<!-- 过渡遮罩 -->
<view wx:if="{{showOverlay}}"
class="transition-overlay {{overlayClass}}"
style="{{overlayStyle}}">
</view>
<!-- 加载指示器 -->
<view wx:if="{{showLoading}}" class="transition-loading">
<view class="loading-spinner" animation="{{loadingAnimation}}"></view>
<text class="loading-text">{{loadingText}}</text>
</view>
</view>

View file

@ -0,0 +1,386 @@
/* 🎬 页面过渡组件样式 */
/* CSS变量定义 */
.page-transition-container {
--primary-color: #007AFF;
--background-color: #F2F2F7;
--surface-color: #FFFFFF;
--text-primary: #000000;
--text-secondary: #8E8E93;
--shadow-medium: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
--radius-large: 20rpx;
}
/* 🌙 深色模式支持 */
@media (prefers-color-scheme: dark) {
.page-transition-container {
--primary-color: #0A84FF;
--background-color: #000000;
--surface-color: #1C1C1E;
--text-primary: #FFFFFF;
--text-secondary: #8E8E93;
--shadow-medium: 0 8rpx 24rpx rgba(0, 0, 0, 0.4);
}
}
.page-transition-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
/* 🎭 过渡类型样式 */
.page-transition-container.slide-left {
transform: translateX(100%);
}
.page-transition-container.slide-right {
transform: translateX(-100%);
}
.page-transition-container.slide-up {
transform: translateY(100%);
}
.page-transition-container.slide-down {
transform: translateY(-100%);
}
.page-transition-container.fade {
opacity: 0;
}
.page-transition-container.scale {
transform: scale(0.8);
opacity: 0;
}
.page-transition-container.flip {
transform: rotateY(90deg);
}
.page-transition-container.zoom {
transform: scale(1.2);
opacity: 0;
}
/* 激活状态 */
.page-transition-container.active {
transform: translateX(0) translateY(0) scale(1) rotateY(0);
opacity: 1;
}
/* 🎨 过渡遮罩 */
.transition-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
pointer-events: none;
}
.transition-overlay.fade-black {
background: rgba(0, 0, 0, 0.5);
}
.transition-overlay.fade-white {
background: rgba(255, 255, 255, 0.8);
}
.transition-overlay.blur {
backdrop-filter: blur(10rpx);
background: rgba(255, 255, 255, 0.1);
}
.transition-overlay.gradient {
background: linear-gradient(45deg,
rgba(0, 122, 255, 0.1) 0%,
rgba(90, 200, 250, 0.1) 100%);
}
/* 🔄 加载指示器 */
.transition-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1001;
display: flex;
flex-direction: column;
align-items: center;
gap: 24rpx;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 4rpx solid rgba(0, 122, 255, 0.2);
border-top: 4rpx solid var(--primary-color);
border-radius: 50%;
}
.loading-text {
font-size: 28rpx;
color: var(--text-secondary);
text-align: center;
}
/* 🎪 预定义动画类 */
@keyframes slideInLeft {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideInRight {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideInUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slideInDown {
from {
transform: translateY(-100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes scaleIn {
from {
transform: scale(0.8);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
@keyframes flipIn {
from {
transform: rotateY(90deg);
opacity: 0;
}
to {
transform: rotateY(0);
opacity: 1;
}
}
@keyframes zoomIn {
from {
transform: scale(1.2);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
@keyframes bounceIn {
0% {
transform: scale(0.3);
opacity: 0;
}
50% {
transform: scale(1.05);
opacity: 1;
}
70% {
transform: scale(0.95);
}
100% {
transform: scale(1);
}
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 🎭 动画类应用 */
.page-transition-container.animate-slide-left {
animation: slideInLeft 0.4s ease-out;
}
.page-transition-container.animate-slide-right {
animation: slideInRight 0.4s ease-out;
}
.page-transition-container.animate-slide-up {
animation: slideInUp 0.4s ease-out;
}
.page-transition-container.animate-slide-down {
animation: slideInDown 0.4s ease-out;
}
.page-transition-container.animate-fade {
animation: fadeIn 0.3s ease-out;
}
.page-transition-container.animate-scale {
animation: scaleIn 0.3s ease-out;
}
.page-transition-container.animate-flip {
animation: flipIn 0.5s ease-out;
}
.page-transition-container.animate-zoom {
animation: zoomIn 0.3s ease-out;
}
.page-transition-container.animate-bounce {
animation: bounceIn 0.6s ease-out;
}
.loading-spinner {
animation: spin 1s linear infinite;
}
/* 📱 响应式设计 */
@media screen and (max-width: 375px) {
.loading-spinner {
width: 50rpx;
height: 50rpx;
border-width: 3rpx;
}
.loading-text {
font-size: 24rpx;
}
}
@media screen and (min-width: 414px) {
.loading-spinner {
width: 70rpx;
height: 70rpx;
border-width: 5rpx;
}
.loading-text {
font-size: 32rpx;
}
}
/* 🎨 性能优化 */
.page-transition-container {
will-change: transform, opacity;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
}
/* 减少重绘 */
.page-transition-container .transition-content {
transform-style: preserve-3d;
}
/* 硬件加速 */
.page-transition-container.hardware-accelerated {
transform: translateZ(0);
-webkit-transform: translateZ(0);
}
/* 🎭 特殊效果 */
.page-transition-container.glass-effect {
backdrop-filter: blur(20rpx);
background: rgba(255, 255, 255, 0.1);
border: 1rpx solid rgba(255, 255, 255, 0.2);
}
.page-transition-container.shadow-effect {
box-shadow: var(--shadow-medium);
}
.page-transition-container.glow-effect {
box-shadow: 0 0 40rpx rgba(0, 122, 255, 0.3);
}
/* 🎪 组合动画 */
.page-transition-container.slide-fade {
animation: slideInLeft 0.4s ease-out, fadeIn 0.4s ease-out;
}
.page-transition-container.scale-fade {
animation: scaleIn 0.3s ease-out, fadeIn 0.3s ease-out;
}
.page-transition-container.flip-fade {
animation: flipIn 0.5s ease-out, fadeIn 0.5s ease-out;
}
/* 🎯 状态指示器 */
.page-transition-container.loading {
pointer-events: none;
}
.page-transition-container.transitioning {
overflow: hidden;
}
.page-transition-container.completed {
transform: none;
opacity: 1;
}
/* 🔧 调试模式 */
.page-transition-container.debug {
border: 2rpx dashed #ff0000;
position: relative;
}
.page-transition-container.debug::before {
content: 'TRANSITION DEBUG';
position: absolute;
top: 10rpx;
left: 10rpx;
background: #ff0000;
color: white;
padding: 4rpx 8rpx;
font-size: 20rpx;
z-index: 9999;
}