ENH - phone open animation

This commit is contained in:
Eichenholz
2026-08-04 14:29:06 +02:00
parent 62761174ce
commit da122a407d
6 changed files with 81 additions and 52 deletions
+49 -39
View File
@@ -31,6 +31,7 @@ type AppMessage = {
const REFERENCE_VIEWPORT_WIDTH = 1920
const REFERENCE_VIEWPORT_HEIGHT = 1080
const PHONE_BASE_SCALE = 0.69
const phone = usePhoneStore()
const clock = useClockStore()
@@ -45,8 +46,11 @@ const viewportScale = ref(
),
)
const phoneResolutionStyle = computed<CSSProperties>(() => ({
'--phone-edge-gap': `${24 * viewportScale.value}px`,
'--phone-zoom':
viewportScale.value * (phone.preferences.settings.phoneScale / 100),
viewportScale.value *
PHONE_BASE_SCALE *
(phone.preferences.settings.phoneScale / 100),
}))
const phoneFrameImage = computed(
() => PHONE_FRAME_IMAGES[phone.preferences.settings.frame],
@@ -134,43 +138,49 @@ onBeforeUnmount(() => {
</script>
<template>
<main
v-if="phone.isOpen || notifications.current"
class="phone-stage"
:class="{ 'phone-stage--peek': notifications.isPeeking }"
>
<div class="phone-resolution-wrapper" :style="phoneResolutionStyle">
<section class="phone-device" :aria-label="phone.t('Common.phone')">
<div class="phone-screen" :class="{ 'phone-screen--app': isAppRoute }">
<k-app
theme="ios"
:dark="phone.isDarkMode"
safe-areas
class="phone-app"
:class="{
dark: phone.isDarkMode,
'phone-app--light': !phone.isDarkMode,
}"
<Transition name="phone-lift" appear>
<main
v-if="phone.isOpen || notifications.current"
class="phone-stage"
:class="{ 'phone-stage--peek': notifications.isPeeking }"
:style="phoneResolutionStyle"
>
<div class="phone-resolution-wrapper">
<section class="phone-device" :aria-label="phone.t('Common.phone')">
<div
class="phone-screen"
:class="{ 'phone-screen--app': isAppRoute }"
>
<PhoneStatusBar />
<SpringboardView />
<RouterView v-slot="{ Component }">
<Transition name="app-window">
<component :is="Component" v-if="isAppRoute" />
</Transition>
</RouterView>
<PhoneHomeIndicator />
<PhoneNotifications />
</k-app>
</div>
<img
class="phone-device__frame"
:src="phoneFrameImage"
alt=""
aria-hidden="true"
draggable="false"
/>
</section>
</div>
</main>
<k-app
theme="ios"
:dark="phone.isDarkMode"
safe-areas
class="phone-app"
:class="{
dark: phone.isDarkMode,
'phone-app--light': !phone.isDarkMode,
}"
>
<PhoneStatusBar />
<SpringboardView />
<RouterView v-slot="{ Component }">
<Transition name="app-window">
<component :is="Component" v-if="isAppRoute" />
</Transition>
</RouterView>
<PhoneHomeIndicator />
<PhoneNotifications />
</k-app>
</div>
<img
class="phone-device__frame"
:src="phoneFrameImage"
alt=""
aria-hidden="true"
draggable="false"
/>
</section>
</div>
</main>
</Transition>
</template>
+18 -5
View File
@@ -34,17 +34,30 @@ button {
position: fixed;
inset: 0;
display: grid;
place-items: center;
pointer-events: none;
}
.phone-stage--peek {
place-items: end;
padding: 0 24px;
padding: 0 var(--phone-edge-gap, 24px) var(--phone-edge-gap, 24px) 0;
pointer-events: none;
}
.phone-stage--peek .phone-device {
transform: translateY(calc(100% - 190px));
transform-origin: right bottom;
}
.phone-lift-enter-active {
transition: opacity 0.52s linear;
}
.phone-lift-leave-active {
transition: opacity 0.36s linear;
}
.phone-lift-enter-active .phone-resolution-wrapper {
transition: transform 0.52s cubic-bezier(0.16, 1, 0.3, 1);
}
.phone-lift-leave-active .phone-resolution-wrapper {
transition: transform 0.36s cubic-bezier(0.7, 0, 0.84, 0);
}
.phone-lift-enter-from .phone-resolution-wrapper,
.phone-lift-leave-to .phone-resolution-wrapper {
transform: translateY(calc(100% + var(--phone-edge-gap, 24px)));
}
.phone-resolution-wrapper {
width: 390px;
height: 844px;
+1 -1
View File
@@ -58,7 +58,7 @@ describe('preferences', () => {
expect(value.settings.frame).toBe('black')
expect(value.settings.notificationVolume).toBe(0)
expect(value.settings.notificationDurationSeconds).toBe(30)
expect(value.settings.phoneScale).toBe(115)
expect(value.settings.phoneScale).toBe(150)
expect(value.settings.ringtoneVolume).toBe(100)
})
})
+5 -2
View File
@@ -13,6 +13,9 @@ export const PHONE_FRAME_IDS = [
export const RINGTONE_IDS = ['skyline', 'horizon', 'pulse'] as const
export const NOTIFICATION_SOUND_IDS = ['chime', 'signal', 'soft'] as const
export const WALLPAPER_IDS = ['midnight', 'aurora', 'ember'] as const
export const PHONE_SCALE_MIN = 50
export const PHONE_SCALE_MAX = 150
export const PHONE_SCALE_STEP = 5
export type AppearanceMode = (typeof APPEARANCE_MODE_IDS)[number]
export type PhoneFrameId = (typeof PHONE_FRAME_IDS)[number]
@@ -167,8 +170,8 @@ export function parsePhonePreferences(raw: string | null): PhonePreferencesV1 {
phoneScale: readNumber(
settings.phoneScale,
defaults.phoneScale,
85,
115,
PHONE_SCALE_MIN,
PHONE_SCALE_MAX,
),
ringtone: readChoice(
settings.ringtone,
+6 -3
View File
@@ -31,6 +31,9 @@ import {
APPEARANCE_MODE_IDS,
NOTIFICATION_SOUND_IDS,
PHONE_FRAME_IDS,
PHONE_SCALE_MAX,
PHONE_SCALE_MIN,
PHONE_SCALE_STEP,
RINGTONE_IDS,
WALLPAPER_IDS,
type AppearanceMode,
@@ -624,9 +627,9 @@ function selectNotificationSound(sound: NotificationSoundId): void {
<k-range
class="w-full"
:value="phone.preferences.settings.phoneScale"
:min="85"
:max="115"
:step="5"
:min="PHONE_SCALE_MIN"
:max="PHONE_SCALE_MAX"
:step="PHONE_SCALE_STEP"
:aria-label="phone.t('Apps.settings.phoneScale')"
@input="updateNumberPreference('phoneScale', $event)"
/>
+2 -2
View File
@@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sky Phone</title>
<script type="module" crossorigin src="./assets/sky-index-DOaxLsz3.js"></script>
<link rel="stylesheet" crossorigin href="./assets/sky-index-It2eOh-6.css">
<script type="module" crossorigin src="./assets/sky-index-D8rAzywY.js"></script>
<link rel="stylesheet" crossorigin href="./assets/sky-index-CCqdfChw.css">
</head>
<body>
<div id="app"></div>