mirror of
https://github.com/DavidHDev/vue-bits.git
synced 2026-03-07 22:49:31 -07:00
1 line
4.7 KiB
JSON
1 line
4.7 KiB
JSON
{"name":"BlobCursor","title":"BlobCursor","description":"Organic blob cursor that smoothly follows the pointer with inertia and elastic morphing.","type":"registry:component","add":"when-added","files":[{"type":"registry:component","role":"file","content":"<script setup lang=\"ts\">\nimport gsap from 'gsap';\nimport { onMounted, onUnmounted, ref, useTemplateRef } from 'vue';\n\ninterface BlobCursorProps {\n blobType?: 'circle' | 'square';\n fillColor?: string;\n trailCount?: number;\n sizes?: number[];\n innerSizes?: number[];\n innerColor?: string;\n opacities?: number[];\n shadowColor?: string;\n shadowBlur?: number;\n shadowOffsetX?: number;\n shadowOffsetY?: number;\n filterId?: string;\n filterStdDeviation?: number;\n filterColorMatrixValues?: string;\n useFilter?: boolean;\n fastDuration?: number;\n slowDuration?: number;\n fastEase?: string;\n slowEase?: string;\n zIndex?: number;\n}\n\nconst props = withDefaults(defineProps<BlobCursorProps>(), {\n blobType: 'circle',\n fillColor: '#27FF64',\n trailCount: 3,\n sizes: () => [60, 125, 75],\n innerSizes: () => [20, 35, 25],\n innerColor: 'rgba(255,255,255,0.8)',\n opacities: () => [0.6, 0.6, 0.6],\n shadowColor: 'rgba(0,0,0,0.75)',\n shadowBlur: 5,\n shadowOffsetX: 10,\n shadowOffsetY: 10,\n filterId: 'blob',\n filterStdDeviation: 30,\n filterColorMatrixValues: '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 35 -10',\n useFilter: true,\n fastDuration: 0.1,\n slowDuration: 0.5,\n fastEase: 'power3.out',\n slowEase: 'power1.out',\n zIndex: 100\n});\n\nconst containerRef = useTemplateRef<HTMLDivElement>('containerRef');\nconst blobsRef = ref<(HTMLElement | null)[]>([]);\n\nconst updateOffset = () => {\n if (!containerRef.value) return { left: 0, top: 0 };\n const rect = containerRef.value.getBoundingClientRect();\n return { left: rect.left, top: rect.top };\n};\n\nconst handleMove = (e: MouseEvent | TouchEvent) => {\n const { left, top } = updateOffset();\n const x = 'clientX' in e ? e.clientX : e.touches[0].clientX;\n const y = 'clientY' in e ? e.clientY : e.touches[0].clientY;\n\n blobsRef.value.forEach((el, i) => {\n if (!el) return;\n\n const isLead = i === 0;\n gsap.to(el, {\n x: x - left,\n y: y - top,\n duration: isLead ? props.fastDuration : props.slowDuration,\n ease: isLead ? props.fastEase : props.slowEase\n });\n });\n};\n\nonMounted(() => {\n if (!updateOffset) return;\n window.addEventListener('resize', updateOffset);\n});\n\nonUnmounted(() => {\n window.removeEventListener('resize', updateOffset);\n});\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n @mousemove=\"handleMove\"\n @touchmove=\"handleMove\"\n class=\"top-0 left-0 relative w-full h-full\"\n :style=\"{ zIndex: props.zIndex }\"\n >\n <svg v-if=\"props.useFilter\" class=\"absolute w-0 h-0\">\n <filter :id=\"props.filterId\">\n <feGaussianBlur in=\"SourceGraphic\" result=\"blur\" :stdDeviation=\"props.filterStdDeviation\" />\n <feColorMatrix in=\"blur\" :values=\"props.filterColorMatrixValues\" />\n </filter>\n </svg>\n\n <div\n class=\"absolute inset-0 overflow-hidden cursor-default pointer-events-none select-none\"\n :style=\"{\n filter: props.useFilter ? `url(#${props.filterId})` : undefined\n }\"\n >\n <div\n v-for=\"(_, i) in props.trailCount\"\n :key=\"i\"\n :ref=\"\n el => {\n blobsRef[i] = el as HTMLElement | null;\n }\n \"\n class=\"absolute -translate-x-1/2 -translate-y-1/2 will-change-transform transform\"\n :style=\"{\n width: `${props.sizes[i]}px`,\n height: `${props.sizes[i]}px`,\n borderRadius: props.blobType === 'circle' ? '50%' : '0',\n backgroundColor: props.fillColor,\n opacity: props.opacities[i],\n boxShadow: `${props.shadowOffsetX}px ${props.shadowOffsetY}px ${props.shadowBlur}px 0 ${props.shadowColor}`\n }\"\n >\n <div\n class=\"absolute\"\n :style=\"{\n width: `${props.innerSizes[i]}px`,\n height: `${props.innerSizes[i]}px`,\n top: `${(props.sizes[i] - props.innerSizes[i]) / 2}px`,\n left: `${(props.sizes[i] - props.innerSizes[i]) / 2}px`,\n backgroundColor: props.innerColor,\n borderRadius: props.blobType === 'circle' ? '50%' : '0'\n }\"\n />\n </div>\n </div>\n </div>\n</template>\n","path":"BlobCursor/BlobCursor.vue","_imports_":[],"registryDependencies":[],"dependencies":[],"devDependencies":[]}],"registryDependencies":[],"dependencies":[{"ecosystem":"js","name":"gsap","version":"^3.13.0"}],"devDependencies":[],"categories":["Animations"]} |