Add prettier config, format codebase

This commit is contained in:
David Haz
2025-07-12 11:59:33 +03:00
parent ac8b2c04d8
commit f4d97ee94e
211 changed files with 10586 additions and 8810 deletions

View File

@@ -2,46 +2,74 @@
<div class="relative w-1/2 max-md:w-11/12 aspect-square" :style="wrapperStyle">
<div ref="sceneRef" class="grid w-full h-full" :style="sceneStyle">
<template v-for="(_, r) in cells" :key="`row-${r}`">
<div v-for="(__, c) in cells" :key="`${r}-${c}`"
class="cube relative w-full h-full aspect-square [transform-style:preserve-3d]" :data-row="r" :data-col="c">
<div
v-for="(__, c) in cells"
:key="`${r}-${c}`"
class="cube relative w-full h-full aspect-square [transform-style:preserve-3d]"
:data-row="r"
:data-col="c"
>
<span class="absolute pointer-events-none -inset-9" />
<div class="cube-face absolute inset-0 flex items-center justify-center" :style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'translateY(-50%) rotateX(90deg)',
}" />
<div class="cube-face absolute inset-0 flex items-center justify-center" :style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'translateY(50%) rotateX(-90deg)',
}" />
<div class="cube-face absolute inset-0 flex items-center justify-center" :style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'translateX(-50%) rotateY(-90deg)',
}" />
<div class="cube-face absolute inset-0 flex items-center justify-center" :style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'translateX(50%) rotateY(90deg)',
}" />
<div class="cube-face absolute inset-0 flex items-center justify-center" :style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'rotateY(-90deg) translateX(50%) rotateY(90deg)',
}" />
<div class="cube-face absolute inset-0 flex items-center justify-center" :style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'rotateY(90deg) translateX(-50%) rotateY(-90deg)',
}" />
<div
class="cube-face absolute inset-0 flex items-center justify-center"
:style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'translateY(-50%) rotateX(90deg)'
}"
/>
<div
class="cube-face absolute inset-0 flex items-center justify-center"
:style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'translateY(50%) rotateX(-90deg)'
}"
/>
<div
class="cube-face absolute inset-0 flex items-center justify-center"
:style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'translateX(-50%) rotateY(-90deg)'
}"
/>
<div
class="cube-face absolute inset-0 flex items-center justify-center"
:style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'translateX(50%) rotateY(90deg)'
}"
/>
<div
class="cube-face absolute inset-0 flex items-center justify-center"
:style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'rotateY(-90deg) translateX(50%) rotateY(90deg)'
}"
/>
<div
class="cube-face absolute inset-0 flex items-center justify-center"
:style="{
background: 'var(--cube-face-bg)',
border: 'var(--cube-face-border)',
boxShadow: 'var(--cube-face-shadow)',
transform: 'rotateY(90deg) translateX(-50%) rotateY(-90deg)'
}"
/>
</div>
</template>
</div>
@@ -49,34 +77,34 @@
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, withDefaults } from 'vue'
import gsap from 'gsap'
import { ref, computed, onMounted, onUnmounted, withDefaults } from 'vue';
import gsap from 'gsap';
interface Gap {
row: number
col: number
row: number;
col: number;
}
interface Duration {
enter: number
leave: number
enter: number;
leave: number;
}
interface Props {
gridSize?: number
cubeSize?: number
maxAngle?: number
radius?: number
easing?: gsap.EaseString
duration?: Duration
cellGap?: number | Gap
borderStyle?: string
faceColor?: string
shadow?: boolean | string
autoAnimate?: boolean
rippleOnClick?: boolean
rippleColor?: string
rippleSpeed?: number
gridSize?: number;
cubeSize?: number;
maxAngle?: number;
radius?: number;
easing?: gsap.EaseString;
duration?: Duration;
cellGap?: number | Gap;
borderStyle?: string;
faceColor?: string;
shadow?: boolean | string;
autoAnimate?: boolean;
rippleOnClick?: boolean;
rippleColor?: string;
rippleSpeed?: number;
}
const props = withDefaults(defineProps<Props>(), {
@@ -91,37 +119,37 @@ const props = withDefaults(defineProps<Props>(), {
autoAnimate: true,
rippleOnClick: true,
rippleColor: '#fff',
rippleSpeed: 2,
})
rippleSpeed: 2
});
const sceneRef = ref<HTMLDivElement | null>(null)
const rafRef = ref<number | null>(null)
const idleTimerRef = ref<number | null>(null)
const userActiveRef = ref(false)
const simPosRef = ref<{ x: number; y: number }>({ x: 0, y: 0 })
const simTargetRef = ref<{ x: number; y: number }>({ x: 0, y: 0 })
const simRAFRef = ref<number | null>(null)
const sceneRef = ref<HTMLDivElement | null>(null);
const rafRef = ref<number | null>(null);
const idleTimerRef = ref<number | null>(null);
const userActiveRef = ref(false);
const simPosRef = ref<{ x: number; y: number }>({ x: 0, y: 0 });
const simTargetRef = ref<{ x: number; y: number }>({ x: 0, y: 0 });
const simRAFRef = ref<number | null>(null);
const colGap = computed(() => {
return typeof props.cellGap === 'number'
? `${props.cellGap}px`
: (props.cellGap as Gap)?.col !== undefined
? `${(props.cellGap as Gap).col}px`
: '5%'
})
: '5%';
});
const rowGap = computed(() => {
return typeof props.cellGap === 'number'
? `${props.cellGap}px`
: (props.cellGap as Gap)?.row !== undefined
? `${(props.cellGap as Gap).row}px`
: '5%'
})
: '5%';
});
const enterDur = computed(() => props.duration.enter)
const leaveDur = computed(() => props.duration.leave)
const enterDur = computed(() => props.duration.enter);
const leaveDur = computed(() => props.duration.leave);
const cells = computed(() => Array.from({ length: props.gridSize }))
const cells = computed(() => Array.from({ length: props.gridSize }));
const sceneStyle = computed(() => ({
gridTemplateColumns: props.cubeSize
@@ -133,189 +161,184 @@ const sceneStyle = computed(() => ({
columnGap: colGap.value,
rowGap: rowGap.value,
perspective: '99999999px',
gridAutoRows: '1fr',
}))
gridAutoRows: '1fr'
}));
const wrapperStyle = computed(() => ({
'--cube-face-border': props.borderStyle,
'--cube-face-bg': props.faceColor,
'--cube-face-shadow':
props.shadow === true ? '0 0 6px rgba(0,0,0,.5)' : props.shadow || 'none',
'--cube-face-shadow': props.shadow === true ? '0 0 6px rgba(0,0,0,.5)' : props.shadow || 'none',
...(props.cubeSize
? {
width: `${props.gridSize * props.cubeSize}px`,
height: `${props.gridSize * props.cubeSize}px`,
}
: {}),
}))
width: `${props.gridSize * props.cubeSize}px`,
height: `${props.gridSize * props.cubeSize}px`
}
: {})
}));
const tiltAt = (rowCenter: number, colCenter: number) => {
if (!sceneRef.value) return
if (!sceneRef.value) return;
sceneRef.value.querySelectorAll<HTMLDivElement>('.cube').forEach((cube) => {
const r = +(cube.dataset.row!)
const c = +(cube.dataset.col!)
const dist = Math.hypot(r - rowCenter, c - colCenter)
sceneRef.value.querySelectorAll<HTMLDivElement>('.cube').forEach(cube => {
const r = +cube.dataset.row!;
const c = +cube.dataset.col!;
const dist = Math.hypot(r - rowCenter, c - colCenter);
if (dist <= props.radius) {
const pct = 1 - dist / props.radius
const angle = pct * props.maxAngle
const pct = 1 - dist / props.radius;
const angle = pct * props.maxAngle;
gsap.to(cube, {
duration: enterDur.value,
ease: props.easing,
overwrite: true,
rotateX: -angle,
rotateY: angle,
})
rotateY: angle
});
} else {
gsap.to(cube, {
duration: leaveDur.value,
ease: 'power3.out',
overwrite: true,
rotateX: 0,
rotateY: 0,
})
rotateY: 0
});
}
})
}
});
};
const onPointerMove = (e: PointerEvent) => {
userActiveRef.value = true
if (idleTimerRef.value) clearTimeout(idleTimerRef.value)
userActiveRef.value = true;
if (idleTimerRef.value) clearTimeout(idleTimerRef.value);
const rect = sceneRef.value!.getBoundingClientRect()
const cellW = rect.width / props.gridSize
const cellH = rect.height / props.gridSize
const colCenter = (e.clientX - rect.left) / cellW
const rowCenter = (e.clientY - rect.top) / cellH
const rect = sceneRef.value!.getBoundingClientRect();
const cellW = rect.width / props.gridSize;
const cellH = rect.height / props.gridSize;
const colCenter = (e.clientX - rect.left) / cellW;
const rowCenter = (e.clientY - rect.top) / cellH;
if (rafRef.value) cancelAnimationFrame(rafRef.value)
rafRef.value = requestAnimationFrame(() =>
tiltAt(rowCenter, colCenter)
)
if (rafRef.value) cancelAnimationFrame(rafRef.value);
rafRef.value = requestAnimationFrame(() => tiltAt(rowCenter, colCenter));
idleTimerRef.value = setTimeout(() => {
userActiveRef.value = false
}, 3000)
}
userActiveRef.value = false;
}, 3000);
};
const resetAll = () => {
if (!sceneRef.value) return
sceneRef.value.querySelectorAll<HTMLDivElement>('.cube').forEach((cube) =>
if (!sceneRef.value) return;
sceneRef.value.querySelectorAll<HTMLDivElement>('.cube').forEach(cube =>
gsap.to(cube, {
duration: leaveDur.value,
rotateX: 0,
rotateY: 0,
ease: 'power3.out',
ease: 'power3.out'
})
)
}
);
};
const onClick = (e: MouseEvent) => {
if (!props.rippleOnClick || !sceneRef.value) return
if (!props.rippleOnClick || !sceneRef.value) return;
const rect = sceneRef.value.getBoundingClientRect()
const cellW = rect.width / props.gridSize
const cellH = rect.height / props.gridSize
const colHit = Math.floor((e.clientX - rect.left) / cellW)
const rowHit = Math.floor((e.clientY - rect.top) / cellH)
const rect = sceneRef.value.getBoundingClientRect();
const cellW = rect.width / props.gridSize;
const cellH = rect.height / props.gridSize;
const colHit = Math.floor((e.clientX - rect.left) / cellW);
const rowHit = Math.floor((e.clientY - rect.top) / cellH);
const baseRingDelay = 0.15
const baseAnimDur = 0.3
const baseHold = 0.6
const baseRingDelay = 0.15;
const baseAnimDur = 0.3;
const baseHold = 0.6;
const spreadDelay = baseRingDelay / props.rippleSpeed
const animDuration = baseAnimDur / props.rippleSpeed
const holdTime = baseHold / props.rippleSpeed
const spreadDelay = baseRingDelay / props.rippleSpeed;
const animDuration = baseAnimDur / props.rippleSpeed;
const holdTime = baseHold / props.rippleSpeed;
const rings: Record<number, HTMLDivElement[]> = {}
sceneRef.value.querySelectorAll<HTMLDivElement>('.cube').forEach((cube) => {
const r = +(cube.dataset.row!)
const c = +(cube.dataset.col!)
const dist = Math.hypot(r - rowHit, c - colHit)
const ring = Math.round(dist)
if (!rings[ring]) rings[ring] = []
rings[ring].push(cube)
})
const rings: Record<number, HTMLDivElement[]> = {};
sceneRef.value.querySelectorAll<HTMLDivElement>('.cube').forEach(cube => {
const r = +cube.dataset.row!;
const c = +cube.dataset.col!;
const dist = Math.hypot(r - rowHit, c - colHit);
const ring = Math.round(dist);
if (!rings[ring]) rings[ring] = [];
rings[ring].push(cube);
});
Object.keys(rings)
.map(Number)
.sort((a, b) => a - b)
.forEach((ring) => {
const delay = ring * spreadDelay
const faces = rings[ring].flatMap((cube) =>
Array.from(cube.querySelectorAll<HTMLElement>('.cube-face'))
)
.forEach(ring => {
const delay = ring * spreadDelay;
const faces = rings[ring].flatMap(cube => Array.from(cube.querySelectorAll<HTMLElement>('.cube-face')));
gsap.to(faces, {
backgroundColor: props.rippleColor,
duration: animDuration,
delay,
ease: 'power3.out',
})
ease: 'power3.out'
});
gsap.to(faces, {
backgroundColor: props.faceColor,
duration: animDuration,
delay: delay + animDuration + holdTime,
ease: 'power3.out',
})
})
}
ease: 'power3.out'
});
});
};
const startAutoAnimation = () => {
if (!props.autoAnimate || !sceneRef.value) return
if (!props.autoAnimate || !sceneRef.value) return;
simPosRef.value = {
x: Math.random() * props.gridSize,
y: Math.random() * props.gridSize,
}
y: Math.random() * props.gridSize
};
simTargetRef.value = {
x: Math.random() * props.gridSize,
y: Math.random() * props.gridSize,
}
y: Math.random() * props.gridSize
};
const speed = 0.02
const speed = 0.02;
const loop = () => {
if (!userActiveRef.value) {
const pos = simPosRef.value
const tgt = simTargetRef.value
pos.x += (tgt.x - pos.x) * speed
pos.y += (tgt.y - pos.y) * speed
tiltAt(pos.y, pos.x)
const pos = simPosRef.value;
const tgt = simTargetRef.value;
pos.x += (tgt.x - pos.x) * speed;
pos.y += (tgt.y - pos.y) * speed;
tiltAt(pos.y, pos.x);
if (Math.hypot(pos.x - tgt.x, pos.y - tgt.y) < 0.1) {
simTargetRef.value = {
x: Math.random() * props.gridSize,
y: Math.random() * props.gridSize,
}
y: Math.random() * props.gridSize
};
}
}
simRAFRef.value = requestAnimationFrame(loop)
}
simRAFRef.value = requestAnimationFrame(loop)
}
simRAFRef.value = requestAnimationFrame(loop);
};
simRAFRef.value = requestAnimationFrame(loop);
};
onMounted(() => {
const el = sceneRef.value
if (!el) return
const el = sceneRef.value;
if (!el) return;
el.addEventListener('pointermove', onPointerMove)
el.addEventListener('pointerleave', resetAll)
el.addEventListener('click', onClick)
el.addEventListener('pointermove', onPointerMove);
el.addEventListener('pointerleave', resetAll);
el.addEventListener('click', onClick);
startAutoAnimation()
})
startAutoAnimation();
});
onUnmounted(() => {
const el = sceneRef.value
const el = sceneRef.value;
if (el) {
el.removeEventListener('pointermove', onPointerMove)
el.removeEventListener('pointerleave', resetAll)
el.removeEventListener('click', onClick)
el.removeEventListener('pointermove', onPointerMove);
el.removeEventListener('pointerleave', resetAll);
el.removeEventListener('click', onClick);
}
if (rafRef.value !== null) cancelAnimationFrame(rafRef.value)
if (idleTimerRef.value !== null) clearTimeout(idleTimerRef.value)
if (simRAFRef.value !== null) cancelAnimationFrame(simRAFRef.value)
})
if (rafRef.value !== null) cancelAnimationFrame(rafRef.value);
if (idleTimerRef.value !== null) clearTimeout(idleTimerRef.value);
if (simRAFRef.value !== null) cancelAnimationFrame(simRAFRef.value);
});
</script>