@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { CursorFloating } from '@proj-airi/stage-ui/components'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
name: string
|
||||
@@ -19,7 +21,7 @@ const emit = defineEmits<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
<CursorFloating
|
||||
relative min-h-120px flex="~ col" cursor-pointer overflow-hidden rounded-xl
|
||||
:class="[
|
||||
isSelected
|
||||
@@ -38,11 +40,11 @@ const emit = defineEmits<{
|
||||
relative flex="~ col 1" justify-between gap-3 overflow-hidden rounded-lg bg="white dark:neutral-900" p-5
|
||||
transition="all ease-in-out duration-400"
|
||||
after="content-empty absolute inset-0 z--2 w-full h-full bg-dotted-[neutral-200/80] bg-size-10px mask-image-[linear-gradient(165deg,white_30%,transparent_50%)] transition-all duration-400 ease-in-out"
|
||||
hover="after:bg-dotted-[primary-300/50] dark:after:bg-dotted-[primary-200/20]"
|
||||
hover="after:bg-dotted-[primary-300/50] dark:after:bg-dotted-[primary-200/20] text-primary-600/80 dark:text-primary-300/80"
|
||||
>
|
||||
<!-- Card header (name and badge) -->
|
||||
<div z-1 flex items-start justify-between gap-2>
|
||||
<h3 flex-1 truncate text-lg font-bold hover="text-primary-600 dark:text-primary-300">
|
||||
<h3 flex-1 truncate text-lg font-bold>
|
||||
{{ name }}
|
||||
</h3>
|
||||
<div v-if="isActive" shrink-0 rounded-md p-1 bg="primary-100 dark:primary-900/40" text="primary-600 dark:primary-400">
|
||||
@@ -51,7 +53,7 @@ const emit = defineEmits<{
|
||||
</div>
|
||||
|
||||
<!-- Card description -->
|
||||
<p v-if="description" line-clamp-3 min-h-40px flex-1 text-sm text="neutral-500 dark:neutral-400" hover="text-primary-600/80 dark:text-primary-300/80">
|
||||
<p v-if="description" line-clamp-3 min-h-40px flex-1 text-sm text="neutral-500 dark:neutral-400">
|
||||
{{ description }}
|
||||
</p>
|
||||
|
||||
@@ -95,5 +97,5 @@ const emit = defineEmits<{
|
||||
<div i-solar:trash-bin-trash-linear text="neutral-500 dark:neutral-400" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</CursorFloating>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { CursorFloating } from '@proj-airi/stage-ui/components'
|
||||
|
||||
interface Props {
|
||||
id: string
|
||||
name: string
|
||||
@@ -19,7 +21,7 @@ const emit = defineEmits<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
<CursorFloating
|
||||
relative min-h-120px flex="~ col" cursor-pointer overflow-hidden rounded-xl
|
||||
:class="[
|
||||
isSelected
|
||||
@@ -38,11 +40,11 @@ const emit = defineEmits<{
|
||||
relative flex="~ col 1" justify-between gap-3 overflow-hidden rounded-lg bg="white dark:neutral-900" p-5
|
||||
transition="all ease-in-out duration-400"
|
||||
after="content-empty absolute inset-0 z--2 w-full h-full bg-dotted-[neutral-200/80] bg-size-10px mask-image-[linear-gradient(165deg,white_30%,transparent_50%)] transition-all duration-400 ease-in-out"
|
||||
hover="after:bg-dotted-[primary-300/50] dark:after:bg-dotted-[primary-200/20]"
|
||||
hover="after:bg-dotted-[primary-300/50] dark:after:bg-dotted-[primary-200/20] text-primary-600/80 dark:text-primary-300/80"
|
||||
>
|
||||
<!-- Card header (name and badge) -->
|
||||
<div z-1 flex items-start justify-between gap-2>
|
||||
<h3 flex-1 truncate text-lg font-bold hover="text-primary-600 dark:text-primary-300">
|
||||
<h3 flex-1 truncate text-lg font-bold>
|
||||
{{ name }}
|
||||
</h3>
|
||||
<div v-if="isActive" shrink-0 rounded-md p-1 bg="primary-100 dark:primary-900/40" text="primary-600 dark:primary-400">
|
||||
@@ -51,7 +53,7 @@ const emit = defineEmits<{
|
||||
</div>
|
||||
|
||||
<!-- Card description -->
|
||||
<p v-if="description" line-clamp-3 min-h-40px flex-1 text-sm text="neutral-500 dark:neutral-400" hover="text-primary-600/80 dark:text-primary-300/80">
|
||||
<p v-if="description" line-clamp-3 min-h-40px flex-1 text-sm text="neutral-500 dark:neutral-400">
|
||||
{{ description }}
|
||||
</p>
|
||||
|
||||
@@ -95,5 +97,5 @@ const emit = defineEmits<{
|
||||
<div i-solar:trash-bin-trash-linear text="neutral-500 dark:neutral-400" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</CursorFloating>
|
||||
</template>
|
||||
|
||||
@@ -69,6 +69,7 @@ words:
|
||||
- higress
|
||||
- histoire
|
||||
- hiyori
|
||||
- Holo
|
||||
- huggingface
|
||||
- huggingspace
|
||||
- hunyuan
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex="1 ~ items-center justify-center" bg-gray-500 h-full w-full>
|
||||
<div rounded h-180 w-100>
|
||||
<div flex h-full>
|
||||
<div flex="1 ~ items-center justify-center" h-full w-full bg-gray-500>
|
||||
<div h-180 w-100 rounded>
|
||||
<div h-full flex>
|
||||
<div flex="1 ~ col" h-full bg="[#1f1f1f]">
|
||||
<div flex="1 ~ col" relative>
|
||||
<div bg-blue-300 flex-1 z-1>
|
||||
<div z-1 flex-1 bg-blue-300>
|
||||
<span text-5xl>BG</span>
|
||||
</div>
|
||||
<div ml--10 py-2 z-3>
|
||||
<div z-3 ml--10 py-2>
|
||||
<div>
|
||||
<span text-lg>subtitle</span>
|
||||
</div>
|
||||
@@ -18,11 +18,11 @@
|
||||
<span text-5xl>Title</span>
|
||||
</div>
|
||||
</div>
|
||||
<div bg-blue-500:50 bottom-0 left--10 right-0 top--10 absolute z-2>
|
||||
<div absolute bottom-0 left--10 right-0 top--10 z-2 bg-blue-500:50>
|
||||
<span text-5xl>Cover</span>
|
||||
</div>
|
||||
</div>
|
||||
<div mx-10 bg-gray-500 h-1 />
|
||||
<div mx-10 h-1 bg-gray-500 />
|
||||
<div px-10 py2>
|
||||
<div>
|
||||
<span text-2xl>Info 1111</span>
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div bg-pink-500 h-full w-15>
|
||||
<div h-full w-15 bg-pink-500>
|
||||
<span text-5xl>R</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import CursorFloating from './CursorFloating.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Story
|
||||
title="Cursor Floating"
|
||||
group="physics"
|
||||
:layout="{ type: 'grid', width: 600 }"
|
||||
>
|
||||
<Variant
|
||||
id="basic"
|
||||
title="Basic"
|
||||
>
|
||||
<div class="h-40 flex items-center justify-center">
|
||||
<CursorFloating>
|
||||
<div class="bg-primary-500/20 h-20 w-20" />
|
||||
</CursorFloating>
|
||||
</div>
|
||||
</Variant>
|
||||
|
||||
<Variant
|
||||
id="with-content"
|
||||
title="With Content"
|
||||
>
|
||||
<div class="h-40 flex items-center justify-center">
|
||||
<CursorFloating>
|
||||
<div class="rounded-lg bg-white p-4 dark:bg-neutral-900">
|
||||
<h3 class="text-lg font-bold">
|
||||
Card Title
|
||||
</h3>
|
||||
<p class="text-sm text-neutral-500">
|
||||
Card description goes here
|
||||
</p>
|
||||
</div>
|
||||
</CursorFloating>
|
||||
</div>
|
||||
</Variant>
|
||||
|
||||
<Variant
|
||||
id="interactive"
|
||||
title="Interactive"
|
||||
>
|
||||
<div class="h-40 flex items-center justify-center">
|
||||
<CursorFloating>
|
||||
<div class="cursor-pointer rounded-lg bg-white p-4 dark:bg-neutral-900 hover:bg-neutral-50 dark:hover:bg-neutral-800">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="i-solar:check-circle-bold-duotone text-primary-500" />
|
||||
<span>Click me!</span>
|
||||
</div>
|
||||
</div>
|
||||
</CursorFloating>
|
||||
</div>
|
||||
</Variant>
|
||||
</Story>
|
||||
</template>
|
||||
@@ -0,0 +1,153 @@
|
||||
<script setup lang="ts">
|
||||
// https://codepen.io/simeydotme/pen/PrQKgo
|
||||
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
interface Props {
|
||||
intensity?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
intensity: 3,
|
||||
})
|
||||
|
||||
const cardRef = ref<HTMLElement | null>(null)
|
||||
const transformStyle = ref('')
|
||||
const gradientPosition = ref('50% 50%')
|
||||
const sparklePosition = ref('50% 50%')
|
||||
const sparkleOpacity = ref(0.5)
|
||||
|
||||
function handleMouseMove(event: MouseEvent) {
|
||||
if (!cardRef.value)
|
||||
return
|
||||
|
||||
const card = cardRef.value
|
||||
const rect = card.getBoundingClientRect()
|
||||
|
||||
// Calculate mouse position relative to card
|
||||
const x = event.clientX - rect.left
|
||||
const y = event.clientY - rect.top
|
||||
const cardWidth = card.offsetWidth
|
||||
const cardHeight = card.offsetHeight
|
||||
|
||||
// Calculate percentages and effect values
|
||||
const xPercent = Math.abs(Math.floor(100 / cardWidth * x) - 100)
|
||||
const yPercent = Math.abs(Math.floor(100 / cardHeight * y) - 100)
|
||||
|
||||
// Calculate positions for gradient and sparkle effects
|
||||
const leftPos = (50 + (xPercent - 50) / 1.5)
|
||||
const topPos = (50 + (yPercent - 50) / 1.5)
|
||||
const sparkleX = (50 + (xPercent - 50) / 7)
|
||||
const sparkleY = (50 + (yPercent - 50) / 7)
|
||||
|
||||
// Calculate 3D rotation angles - intensity controlled by prop
|
||||
const rotateY = ((leftPos - 50) / 1.5) * 0.2 * props.intensity
|
||||
const rotateX = ((topPos - 50) / 2) * -1 * 0.2 * props.intensity
|
||||
|
||||
// Calculate sparkle opacity with intensity control
|
||||
const pAngle = (50 - xPercent) + (50 - yPercent)
|
||||
const opacity = 0.5 + (Math.abs(pAngle) * 0.008 * props.intensity)
|
||||
|
||||
// Update style values
|
||||
transformStyle.value = `perspective(1200px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(${1 + 0.015 * props.intensity}, ${1 + 0.015 * props.intensity}, ${1 + 0.015 * props.intensity})`
|
||||
gradientPosition.value = `${leftPos}% ${topPos}%`
|
||||
sparklePosition.value = `${sparkleX}% ${sparkleY}%`
|
||||
sparkleOpacity.value = opacity
|
||||
}
|
||||
|
||||
function resetCard() {
|
||||
transformStyle.value = 'perspective(1200px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)'
|
||||
gradientPosition.value = '50% 50%'
|
||||
sparklePosition.value = '50% 50%'
|
||||
sparkleOpacity.value = 0.5
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// Initialize with default transform to ensure smooth transitions
|
||||
transformStyle.value = 'perspective(1200px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="cardRef"
|
||||
class="card-hover-effect"
|
||||
:style="{
|
||||
'transform': transformStyle,
|
||||
'--effect-intensity': intensity,
|
||||
}"
|
||||
@mousemove="handleMouseMove"
|
||||
@mouseleave="resetCard"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card-hover-effect {
|
||||
transform-style: preserve-3d;
|
||||
transform-origin: center;
|
||||
will-change: transform;
|
||||
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
}
|
||||
|
||||
.card-hover-effect::before,
|
||||
.card-hover-effect::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
background-repeat: no-repeat;
|
||||
transition: all 0.33s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.card-hover-effect::before {
|
||||
background-position: var(--card-position-x) var(--card-position-y);
|
||||
background-size: 300% 300%;
|
||||
background-image: linear-gradient(
|
||||
115deg,
|
||||
transparent 0%,
|
||||
rgba(var(--color-primary-500), calc(0.3 * var(--effect-intensity))) 25%,
|
||||
transparent 47%,
|
||||
transparent 53%,
|
||||
rgba(var(--color-primary-600), calc(0.3 * var(--effect-intensity))) 75%,
|
||||
transparent 100%
|
||||
);
|
||||
opacity: calc(0.5 * var(--effect-intensity));
|
||||
filter: brightness(0.5) contrast(1);
|
||||
z-index: 1;
|
||||
mix-blend-mode: color-dodge;
|
||||
}
|
||||
|
||||
.card-hover-effect::after {
|
||||
background-position: var(--sparkle-position-x) var(--sparkle-position-y);
|
||||
background-size: 160%;
|
||||
background-image: linear-gradient(
|
||||
125deg,
|
||||
rgba(var(--color-primary-500), calc(0.3 * var(--effect-intensity))) 15%,
|
||||
rgba(var(--color-primary-400), calc(0.25 * var(--effect-intensity))) 30%,
|
||||
rgba(var(--color-primary-300), calc(0.2 * var(--effect-intensity))) 40%,
|
||||
rgba(var(--color-primary-200), calc(0.1 * var(--effect-intensity))) 60%,
|
||||
rgba(var(--color-primary-400), calc(0.25 * var(--effect-intensity))) 70%,
|
||||
rgba(var(--color-primary-500), calc(0.3 * var(--effect-intensity))) 85%
|
||||
);
|
||||
background-blend-mode: overlay;
|
||||
opacity: calc(var(--sparkle-opacity) * var(--effect-intensity));
|
||||
filter: brightness(1) contrast(1);
|
||||
z-index: 2;
|
||||
mix-blend-mode: color-dodge;
|
||||
}
|
||||
|
||||
.card-hover-effect:hover::before {
|
||||
opacity: calc(0.88 * var(--effect-intensity));
|
||||
filter: brightness(0.66) contrast(1.33);
|
||||
}
|
||||
|
||||
.card-hover-effect:hover::after {
|
||||
opacity: var(--effect-intensity);
|
||||
filter: brightness(1.1) contrast(1.2);
|
||||
}
|
||||
</style>
|
||||
@@ -1 +1,2 @@
|
||||
export { default as CursorFloating } from './CursorFloating.vue'
|
||||
export { default as CursorMomentum } from './CursorMomentum.vue'
|
||||
|
||||
Reference in New Issue
Block a user