feat: add Minecraft launch transition animation (cover/logo phases)

This commit is contained in:
Xiao-no-love
2026-09-15 22:33:18 +08:00
parent bd09fed0ed
commit 776f621436
8 changed files with 1301 additions and 406 deletions

View File

@ -373,6 +373,12 @@ let allowWindowClose = false
let unlistenCloseRequested: (() => void) | undefined
let unlistenLightweightModeError: (() => void) | undefined
let unlistenSystemAccentColor: (() => void) | undefined
let unlistenMcTransitionFadeout: (() => void) | undefined
let unlistenMcTransitionFocus: (() => void) | undefined
let unlistenMcTransitionCover: (() => void) | undefined
let unlistenMcTransitionLogoIn: (() => void) | undefined
let unlistenMcTransitionLogoOut: (() => void) | undefined
let unlistenMcTransitionReset: (() => void) | undefined
let maximizedStateTimer: ReturnType<typeof setTimeout> | undefined
let unlistenWindowResize: (() => void) | undefined
const minecraftCrashModal = ref()
@ -491,6 +497,104 @@ onMounted(async () => {
checkUpdates()
void warnIfRunningElevated()
startDirectLinkSync()
unlistenMcTransitionFadeout = await listen('mc-transition-fadeout', async () => {
// 游戏窗口已就位、启动器已缩放到同尺寸。这里做 0.5s CSS 淡出,
// 完成后回调 Rust由后端执行聚焦游戏 + 最小化启动器。
const root = document.getElementById('app')
if (root) {
root.style.transition = 'opacity 500ms ease-out'
root.style.opacity = '0'
}
window.setTimeout(() => {
void invoke('mc_transition_fade_done').catch(() => {})
}, 510)
})
// 窗口恢复可见时,清掉残留的淡出透明度,避免再次打开时全透明。
const resetLauncherOpacity = () => {
const root = document.getElementById('app')
if (root) {
root.style.transition = 'none'
root.style.opacity = ''
void root.offsetHeight
}
}
window.addEventListener('focus', resetLauncherOpacity)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') resetLauncherOpacity()
})
unlistenMcTransitionFocus = await getCurrentWindow().onFocusChanged(({ payload: focused }) => {
if (focused) resetLauncherOpacity()
})
// ============ 启动过渡遮罩(三阶段) ============
// 阶段1纯色遮罩主题色无图—— 盖住放大抖动
// 阶段2全屏后渐显 SVG logo
// 阶段3缩小前背景变 Mojang 红 + 渐隐 logo
let mcCoverEl = null
let mcCoverImg = null
unlistenMcTransitionCover = await listen('mc-transition-cover-show', () => {
const app = document.getElementById('app')
if (!app) return
if (!mcCoverEl) {
mcCoverEl = document.createElement('div')
mcCoverEl.id = 'mc-transition-cover'
mcCoverEl.style.cssText = [
'position: fixed',
'inset: 0',
'z-index: 999999',
'display: flex',
'align-items: center',
'justify-content: center',
'background: var(--color-brand, #db1f29)',
'transition: background-color 500ms ease, opacity 300ms ease-out',
'opacity: 0',
'pointer-events: none',
].join(';')
mcCoverImg = document.createElement('img')
mcCoverImg.src = '/mojang-logo.svg'
mcCoverImg.alt = ''
mcCoverImg.style.cssText =
'width: 42%; max-width: 640px; height: auto; opacity: 0; transition: opacity 500ms ease-out;'
mcCoverEl.appendChild(mcCoverImg)
app.appendChild(mcCoverEl)
}
requestAnimationFrame(() => {
mcCoverEl.style.opacity = '1'
})
window.setTimeout(() => {
void invoke('mc_transition_cover_ready').catch(() => {})
}, 320)
})
unlistenMcTransitionLogoIn = await listen('mc-transition-logo-in', () => {
if (mcCoverImg) mcCoverImg.style.opacity = '1'
window.setTimeout(() => {
void invoke('mc_transition_logo_in_done').catch(() => {})
}, 520)
})
unlistenMcTransitionLogoOut = await listen('mc-transition-logo-out', () => {
if (mcCoverEl) mcCoverEl.style.backgroundColor = '#db1f29'
if (mcCoverImg) mcCoverImg.style.opacity = '0'
window.setTimeout(() => {
void invoke('mc_transition_logo_out_done').catch(() => {})
}, 520)
})
unlistenMcTransitionReset = await listen('mc-transition-reset-opacity', () => {
const app = document.getElementById('app')
if (app) {
app.style.transition = 'none'
app.style.opacity = ''
}
if (mcCoverEl) {
mcCoverEl.remove()
mcCoverEl = null
mcCoverImg = null
}
})
})
let directLinkSync: (() => Promise<void>) | undefined
@ -567,6 +671,12 @@ onUnmounted(async () => {
window.removeEventListener('keydown', handleGlobalKeydown, true)
unlistenCloseRequested?.()
unlistenLightweightModeError?.()
unlistenMcTransitionFadeout?.()
unlistenMcTransitionFocus?.()
unlistenMcTransitionCover?.()
unlistenMcTransitionLogoIn?.()
unlistenMcTransitionLogoOut?.()
unlistenMcTransitionReset?.()
unlistenSystemAccentColor?.()
document.querySelector('body').removeEventListener('click', handleClick)
document.querySelector('body').removeEventListener('auxclick', handleAuxClick)