:drunk: Created a broke view transition

This commit is contained in:
2025-11-06 01:44:47 +08:00
parent 9f82214797
commit 5b43f19a6c
2 changed files with 107 additions and 5 deletions

View File

@@ -63,6 +63,23 @@ const imageLoaded = ref(false)
const router = useRouter() const router = useRouter()
function openExternally() { function openExternally() {
// Capture image position for transition
const img = event?.target as HTMLImageElement
if (img && itemType.value === 'image') {
const rect = img.getBoundingClientRect()
const transitionData = {
src: remoteSource.value,
x: rect.left,
y: rect.top,
width: rect.width,
height: rect.height,
aspectRatio: aspectRatio.value
}
// Store transition data
sessionStorage.setItem('imageTransition', JSON.stringify(transitionData))
}
router.push('/files/' + props.item.id) router.push('/files/' + props.item.id)
} }

View File

@@ -133,6 +133,19 @@
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
<!-- View Transition Overlay -->
<div
v-if="isTransitioning"
class="transition-overlay"
:style="transitionStyle"
>
<img
:src="transitionImage"
class="transition-image"
alt="Transitioning image"
/>
</div>
</div> </div>
</template> </template>
@@ -144,10 +157,6 @@ import { downloadAndDecryptFile } from "./secure"
import { formatBytes } from "./format" import { formatBytes } from "./format"
import type { SnCloudFile } from "~/types/api/post" import type { SnCloudFile } from "~/types/api/post"
useHead({
title: computed(() => fileInfo.value?.name ? `${fileInfo.value.name} - File Preview` : 'File Preview')
})
const route = useRoute() const route = useRoute()
const error = ref<string | null>(null) const error = ref<string | null>(null)
@@ -161,9 +170,18 @@ const infoDialog = ref<boolean>(false)
const secretDialog = ref<boolean>(false) const secretDialog = ref<boolean>(false)
const dialogPassword = ref<string>("") const dialogPassword = ref<string>("")
// View transition state
const isTransitioning = ref<boolean>(false)
const transitionImage = ref<string>("")
const transitionStyle = ref<Record<string, string | number>>({})
const api = useSolarNetwork() const api = useSolarNetwork()
const fileInfo = ref<SnCloudFile | null>(null) const fileInfo = ref<SnCloudFile | null>(null)
useHead({
title: computed(() => fileInfo.value?.name ? `${fileInfo.value.name} - File Preview` : 'File Preview')
})
async function fetchFileInfo() { async function fetchFileInfo() {
try { try {
let url = "/drive/files/" + fileId + "/info" let url = "/drive/files/" + fileId + "/info"
@@ -176,7 +194,61 @@ async function fetchFileInfo() {
error.value = (err as Error).message error.value = (err as Error).message
} }
} }
onMounted(() => fetchFileInfo())
function checkForTransition() {
const transitionData = sessionStorage.getItem('imageTransition')
if (transitionData) {
try {
const data = JSON.parse(transitionData)
isTransitioning.value = true
transitionImage.value = data.src
// Calculate final position (centered in viewport)
const viewportWidth = window.innerWidth
const viewportHeight = window.innerHeight
const finalWidth = Math.min(viewportWidth * 0.9, data.width * (viewportHeight / data.height))
const finalHeight = finalWidth / data.aspectRatio
const finalX = (viewportWidth - finalWidth) / 2
const finalY = (viewportHeight - finalHeight) / 2
// Set initial position (from original image location)
transitionStyle.value = {
position: 'fixed',
top: `${data.y}px`,
left: `${data.x}px`,
width: `${data.width}px`,
height: `${data.height}px`,
zIndex: 9999,
transition: 'all 0.3s ease-out'
}
// Animate to final position
requestAnimationFrame(() => {
transitionStyle.value = {
...transitionStyle.value,
top: `${finalY}px`,
left: `${finalX}px`,
width: `${finalWidth}px`,
height: `${finalHeight}px`
}
// Hide transition after animation
setTimeout(() => {
isTransitioning.value = false
sessionStorage.removeItem('imageTransition')
}, 300)
})
} catch (error) {
console.warn('Failed to parse transition data:', error)
sessionStorage.removeItem('imageTransition')
}
}
}
onMounted(() => {
fetchFileInfo()
checkForTransition()
})
const apiBase = useSolarNetworkUrl() const apiBase = useSolarNetworkUrl()
@@ -349,4 +421,17 @@ definePageMeta({
.top-toolbar :deep(.v-app-bar__content) { .top-toolbar :deep(.v-app-bar__content) {
padding: 0; padding: 0;
} }
/* View transition styles */
.transition-overlay {
pointer-events: none;
z-index: 9999;
}
.transition-image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 4px;
}
</style> </style>