222 lines
7.5 KiB
Vue
222 lines
7.5 KiB
Vue
<template>
|
|
<div class="min-h-screen bg-gradient-to-b from-slate-900 to-slate-800 p-8">
|
|
<h1 class="text-3xl font-bold text-center text-white mb-8">Card 3D Playground</h1>
|
|
|
|
<div class="container mx-auto flex flex-col lg:flex-row gap-8">
|
|
<!-- Controls Panel -->
|
|
<div class="w-full lg:w-1/3 bg-slate-800 p-6 rounded-xl shadow-lg">
|
|
<h2 class="text-xl font-semibold text-white mb-6">3D Controls</h2>
|
|
|
|
<div class="space-y-6">
|
|
<!-- Rotation -->
|
|
<div>
|
|
<h3 class="text-white font-medium mb-2">Rotation</h3>
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm text-gray-300 mb-1">X-Axis (rotateX)</label>
|
|
<input v-model="rotationX" type="range" min="-180" max="180" class="w-full" @input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ rotationX }}°</span>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm text-gray-300 mb-1">Y-Axis (rotateY)</label>
|
|
<input v-model="rotationY" type="range" min="-180" max="180" class="w-full" @input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ rotationY }}°</span>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm text-gray-300 mb-1">Z-Axis (rotateZ)</label>
|
|
<input v-model="rotationZ" type="range" min="-180" max="180" class="w-full" @input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ rotationZ }}°</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Translation -->
|
|
<div>
|
|
<h3 class="text-white font-medium mb-2">Position (px)</h3>
|
|
<div class="grid grid-cols-3 gap-4">
|
|
<div>
|
|
<label class="block text-xs text-gray-300 mb-1">X</label>
|
|
<input v-model="translateX" type="range" min="-200" max="200" class="w-full" @input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ translateX }}px</span>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-300 mb-1">Y</label>
|
|
<input v-model="translateY" type="range" min="-200" max="200" class="w-full" @input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ translateY }}px</span>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-300 mb-1">Z</label>
|
|
<input v-model="translateZ" type="range" min="-500" max="500" class="w-full" @input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ translateZ }}px</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scale -->
|
|
<div>
|
|
<h3 class="text-white font-medium mb-2">Scale</h3>
|
|
<div class="grid grid-cols-3 gap-4">
|
|
<div>
|
|
<label class="block text-xs text-gray-300 mb-1">X</label>
|
|
<input v-model="scaleX" type="range" min="0.5" max="2" step="0.1" class="w-full"
|
|
@input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ scaleX }}x</span>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-300 mb-1">Y</label>
|
|
<input v-model="scaleY" type="range" min="0.5" max="2" step="0.1" class="w-full"
|
|
@input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ scaleY }}x</span>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-300 mb-1">Z</label>
|
|
<input v-model="scaleZ" type="range" min="0.5" max="2" step="0.1" class="w-full"
|
|
@input="updateTransform">
|
|
<span class="text-xs text-gray-400">{{ scaleZ }}x</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reset Button -->
|
|
<button
|
|
class="w-full mt-6 bg-esyellow hover:bg-yellow-500 text-black font-medium py-2 px-4 rounded-md transition-colors"
|
|
@click="resetTransforms">
|
|
Reset to Default
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card Preview -->
|
|
<div class="flex-1 flex items-center justify-center min-h-[60vh] lg:min-h-auto">
|
|
<div class="relative w-64 h-96 transition-transform duration-300" :style="cardStyle">
|
|
<div class="w-full h-full" :style="{
|
|
transform: `
|
|
perspective(1000px)
|
|
rotateX(${rotationX}deg)
|
|
rotateY(${rotationY}deg)
|
|
rotateZ(${rotationZ}deg)
|
|
translate3d(${translateX}px, ${translateY}px, ${translateZ}px)
|
|
scale3d(${scaleX}, ${scaleY}, ${scaleZ})
|
|
`,
|
|
transformStyle: 'preserve-3d',
|
|
transition: 'transform 0.3s ease-out',
|
|
willChange: 'transform',
|
|
backfaceVisibility: 'visible'
|
|
}">
|
|
<div
|
|
class="w-full h-full bg-gradient-to-br from-cyan-500 to-blue-600 rounded-xl shadow-2xl flex items-center justify-center p-4">
|
|
<div class="text-center text-white">
|
|
<div class="text-6xl mb-2">🃏</div>
|
|
<h3 class="text-xl font-bold">Card Playground</h3>
|
|
<p class="text-sm opacity-80">Drag the sliders to transform me!</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
// State for 3D transformations
|
|
import { useDataStore } from '~/store/data'
|
|
|
|
const rotationX = ref(0)
|
|
const rotationY = ref(0)
|
|
const rotationZ = ref(0)
|
|
const translateX = ref(0)
|
|
const translateY = ref(0)
|
|
const translateZ = ref(0)
|
|
const scaleX = ref(1)
|
|
const scaleY = ref(1)
|
|
const scaleZ = ref(1)
|
|
const dataStore = useDataStore()
|
|
|
|
// Computed property for the card's transform style
|
|
const cardStyle = computed(() => {
|
|
return {
|
|
transform: `
|
|
perspective(1000px)
|
|
rotateX(${rotationX.value}deg)
|
|
rotateY(${rotationY.value}deg)
|
|
rotateZ(${rotationZ.value}deg)
|
|
translate3d(${translateX.value}px, ${translateY.value}px, ${translateZ.value}px)
|
|
scale3d(${scaleX.value}, ${scaleY.value}, ${scaleZ.value})
|
|
`,
|
|
transformStyle: 'preserve-3d',
|
|
transition: 'transform 0.3s ease-out',
|
|
willChange: 'transform',
|
|
backfaceVisibility: 'visible'
|
|
}
|
|
})
|
|
|
|
// Function to update transform (for immediate feedback)
|
|
const updateTransform = () => {
|
|
// The computed property will handle the update
|
|
}
|
|
|
|
// Function to reset all transforms
|
|
const resetTransforms = () => {
|
|
rotationX.value = 0
|
|
rotationY.value = 0
|
|
rotationZ.value = 0
|
|
translateX.value = 0
|
|
translateY.value = 0
|
|
translateZ.value = 0
|
|
scaleX.value = 1
|
|
scaleY.value = 1
|
|
scaleZ.value = 1
|
|
}
|
|
|
|
dataStore.isLoading.value = false
|
|
|
|
// Set page metadata
|
|
definePageMeta({
|
|
layout: 'default'
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Add any additional styles here */
|
|
.container {
|
|
perspective: 1000px;
|
|
}
|
|
|
|
/* Style range inputs */
|
|
input[type="range"] {
|
|
-webkit-appearance: none;
|
|
width: 100%;
|
|
height: 6px;
|
|
border-radius: 3px;
|
|
background: #475569;
|
|
outline: none;
|
|
}
|
|
|
|
input[type="range"]::-webkit-slider-thumb {
|
|
-webkit-appearance: none;
|
|
appearance: none;
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
background: #f59e0b;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
input[type="range"]::-webkit-slider-thumb:hover {
|
|
background: #d97706;
|
|
transform: scale(1.2);
|
|
}
|
|
|
|
/* Card styling */
|
|
.card-3d {
|
|
transform-style: preserve-3d;
|
|
transition: transform 0.3s ease-out;
|
|
will-change: transform;
|
|
backface-visibility: visible;
|
|
}
|
|
</style>
|