mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 22:49:31 -07:00
1 line
4.9 KiB
JSON
1 line
4.9 KiB
JSON
{"name":"Stack","title":"Stack","description":"Layered stack with swipe animations and smooth transitions.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<template>\n <div\n class=\"relative\"\n :style=\"{ width: cardDimensions.width + 'px', height: cardDimensions.height + 'px', perspective: 600 }\"\n >\n <template v-for=\"(card, index) in cards\" :key=\"card.id\">\n <Motion\n as=\"div\"\n class=\"absolute cursor-grab\"\n :style=\"{\n x: cardStates.get(card.id)?.x,\n y: cardStates.get(card.id)?.y,\n rotateX: cardStates.get(card.id)?.rotateX,\n rotateY: cardStates.get(card.id)?.rotateY\n }\"\n drag\n :drag-constraints=\"{ top: 0, right: 0, bottom: 0, left: 0 }\"\n :dragElastic=\"0.6\"\n :whileTap=\"{ cursor: 'grabbing', scale: 1.02 }\"\n :onDragEnd=\"(e, info) => handleDragEnd(e, info, card.id)\"\n >\n <Motion\n as=\"div\"\n class=\"rounded-2xl overflow-hidden border-4 border-white\"\n @click=\"sendToBackOnClick && sendToBack(card.id)\"\n :animate=\"{\n rotateZ: (cards.length - index - 1) * 4 + (randomRotation ? Math.random() * 10 - 5 : 0),\n scale: 1 + index * 0.06 - cards.length * 0.06,\n transformOrigin: '90% 90%'\n }\"\n :initial=\"false\"\n :transition=\"{\n type: 'spring',\n stiffness: animationConfig.stiffness,\n damping: animationConfig.damping\n }\"\n :style=\"{\n width: cardDimensions.width + 'px',\n height: cardDimensions.height + 'px'\n }\"\n >\n <img :src=\"card.img\" :alt=\"`card-${card.id}`\" className=\"w-full h-full object-cover pointer-events-none\" />\n </Motion>\n </Motion>\n </template>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport type { PanInfo } from 'motion-v';\nimport { Motion, useMotionValue, useTransform } from 'motion-v';\nimport { onBeforeMount, ref } from 'vue';\n\ninterface StackProps {\n className?: string;\n randomRotation?: boolean;\n sensitivity?: number;\n cardDimensions?: { width: number; height: number };\n cardsData: { id: number; img: string }[];\n animationConfig?: { stiffness: number; damping: number };\n sendToBackOnClick?: boolean;\n}\n\nconst props = withDefaults(defineProps<StackProps>(), {\n randomRotation: false,\n sensitivity: 200,\n cardDimensions: () => ({ width: 208, height: 208 }),\n cardsData: () => [],\n animationConfig: () => ({ stiffness: 260, damping: 20 }),\n sendToBackOnClick: false\n});\n\nconst cards = ref(\n props.cardsData.length\n ? props.cardsData\n : [\n { id: 1, img: 'https://images.unsplash.com/photo-1480074568708-e7b720bb3f09?q=80&w=500&auto=format' },\n { id: 2, img: 'https://images.unsplash.com/photo-1449844908441-8829872d2607?q=80&w=500&auto=format' },\n { id: 3, img: 'https://images.unsplash.com/photo-1452626212852-811d58933cae?q=80&w=500&auto=format' },\n { id: 4, img: 'https://images.unsplash.com/photo-1572120360610-d971b9d7767c?q=80&w=500&auto=format' }\n ]\n);\n\ntype CardState = {\n x: ReturnType<typeof useMotionValue<number>>;\n y: ReturnType<typeof useMotionValue<number>>;\n rotateX: ReturnType<typeof useMotionValue<number>>;\n rotateY: ReturnType<typeof useMotionValue<number>>;\n reset: () => void;\n};\n\nconst cardStates = new Map<number, CardState>();\n\nfunction createCardState(): CardState {\n const x = useMotionValue(0);\n const y = useMotionValue(0);\n\n const rotateX = useTransform(y, [-100, 100], [60, -60]);\n const rotateY = useTransform(x, [-100, 100], [-60, 60]);\n\n return {\n x,\n y,\n rotateX,\n rotateY,\n reset() {\n x.set(0);\n y.set(0);\n }\n };\n}\n\nonBeforeMount(() => {\n cards.value.forEach(card => {\n if (!cardStates.has(card.id)) {\n cardStates.set(card.id, createCardState());\n }\n });\n});\n\nfunction getCardState(cardId: number): CardState {\n let state = cardStates.get(cardId);\n if (!state) {\n state = createCardState();\n cardStates.set(cardId, state);\n }\n return state;\n}\n\nfunction handleDragEnd(_: PointerEvent, info: PanInfo, cardId: number) {\n if (Math.abs(info.offset.x) > props.sensitivity || Math.abs(info.offset.y) > props.sensitivity) {\n sendToBack(cardId);\n } else {\n getCardState(cardId).reset();\n }\n}\n\nconst sendToBack = (id: number) => {\n const newCards = [...cards.value];\n const index = newCards.findIndex(card => card.id === id);\n const [card] = newCards.splice(index, 1);\n newCards.unshift(card);\n cards.value = newCards;\n};\n</script>\n","path":"Stack/Stack.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"motion-v","version":"^1.5.0"}],"devDependencies":[],"categories":["Components"]} |