mirror of
https://github.com/ArthurDanjou/artchat.git
synced 2026-02-04 06:32:08 +01:00
feat(infinite-canvas): add infinite canvas component with drag and zoom functionality
- Implemented InfiniteCanvas.vue for rendering an infinite canvas with drag and zoom capabilities. - Created useInfiniteCanvas composable for managing canvas state and interactions. - Added useImagePreloader composable for preloading images and videos. - Introduced constants for physics, touch interactions, viewport settings, and zoom defaults. - Developed utility functions for touch handling and media type detection. - Defined TypeScript types for canvas items, grid items, and composables. - Registered components and composables in the Nuxt module. - Added screenshot generation functionality for content files. - Updated package.json to include capture-website dependency.
This commit is contained in:
116
modules/infinite-canvas/components/CanvasLoader.vue
Normal file
116
modules/infinite-canvas/components/CanvasLoader.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<script setup lang="ts">
|
||||
import type { LoaderProps } from '../types'
|
||||
|
||||
const props = withDefaults(defineProps<LoaderProps>(), {
|
||||
title: 'Loading Canvas',
|
||||
description: 'Preparing your visual journey...',
|
||||
})
|
||||
|
||||
const RADIUS = 45
|
||||
const CIRCUMFERENCE = 2 * Math.PI * RADIUS
|
||||
|
||||
/**
|
||||
* Calculate progress percentage for display
|
||||
*/
|
||||
const percent = computed(() => Math.round(props.progress * 100))
|
||||
|
||||
/**
|
||||
* Calculate stroke dash offset for circular progress
|
||||
*/
|
||||
const dashOffset = computed(() => CIRCUMFERENCE * (1 - props.progress))
|
||||
|
||||
/**
|
||||
* Animation props for the content reveal
|
||||
*/
|
||||
const contentAnimation = {
|
||||
initial: { opacity: 0, y: 20 },
|
||||
animate: { opacity: 1, y: 0 },
|
||||
transition: { duration: 0.5, delay: 0.2 },
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if loading is complete
|
||||
*/
|
||||
const isComplete = computed(() => props.progress >= 1)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-500 ease-out"
|
||||
leave-active-class="transition-all duration-700 ease-out"
|
||||
enter-from-class="opacity-0"
|
||||
enter-to-class="opacity-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<div
|
||||
v-if="isVisible"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-default"
|
||||
>
|
||||
<div class="pointer-events-none fixed inset-0 size-full overflow-hidden">
|
||||
<div class="noise pointer-events-none absolute inset-[-200%] size-[400%] bg-[url('/noise.png')] opacity-[4%]" />
|
||||
</div>
|
||||
|
||||
<div class="relative flex flex-col items-center space-y-8">
|
||||
<div class="relative">
|
||||
<svg
|
||||
class="size-24 -rotate-90 transform"
|
||||
viewBox="0 0 100 100"
|
||||
>
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
:r="RADIUS"
|
||||
fill="none"
|
||||
stroke="rgba(255, 255, 255, 0.1)"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<circle
|
||||
cx="50"
|
||||
cy="50"
|
||||
:r="RADIUS"
|
||||
fill="none"
|
||||
stroke="rgba(255, 255, 255, 0.8)"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
:stroke-dasharray="CIRCUMFERENCE"
|
||||
:stroke-dashoffset="dashOffset"
|
||||
class="transition-all duration-300 ease-out"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<span class="text-xl font-medium text-highlighted">
|
||||
{{ percent }}%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<Motion v-bind="contentAnimation">
|
||||
<h2 class="text-2xl font-light text-highlighted mb-2">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<p class="text-sm text-highlighted/60">
|
||||
{{ description }}
|
||||
</p>
|
||||
</Motion>
|
||||
</div>
|
||||
|
||||
<div class="flex space-x-2">
|
||||
<div
|
||||
v-for="i in 3"
|
||||
:key="i"
|
||||
class="size-2 rounded-full bg-inverted/40"
|
||||
:class="{ 'animate-pulse': !isComplete }"
|
||||
:style="{ animationDelay: `${i * 0.2}s` }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pointer-events-none absolute inset-0">
|
||||
<div class="absolute left-1/2 top-1/2 size-96 -translate-x-1/2 -translate-y-1/2 rounded-full bg-inverted/5 blur-3xl" />
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
Reference in New Issue
Block a user