From 6f34e0d126699c59c27cd53d451c3f9794c6a020 Mon Sep 17 00:00:00 2001 From: Rastislav Date: Mon, 20 Jan 2025 21:14:03 +0100 Subject: [PATCH] Animations Feature --- .gitignore | 8 + .vscode/settings.json | 9 + package.json | 40 ++-- .../BackrollCubes/BackrollCubes.module.scss | 138 +++++++++++ .../BackrollCubes/BackrollCubes.tsx | 15 ++ .../CubeAssembly/CubeAssembly.module.scss | 102 ++++++++ .../Animations/CubeAssembly/CubeAssembly.tsx | 15 ++ .../Animations/CubeDots/CubeDots.module.scss | 173 ++++++++++++++ .../Animations/CubeDots/CubeDots.tsx | 19 ++ .../CubeExpand/CubeExpand.module.scss | 140 +++++++++++ .../Animations/CubeExpand/CubeExpand.tsx | 26 +++ .../CubeFrame/CubeFrame.module.scss | 89 +++++++ .../Animations/CubeFrame/CubeFrame.tsx | 15 ++ .../CubeGradient/CubeGradient.module.scss | 60 +++++ .../Animations/CubeGradient/CubeGradient.tsx | 19 ++ .../Animations/CubeGrid/CubeGrid.module.scss | 156 +++++++++++++ .../Animations/CubeGrid/CubeGrid.tsx | 26 +++ .../CubeMatrix/CubeMatrix.module.scss | 63 +++++ .../Animations/CubeMatrix/CubeMatrix.tsx | 11 + .../CubeOrbit/CubeOrbit.module.scss | 100 ++++++++ .../Animations/CubeOrbit/CubeOrbit.tsx | 35 +++ .../CubeShift/CubeShift.module.scss | 65 ++++++ .../Animations/CubeShift/CubeShift.tsx | 18 ++ .../CubeSlide/CubeSlide.module.scss | 89 +++++++ .../Animations/CubeSlide/CubeSlide.tsx | 13 ++ .../CubeStretch/CubeStretch.module.scss | 120 ++++++++++ .../Animations/CubeStretch/CubeStretch.tsx | 13 ++ .../PenroseTriangle.module.scss | 75 ++++++ .../PenroseTriangle/PenroseTriangle.tsx | 13 ++ .../SwitchingCubes/SwitchingCubes.module.scss | 102 ++++++++ .../SwitchingCubes/SwitchingCubes.tsx | 29 +++ .../TricklingCubes/TricklingCubes.module.scss | 118 ++++++++++ .../TricklingCubes/TricklingCubes.tsx | 28 +++ .../TunnelBall/TunnelBall.module.scss | 220 ++++++++++++++++++ .../Animations/TunnelBall/TunnelBall.tsx | 21 ++ src/components/Animations/index.ts | 16 ++ src/components/index.ts | 1 + src/pages/animations.astro | 147 ++++++++++++ src/sections/home/IntroSection.tsx | 8 +- 39 files changed, 2330 insertions(+), 25 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/components/Animations/BackrollCubes/BackrollCubes.module.scss create mode 100644 src/components/Animations/BackrollCubes/BackrollCubes.tsx create mode 100644 src/components/Animations/CubeAssembly/CubeAssembly.module.scss create mode 100644 src/components/Animations/CubeAssembly/CubeAssembly.tsx create mode 100644 src/components/Animations/CubeDots/CubeDots.module.scss create mode 100644 src/components/Animations/CubeDots/CubeDots.tsx create mode 100644 src/components/Animations/CubeExpand/CubeExpand.module.scss create mode 100644 src/components/Animations/CubeExpand/CubeExpand.tsx create mode 100644 src/components/Animations/CubeFrame/CubeFrame.module.scss create mode 100644 src/components/Animations/CubeFrame/CubeFrame.tsx create mode 100644 src/components/Animations/CubeGradient/CubeGradient.module.scss create mode 100644 src/components/Animations/CubeGradient/CubeGradient.tsx create mode 100644 src/components/Animations/CubeGrid/CubeGrid.module.scss create mode 100644 src/components/Animations/CubeGrid/CubeGrid.tsx create mode 100644 src/components/Animations/CubeMatrix/CubeMatrix.module.scss create mode 100644 src/components/Animations/CubeMatrix/CubeMatrix.tsx create mode 100644 src/components/Animations/CubeOrbit/CubeOrbit.module.scss create mode 100644 src/components/Animations/CubeOrbit/CubeOrbit.tsx create mode 100644 src/components/Animations/CubeShift/CubeShift.module.scss create mode 100644 src/components/Animations/CubeShift/CubeShift.tsx create mode 100644 src/components/Animations/CubeSlide/CubeSlide.module.scss create mode 100644 src/components/Animations/CubeSlide/CubeSlide.tsx create mode 100644 src/components/Animations/CubeStretch/CubeStretch.module.scss create mode 100644 src/components/Animations/CubeStretch/CubeStretch.tsx create mode 100644 src/components/Animations/PenroseTriangle/PenroseTriangle.module.scss create mode 100644 src/components/Animations/PenroseTriangle/PenroseTriangle.tsx create mode 100644 src/components/Animations/SwitchingCubes/SwitchingCubes.module.scss create mode 100644 src/components/Animations/SwitchingCubes/SwitchingCubes.tsx create mode 100644 src/components/Animations/TricklingCubes/TricklingCubes.module.scss create mode 100644 src/components/Animations/TricklingCubes/TricklingCubes.tsx create mode 100644 src/components/Animations/TunnelBall/TunnelBall.module.scss create mode 100644 src/components/Animations/TunnelBall/TunnelBall.tsx create mode 100644 src/components/Animations/index.ts create mode 100644 src/pages/animations.astro diff --git a/.gitignore b/.gitignore index 16d54bb..9711bba 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,11 @@ pnpm-debug.log* # jetbrains setting folder .idea/ + +# astro setting folder +.astro/ + +# Lock file +package-lock.json +yarn.lock +pnpm-lock.yaml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..dd9bfb5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "workbench.colorCustomizations": { + "titleBar.activeForeground": "#283a22", + "titleBar.inactiveForeground": "#82bf72", + "titleBar.activeBackground": "#77bb65", + "titleBar.inactiveBackground": "#49713e" + }, + "editor.tabSize": 4 +} diff --git a/package.json b/package.json index fb661f9..8c78426 100644 --- a/package.json +++ b/package.json @@ -11,38 +11,38 @@ }, "dependencies": { "@astrojs/check": "^0.9.4", - "@astrojs/react": "4.1.1", - "@astrojs/starlight": "^0.30.3", - "@astrojs/tailwind": "5.1.3", + "@astrojs/react": "4.1.5", + "@astrojs/starlight": "^0.31.1", + "@astrojs/tailwind": "5.1.4", "@heroicons/react": "^2.2.0", "@splinetool/react-spline": "^4.0.0", - "@splinetool/runtime": "^1.9.48", + "@splinetool/runtime": "^1.9.59", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", - "astro": "5.1.0", + "astro": "5.1.7", "clsx": "^2.1.1", - "motion": "^11.12.0", + "motion": "^11.18.1", "react": "^18.3.1", "react-dom": "^18.3.1", - "tailwindcss": "^3.4.15", - "typescript": "^5.7.2" + "tailwindcss": "^3.4.17", + "typescript": "^5.7.3" }, "devDependencies": { - "@eslint/js": "^9.15.0", + "@eslint/js": "^9.18.0", "@phosphor-icons/react": "^2.1.7", - "@typescript-eslint/parser": "^8.16.0", - "eslint": "^9.15.0", - "eslint-config-prettier": "^9.1.0", + "@typescript-eslint/parser": "^8.20.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", "eslint-plugin-astro": "^1.3.1", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.0.0", - "eslint-plugin-tailwindcss": "^3.17.5", - "prettier": "^3.4.1", + "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-tailwindcss": "^3.18.0", + "prettier": "^3.4.2", "prettier-plugin-astro": "^0.14.1", - "sass": "^1.81.0", - "tailwind-merge": "^2.5.5", - "typescript-eslint": "^8.16.0" + "sass": "^1.83.4", + "tailwind-merge": "^2.6.0", + "typescript-eslint": "^8.20.0" } } diff --git a/src/components/Animations/BackrollCubes/BackrollCubes.module.scss b/src/components/Animations/BackrollCubes/BackrollCubes.module.scss new file mode 100644 index 0000000..8f85793 --- /dev/null +++ b/src/components/Animations/BackrollCubes/BackrollCubes.module.scss @@ -0,0 +1,138 @@ +$grid-size: 5; // sqrt of number of cubes +$cube-size: 4em; // cube edge length +$faces: 6; // number of cube faces +$edges: 4; // number of square edges +$color: #ddd; +$brightness-percent: 10%; +$gap: 0.25 * $cube-size; +$middle: 0.5 * ($grid-size - 1); +$distance: $cube-size + $gap; +$animation-distance: 3 * $distance; +$edge-angle: 360deg/$edges; +$duration: 2.8s; +$breakpoint: 50%/$grid-size; + +@mixin background($color, $angle, $percent) { + background: darken($color, (1 - cos($angle)) * $percent); +} + +@for $i from 0 through $grid-size { + .animate-backrollcubes-cube:nth-child(n) { + &[data-movement="#{$i}"] { + animation-name: animate-backrollcubes-movement-#{$i}; + + div:before { + animation-name: animate-backrollcubes-fade-#{$i}; + } + } + } + + @keyframes animate-backrollcubes-movement-#{$i} { + #{$i * $breakpoint} { + transform: none; + } + #{($i + 2) * $breakpoint}, 100% { + transform: translateZ(-$animation-distance) rotateX($edge-angle); + } + } + + @keyframes animate-backrollcubes-fade-#{$i} { + #{$i * $breakpoint} { + opacity: 0.999; + } + #{($i + 2) * $breakpoint}, 100% { + opacity: 0.001; + } + } +} + +.animate-backrollcubes-assembly { + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + animation: animate-backrollcubes-assembly $duration linear infinite; +} + +@keyframes animate-backrollcubes-assembly { + to { + transform: translateZ($animation-distance); + } +} + +.animate-backrollcubes-cube { + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + animation: animate-backrollcubes-phase $duration cubic-bezier(0.65, 0.05, 0.35, 1) infinite; + + @for $i from 0 to $grid-size { + $y: ($i - $middle) * $distance; + $q1: abs($i - $middle); + + @for $j from 0 to $grid-size { + $x: ($j - $middle) * $distance; + $idx: $i * $grid-size + $j + 1; + $q2: abs($j - $middle); + $s: $q1 + $q2 + max(0, max($q1, $q2) - 1); + + &:nth-child(#{$idx}) { + margin: $y $x; + animation-name: animate-backrollcubes-movement-#{$s}; + + div:before { + animation-name: animate-backrollcubes-fade-#{$s}; + } + } + } + } +} + +.animate-backrollcubes-cube__face { + position: absolute; + top: 50%; + left: 50%; + margin: -0.5 * $cube-size; + width: $cube-size; + height: $cube-size; + backface-visibility: hidden; + + &:nth-child(-n + 4):before { + position: absolute; + width: inherit; + height: inherit; + animation: animate-backrollcubes-phase $duration ease-in-out infinite; + content: ''; + } + + &:nth-child(n + 5) { + background: darken($color, 3 * $brightness-percent); + } + + @for $i from 0 to $faces { + $angle: $i * $edge-angle; + $prev-angle: ($i - 1) * $edge-angle; + + &:nth-child(#{$i + 1}) { + transform: if($i < $edges, + rotateX($angle), + rotateY(pow(-1, $i) * $edge-angle) + ) translateZ(0.5 * $cube-size); + + @if $i < $edges { + @include background($color, $angle, $brightness-percent); + + &:before { + @include background($color, $prev-angle, $brightness-percent); + } + } + } + } +} + +@keyframes animate-backrollcubes-phase { + from, to { + transform: none; + } +} diff --git a/src/components/Animations/BackrollCubes/BackrollCubes.tsx b/src/components/Animations/BackrollCubes/BackrollCubes.tsx new file mode 100644 index 0000000..99bab5e --- /dev/null +++ b/src/components/Animations/BackrollCubes/BackrollCubes.tsx @@ -0,0 +1,15 @@ +import styles from './BackrollCubes.module.scss'; + +export const BackrollCubes = () => { + return ( +
+ {Array.from({ length: 25 }, (_, i) => ( +
+ {Array.from({ length: 6 }, (_, j) => ( +
+ ))} +
+ ))} +
+ ); +}; diff --git a/src/components/Animations/CubeAssembly/CubeAssembly.module.scss b/src/components/Animations/CubeAssembly/CubeAssembly.module.scss new file mode 100644 index 0000000..5c71d1e --- /dev/null +++ b/src/components/Animations/CubeAssembly/CubeAssembly.module.scss @@ -0,0 +1,102 @@ +$palette: #1c6c3e #dd7ca9; // Using our theme colors +$n-items: 6; +$edge-length: 3.125em; +$inradius: .5 * $edge-length; +$s: .2; // minimum scale factor +$t: .6s; // animation duration +$p: 7%; // difference between face shades + +@mixin c($c, $p: $p) { + color: lighten(saturate($c, $p), $p); +} + +.wrapper { + width: 100%; + height: 100%; + position: relative; +} + +.assembly { + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + transform: rotateX(-35deg) rotateY(-45deg) translateZ($inradius); +} + +.item { + position: absolute; + margin: -$inradius; + width: $edge-length; + height: $edge-length; + transform-style: preserve-3d; + transform-origin: 50% 50% (-1 * $inradius); + animation: s $t cubic-bezier(.45, .03, .51, .95) infinite alternate; + + // Front face + background: currentColor; + box-shadow: 0 0 .125em currentColor; + transform-style: preserve-3d; + + // Top face + &:before { + position: absolute; + content: ''; + width: 100%; + height: $edge-length; + background: currentColor; + transform: rotateX(90deg) translateY(-100%); + transform-origin: top; + box-shadow: inherit; + } + + // Left side face + &:after { + position: absolute; + content: ''; + width: $edge-length; + height: 100%; + background: currentColor; + transform: rotateY(-90deg) translateX(-100%); + transform-origin: left; + box-shadow: inherit; + } + + // Right side face + div { + position: absolute; + width: $edge-length; + height: 100%; + background: currentColor; + transform: rotateY(90deg) translateX(100%); + transform-origin: right; + box-shadow: inherit; + } + + @for $i from 0 through ($n-items - 1) { + $n: $n-items - 1; + $c: mix(nth($palette, 1), nth($palette, 2), $i/$n * 100%); + + &:nth-child(#{$i + 1}) { + margin-top: (.5 * $n - $i - .5) * $edge-length; + color: $c; + animation-delay: ($i/$n-items - 1) * 2 * $t; + + &:before { + @include c($c, 2 * $p); + } + &:after { + @include c($c); + filter: brightness(0.85); + } + div { + @include c($c); + filter: brightness(0.7); + } + } + } +} + +@keyframes s { + to { transform: scale3d($s, $s, $s); } +} diff --git a/src/components/Animations/CubeAssembly/CubeAssembly.tsx b/src/components/Animations/CubeAssembly/CubeAssembly.tsx new file mode 100644 index 0000000..baac425 --- /dev/null +++ b/src/components/Animations/CubeAssembly/CubeAssembly.tsx @@ -0,0 +1,15 @@ +import styles from './CubeAssembly.module.scss'; + +export const CubeAssembly = () => { + return ( +
+
+ {Array.from({ length: 6 }, (_, index) => ( +
+
+
+ ))} +
+
+ ); +}; diff --git a/src/components/Animations/CubeDots/CubeDots.module.scss b/src/components/Animations/CubeDots/CubeDots.module.scss new file mode 100644 index 0000000..7257051 --- /dev/null +++ b/src/components/Animations/CubeDots/CubeDots.module.scss @@ -0,0 +1,173 @@ +.animate-cubedots-wrapper { + display: flex; + width: 100vw; + height: 100vh; + margin: 0; + padding: 0; + align-items: center; + justify-content: center; + + &__content { + height: 450px; + width: 280px; + position: relative; + } +} + +@keyframes animate-cubedots-rotation { + 0% { + transform: rotateZ(0deg) rotateX(-15deg) rotateY(0deg); + } + 100% { + transform: rotateZ(0deg) rotateX(-15deg) rotateY(-360deg); + } +} + +@keyframes animate-cubedots-color-front { + 0%, 100% { + opacity: 1; + } + 25%, 75% { + opacity: 0; + } +} + +@keyframes animate-cubedots-color-back { + 0%, 100%, 25%, 75% { + opacity: 0; + } + 50% { + opacity: 1; + } +} + +@keyframes animate-cubedots-color-right { + 0%, 50% { + opacity: 0.25; + } + 50% { + opacity: 0; + } + 25% { + opacity: 1; + } +} + +@keyframes animate-cubedots-color-left { + 0%, 50%, 100% { + opacity: 0; + } + 75% { + opacity: 1; + } +} + +.animate-cubedots-cube { + --animate-cubedots-z-angle: 100px; + --animate-cubedots-color: #06FFFF; + --animate-cubedots-time: 5s; + + width: 203px; + height: 203px; + position: relative; + transform-style: preserve-3d; + transform-origin: center center; + transform: rotateZ(0deg) rotateX(0deg) rotateY(0deg); + animation: animate-cubedots-rotation var(--animate-cubedots-time) infinite linear; +} + +.animate-cubedots-side { + position: absolute; + width: 203px; + height: 203px; + opacity: 0; + + &:after { + content: ''; + left: 0; + top: 0; + width: 3px; + height: 3px; + background: var(--animate-cubedots-color); + position: absolute; + border-radius: 100%; + box-shadow: + 0px 40px 0px 0px var(--animate-cubedots-color), + 0px 80px 0px 0px var(--animate-cubedots-color), + 0px 120px 0px 0px var(--animate-cubedots-color), + 0px 160px 0px 0px var(--animate-cubedots-color), + 0px 200px 0px 0px var(--animate-cubedots-color), + 40px 0px 0px 0px var(--animate-cubedots-color), + 80px 0px 0px 0px var(--animate-cubedots-color), + 120px 0px 0px 0px var(--animate-cubedots-color), + 160px 0px 0px 0px var(--animate-cubedots-color), + 200px 0px 0px 0px var(--animate-cubedots-color), + 40px 40px 0px 0px var(--animate-cubedots-color), + 80px 40px 0px 0px var(--animate-cubedots-color), + 120px 40px 0px 0px var(--animate-cubedots-color), + 160px 40px 0px 0px var(--animate-cubedots-color), + 200px 40px 0px 0px var(--animate-cubedots-color), + 40px 80px 0px 0px var(--animate-cubedots-color), + 80px 80px 0px 0px var(--animate-cubedots-color), + 120px 80px 0px 0px var(--animate-cubedots-color), + 160px 80px 0px 0px var(--animate-cubedots-color), + 200px 80px 0px 0px var(--animate-cubedots-color), + 40px 120px 0px 0px var(--animate-cubedots-color), + 80px 120px 0px 0px var(--animate-cubedots-color), + 120px 120px 0px 0px var(--animate-cubedots-color), + 160px 120px 0px 0px var(--animate-cubedots-color), + 200px 120px 0px 0px var(--animate-cubedots-color), + 40px 160px 0px 0px var(--animate-cubedots-color), + 80px 160px 0px 0px var(--animate-cubedots-color), + 120px 160px 0px 0px var(--animate-cubedots-color), + 160px 160px 0px 0px var(--animate-cubedots-color), + 200px 160px 0px 0px var(--animate-cubedots-color), + 40px 200px 0px 0px var(--animate-cubedots-color), + 80px 200px 0px 0px var(--animate-cubedots-color), + 120px 200px 0px 0px var(--animate-cubedots-color), + 160px 200px 0px 0px var(--animate-cubedots-color), + 200px 200px 0px 0px var(--animate-cubedots-color); + } + + &--front { + animation: animate-cubedots-color-front var(--animate-cubedots-time) infinite linear; + transform: rotateY(0deg) translateZ(var(--animate-cubedots-z-angle)); + } + + &--right { + transform: rotateY(90deg) translateZ(var(--animate-cubedots-z-angle)); + animation: animate-cubedots-color-right var(--animate-cubedots-time) infinite linear; + } + + &--back { + transform: rotateY(180deg) translateZ(var(--animate-cubedots-z-angle)); + animation: animate-cubedots-color-back var(--animate-cubedots-time) infinite linear; + } + + &--left { + transform: rotateY(-90deg) translateZ(var(--animate-cubedots-z-angle)); + animation: animate-cubedots-color-left var(--animate-cubedots-time) infinite linear; + } + + &--top { + opacity: 1; + transform: rotateX(90deg) translateZ(var(--animate-cubedots-z-angle)); + } + + &--bottom { + opacity: 0; + } +} + +.animate-cubedots-shadow { + position: absolute; + top: 258px; + width: 203px; + height: 203px; + transform-style: preserve-3d; + transform: rotateX(90deg); + filter: blur(8px); + opacity: 0.2; + background: var(--animate-cubedots-color); + animation: animate-cubedots-rotation var(--animate-cubedots-time) infinite linear; +} diff --git a/src/components/Animations/CubeDots/CubeDots.tsx b/src/components/Animations/CubeDots/CubeDots.tsx new file mode 100644 index 0000000..a1134a9 --- /dev/null +++ b/src/components/Animations/CubeDots/CubeDots.tsx @@ -0,0 +1,19 @@ +import styles from './CubeDots.module.scss'; + +export const CubeDots = () => { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+ ); +}; diff --git a/src/components/Animations/CubeExpand/CubeExpand.module.scss b/src/components/Animations/CubeExpand/CubeExpand.module.scss new file mode 100644 index 0000000..a183e00 --- /dev/null +++ b/src/components/Animations/CubeExpand/CubeExpand.module.scss @@ -0,0 +1,140 @@ +:root { + --animate-cubeexpand-size: 2em; + --animate-cubeexpand-multiplier: 5; + --animate-cubeexpand-duration: 0.5s; + --animate-cubeexpand-color1: #ffffff; + --animate-cubeexpand-color2: #ff3366; + --animate-cubeexpand-color3: #ffcc33; + --animate-cubeexpand-color4: #ccff33; +} + +.animate-cubeexpand-wrapper { + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + transform: rotateX(-35deg) rotateY(-45deg); +} + +.animate-cubeexpand-group { + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + + $rotation-chain: (); + $reverse-chain: (); + + @for $i from 0 through 5 { + $j: $i % 2; + + &:nth-child(#{$i + 1}) { + @if $i > 0 { + $rotation: rotate3d(0, #{1 - $j}, $j, #{pow(-1, $j + 1) * 90}deg); + $rotation-chain: $rotation-chain $rotation; + $reverse-chain: rotate3d(0, #{1 - $j}, $j, #{pow(-1, $j) * 90}deg) $reverse-chain; + } + + z-index: $j; + transform: $rotation-chain translate3d( + 0, + calc(-0.5 * (var(--animate-cubeexpand-multiplier) + 1) * var(--animate-cubeexpand-size)), + calc(-0.5 * (var(--animate-cubeexpand-multiplier) + 1) * var(--animate-cubeexpand-size)) + ); + + > .animate-cubeexpand-scale3d { + transform: translateX(calc(-0.5 * (var(--animate-cubeexpand-multiplier) + 1) * var(--animate-cubeexpand-size))); + + .animate-cubeexpand-face { + box-shadow: 0 0 1px currentColor; + } + } + } + } + + &:nth-child(3n + 1) .animate-cubeexpand-expand { color: var(--animate-cubeexpand-color2); } + &:nth-child(3n + 2) .animate-cubeexpand-expand { color: var(--animate-cubeexpand-color3); } + &:nth-child(3n + 3) .animate-cubeexpand-expand { color: var(--animate-cubeexpand-color4); } +} + +.animate-cubeexpand-expand { + transform: scaleX(var(--animate-cubeexpand-multiplier)); + transform-style: preserve-3d; +} + +.animate-cubeexpand-scale3d { + position: absolute; + transform-style: preserve-3d; + + .animate-cubeexpand-expand & { + transform-origin: calc(var(--animate-cubeexpand-size) * -0.5); + animation: + animate-cubeexpand-scale var(--animate-cubeexpand-duration) ease-in-out infinite alternate, + animate-cubeexpand-origin calc(var(--animate-cubeexpand-duration) * 2) steps(1) infinite; + } + + .animate-cubeexpand-group:nth-child(2n) .animate-cubeexpand-expand & { + animation-delay: calc(var(--animate-cubeexpand-duration) * -1); + } +} + +.animate-cubeexpand-reverse { + position: absolute; + transform-style: preserve-3d; +} + +.animate-cubeexpand-face { + position: absolute; + margin: calc(var(--animate-cubeexpand-size) * -0.5); + width: var(--animate-cubeexpand-size); + height: var(--animate-cubeexpand-size); + background: currentColor; + transform-style: preserve-3d; + backface-visibility: visible; + + // All six faces of the cube + &:nth-child(1) { + transform: translateZ(calc(var(--animate-cubeexpand-size) * 0.5)); + filter: brightness(1); + } + &:nth-child(2) { + transform: rotateY(180deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5)); + filter: brightness(0.8); + } + &:nth-child(3) { + transform: rotateY(90deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5)); + filter: brightness(0.9); + } + &:nth-child(4) { + transform: rotateY(-90deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5)); + filter: brightness(0.7); + } + &:nth-child(5) { + transform: rotateX(90deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5)); + filter: brightness(0.95); + } + &:nth-child(6) { + transform: rotateX(-90deg) translateZ(calc(var(--animate-cubeexpand-size) * 0.5)); + filter: brightness(0.85); + } +} + +@keyframes animate-cubeexpand-scale { + 0% { + transform: scale3d(0, 1, 1); + } + 100% { + transform: scale3d(1, 1, 1); + } +} + +@keyframes animate-cubeexpand-origin { + 50% { + transform-origin: calc(var(--animate-cubeexpand-size) * 0.5); + } +} + +:global(body) { + background: #000; + overflow: hidden; +} diff --git a/src/components/Animations/CubeExpand/CubeExpand.tsx b/src/components/Animations/CubeExpand/CubeExpand.tsx new file mode 100644 index 0000000..74af7dd --- /dev/null +++ b/src/components/Animations/CubeExpand/CubeExpand.tsx @@ -0,0 +1,26 @@ +import styles from './CubeExpand.module.scss'; + +const Block = () => ( +
+
+ {Array.from({ length: 6 }, (_, i) => ( +
+ ))} +
+
+); + +export const CubeExpand = () => { + return ( +
+ {Array.from({ length: 6 }, (_, i) => ( +
+ +
+ +
+
+ ))} +
+ ); +}; diff --git a/src/components/Animations/CubeFrame/CubeFrame.module.scss b/src/components/Animations/CubeFrame/CubeFrame.module.scss new file mode 100644 index 0000000..bea3e74 --- /dev/null +++ b/src/components/Animations/CubeFrame/CubeFrame.module.scss @@ -0,0 +1,89 @@ +$n: 7; +$s: 5rem; + +.animate-cubeframe-cubes { + transform: rotateX(55deg) rotateZ(-45deg); + position: absolute; + width: $s; + height: $s; + transform-style: preserve-3d; + display: flex; + justify-content: center; + align-items: center; +} + +.animate-cubeframe-cube { + position: absolute; + width: $s; + height: $s; + transform-style: preserve-3d; + display: flex; + justify-content: center; + align-items: center; +} + +.animate-cubeframe-x, +.animate-cubeframe-y, +.animate-cubeframe-z { + position: absolute; + width: $s; + height: $s; + transform-style: preserve-3d; + display: flex; + justify-content: center; + align-items: center; + + &:before, + &:after { + content: ''; + position: absolute; + width: $s; + height: $s; + border: 1px solid white; + box-shadow: inset 0 0 3px 0 white; + background-color: #272727; + } +} + +.animate-cubeframe-x { + transform: rotateX(90deg); + &:before { transform: translateZ($s / 2); } + &:after { transform: translateZ(-$s / 2); } +} + +.animate-cubeframe-y { + transform: rotateY(90deg); + &:before { transform: translateZ($s / 2); } + &:after { transform: translateZ(-$s / 2); } +} + +.animate-cubeframe-z { + transform: rotateZ(90deg); + &:before { transform: translateZ($s / 2); } + &:after { transform: translateZ(-$s / 2); } +} + +@for $i from 0 through $n - 1 { + .animate-cubeframe-cube:nth-child(#{$i + 1}) { + --animate-cubeframe-delay: #{$i * 0.15}s; + animation: animate-cubeframe-spin 3s var(--animate-cubeframe-delay) cubic-bezier(1, -0.50, 0.50, 1.25) infinite; + + .animate-cubeframe-x, + .animate-cubeframe-y, + .animate-cubeframe-z { + &:before { + transform: translateZ((($s / 3) * $i) + ($s / 2)); + } + + &:after { + transform: translateZ(((-$s / 3) * $i) - ($s / 2)); + } + } + } +} + +@keyframes animate-cubeframe-spin { + 50%, 100% { + transform: rotateZ(-90deg); + } +} diff --git a/src/components/Animations/CubeFrame/CubeFrame.tsx b/src/components/Animations/CubeFrame/CubeFrame.tsx new file mode 100644 index 0000000..4f0622a --- /dev/null +++ b/src/components/Animations/CubeFrame/CubeFrame.tsx @@ -0,0 +1,15 @@ +import styles from './CubeFrame.module.scss'; + +export const CubeFrame = () => { + return ( +
+ {Array.from({ length: 7 }, (_, i) => ( +
+
+
+
+
+ ))} +
+ ); +}; diff --git a/src/components/Animations/CubeGradient/CubeGradient.module.scss b/src/components/Animations/CubeGradient/CubeGradient.module.scss new file mode 100644 index 0000000..b334638 --- /dev/null +++ b/src/components/Animations/CubeGradient/CubeGradient.module.scss @@ -0,0 +1,60 @@ +.animate-cubegradient-cube { + position: relative; + width: 300px; + height: 300px; + transform-style: preserve-3d; + animation: animate-cubegradient-animate 4s linear infinite; + margin: 0 auto; + + @keyframes animate-cubegradient-animate { + 0% { + transform: rotateX(-30deg) rotateY(0deg); + } + 100% { + transform: rotateX(-30deg) rotateY(360deg); + } + } +} + +.animate-cubegradient-sides { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + transform-style: preserve-3d; +} + +.animate-cubegradient-side { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(#212121, #1c6c3e); + transform: rotateY(calc(90deg * var(--animate-cubegradient-i))) translateZ(150px); +} + +.animate-cubegradient-top { + position: absolute; + top: 0; + left: 0; + width: 300px; + height: 300px; + background: #222; + transform: rotateX(90deg) translateZ(150px); +} + +.animate-cubegradient-bottom { + position: absolute; + top: 0; + left: 0; + width: 300px; + height: 300px; + background: #1c6c3e; + transform: rotateX(-90deg) translateZ(150px); + filter: blur(20px); + box-shadow: 0 0 120px rgba(28, 108, 62, 0.2), + 0 0 200px rgba(28, 108, 62, 0.4), + 0 0 300px rgba(28, 108, 62, 0.6); +} diff --git a/src/components/Animations/CubeGradient/CubeGradient.tsx b/src/components/Animations/CubeGradient/CubeGradient.tsx new file mode 100644 index 0000000..46a6b31 --- /dev/null +++ b/src/components/Animations/CubeGradient/CubeGradient.tsx @@ -0,0 +1,19 @@ +import styles from './CubeGradient.module.scss'; + +export const CubeGradient = () => { + return ( +
+
+
+
+ {[0, 1, 2, 3].map((index) => ( + + ))} +
+
+ ); +}; diff --git a/src/components/Animations/CubeGrid/CubeGrid.module.scss b/src/components/Animations/CubeGrid/CubeGrid.module.scss new file mode 100644 index 0000000..0af4a44 --- /dev/null +++ b/src/components/Animations/CubeGrid/CubeGrid.module.scss @@ -0,0 +1,156 @@ +// Define size variable first +$size: 60; + +:root { + --animate-cubegrid-size: 60px; + --animate-cubegrid-color-front: #e7Ac20; + --animate-cubegrid-color-left: #d53a33; + --animate-cubegrid-color-top: #1d9099; +} + +* { + box-sizing: border-box; + transition: 0.35s ease; +} + +.animate-cubegrid-container { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + width: 88%; + width: var(--animate-cubegrid-size); + height: var(--animate-cubegrid-size); + perspective: 6000px; +} + +.animate-cubegrid-cube { + position: absolute; + height: 100%; + width: 100%; + transform-style: preserve-3d; + transform: rotateX(-30deg) rotateY(45deg); + + div { + position: absolute; + width: 100%; + height: 100%; + } + + .animate-cubegrid-front { + transform: rotateY(0deg) translateZ(calc(var(--animate-cubegrid-size) / 2)); + background-color: var(--animate-cubegrid-color-front); + } + + .animate-cubegrid-left { + transform: rotateY(-90deg) translateZ(calc(var(--animate-cubegrid-size) / 2)); + background-color: var(--animate-cubegrid-color-left); + } + + .animate-cubegrid-top { + transform: rotateX(90deg) translateZ(calc(var(--animate-cubegrid-size) / 2)); + background-color: var(--animate-cubegrid-color-top); + } + + @for $i from 1 through 27 { + &:nth-child(#{$i}) { + animation: animate-cubegrid-#{$i} 3.5s cubic-bezier(0.75, 0, 0.2, 1) infinite; + } + } + + // Base cube positions - matching original exactly + &.cube1 { transform: rotateX(-30deg) rotateY(45deg) translateX(($size - 1)px) translateY(($size - 1)px) translateZ(-($size - 1)px) } + &.cube2 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(($size - 1)px) translateZ(-($size - 1)px) } + &.cube3 { transform: rotateX(-30deg) rotateY(45deg) translateX(($size - 1)px) translateY(($size - 1)px) translateZ(0) } + &.cube4 { transform: rotateX(-30deg) rotateY(45deg) translateX(-($size - 1)px) translateY(($size - 1)px) translateZ(-($size - 1)px) } + &.cube5 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(($size - 1)px) translateZ(0) } + &.cube6 { transform: rotateX(-30deg) rotateY(45deg) translateX(($size - 1)px) translateY(($size - 1)px) translateZ(($size - 1)px) } + &.cube7 { transform: rotateX(-30deg) rotateY(45deg) translateX(-($size - 1)px) translateY(($size - 1)px) translateZ(0) } + &.cube8 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(($size - 1)px) translateZ(($size - 1)px) } + &.cube9 { transform: rotateX(-30deg) rotateY(45deg) translateX(-($size - 1)px) translateY(($size - 1)px) translateZ(($size - 1)px) } + + // Middle layer (z = 1) + &.cube10 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(0) translateZ(var(--animate-cubegrid-size)) } + &.cube11 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(0) translateZ(var(--animate-cubegrid-size)) } + &.cube12 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(0) translateZ(var(--animate-cubegrid-size)) } + &.cube13 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(var(--animate-cubegrid-size)) translateZ(var(--animate-cubegrid-size)) } + &.cube14 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(var(--animate-cubegrid-size)) translateZ(var(--animate-cubegrid-size)) } + &.cube15 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(var(--animate-cubegrid-size)) translateZ(var(--animate-cubegrid-size)) } + &.cube16 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(var(--animate-cubegrid-size)) } + &.cube17 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(var(--animate-cubegrid-size)) } + &.cube18 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(var(--animate-cubegrid-size)) } + + // Top layer (z = 2) + &.cube19 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(0) translateZ(calc(2 * var(--animate-cubegrid-size))) } + &.cube20 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(0) translateZ(calc(2 * var(--animate-cubegrid-size))) } + &.cube21 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(0) translateZ(calc(2 * var(--animate-cubegrid-size))) } + &.cube22 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(var(--animate-cubegrid-size)) translateZ(calc(2 * var(--animate-cubegrid-size))) } + &.cube23 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(var(--animate-cubegrid-size)) translateZ(calc(2 * var(--animate-cubegrid-size))) } + &.cube24 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(var(--animate-cubegrid-size)) translateZ(calc(2 * var(--animate-cubegrid-size))) } + &.cube25 { transform: rotateX(-30deg) rotateY(45deg) translateX(0) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(calc(2 * var(--animate-cubegrid-size))) } + &.cube26 { transform: rotateX(-30deg) rotateY(45deg) translateX(var(--animate-cubegrid-size)) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(calc(2 * var(--animate-cubegrid-size))) } + &.cube27 { transform: rotateX(-30deg) rotateY(45deg) translateX(calc(2 * var(--animate-cubegrid-size))) translateY(calc(2 * var(--animate-cubegrid-size))) translateZ(calc(2 * var(--animate-cubegrid-size))) } +} + +// Animation keyframes for each cube +@for $i from 1 through 27 { + $x: ($i - 1) % 3 - 1; + $y: floor(($i - 1) / 9) - 1; + $z: floor(($i - 1) % 9 / 3) - 1; + + @keyframes animate-cubegrid-#{$i} { + 0% { + transform: rotateX(-30deg) rotateY(45deg) + translateX(calc(#{$x} * (var(--animate-cubegrid-size) - 1px))) + translateY(calc(#{$y} * (var(--animate-cubegrid-size) - 1px))) + translateZ(calc(#{$z} * (var(--animate-cubegrid-size) - 1px))); + } + 16.666% { + transform: rotateX(-30deg) rotateY(45deg) + translateX(calc(#{$x} * (var(--animate-cubegrid-size) - 1px))) + translateY(calc(#{$y} * (var(--animate-cubegrid-size) - 1px))) + translateZ(calc(#{if($z < 0, -2, if($z > 0, 2, $z))} * var(--animate-cubegrid-size))); + } + 33.333% { + transform: rotateX(-30deg) rotateY(45deg) + translateX(calc(#{if($x < 0, -2, if($x > 0, 2, $x))} * var(--animate-cubegrid-size))) + translateY(calc(#{$y} * (var(--animate-cubegrid-size) - 1px))) + translateZ(calc(#{if($z < 0, -2, if($z > 0, 2, $z))} * var(--animate-cubegrid-size))); + } + 50% { + transform: rotateX(-30deg) rotateY(45deg) + translateX(calc(#{if($x < 0, -2, if($x > 0, 2, $x))} * var(--animate-cubegrid-size))) + translateY(calc(#{if($y < 0, -1.675, if($y > 0, 1.675, $y))} * var(--animate-cubegrid-size))) + translateZ(calc(#{if($z < 0, -2, if($z > 0, 2, $z))} * var(--animate-cubegrid-size))); + } + 66.666% { + transform: rotateX(-30deg) rotateY(45deg) + translateX(calc(#{if($x < 0, -2, if($x > 0, 2, $x))} * var(--animate-cubegrid-size))) + translateY(calc(#{if($y < 0, -1.675, if($y > 0, 1.675, $y))} * var(--animate-cubegrid-size))) + translateZ(calc(#{$z} * (var(--animate-cubegrid-size) - 1px))); + } + 83.333% { + transform: rotateX(-30deg) rotateY(45deg) + translateX(calc(#{$x} * (var(--animate-cubegrid-size) - 1px))) + translateY(calc(#{if($y < 0, -1.675, if($y > 0, 1.675, $y))} * var(--animate-cubegrid-size))) + translateZ(calc(#{$z} * (var(--animate-cubegrid-size) - 1px))); + } + 100% { + transform: rotateX(-30deg) rotateY(45deg) + translateX(calc(#{$x} * (var(--animate-cubegrid-size) - 1px))) + translateY(calc(#{$y} * (var(--animate-cubegrid-size) - 1px))) + translateZ(calc(#{$z} * (var(--animate-cubegrid-size) - 1px))); + } + } +} + +@media screen and (max-width: 600px) { + .animate-cubegrid-container { + transform: translate(-50%, -50%) scale(0.55); + } +} + +:global(body) { + background-color: #262626; +} diff --git a/src/components/Animations/CubeGrid/CubeGrid.tsx b/src/components/Animations/CubeGrid/CubeGrid.tsx new file mode 100644 index 0000000..461b893 --- /dev/null +++ b/src/components/Animations/CubeGrid/CubeGrid.tsx @@ -0,0 +1,26 @@ +import styles from './CubeGrid.module.scss'; + +const SmallCube = () => ( +
+
+
+
+
+); + +const CubeSection = () => ( +
+ {Array.from({ length: 27 }, (_, i) => ( + + ))} +
+); + +export const CubeGrid = () => { + return ( + <> + + + + ); +}; diff --git a/src/components/Animations/CubeMatrix/CubeMatrix.module.scss b/src/components/Animations/CubeMatrix/CubeMatrix.module.scss new file mode 100644 index 0000000..b59de99 --- /dev/null +++ b/src/components/Animations/CubeMatrix/CubeMatrix.module.scss @@ -0,0 +1,63 @@ +$size: 50px; +$cubeTop: #F6FDF8; +$cubeLeft: #6C7340; +$cubeRight: #72A298; +$defaultSpacing: 30px; // Default gap between cubes +$expandedSpacing: 60px; // Expanded spacing for animation + +.animate-cubematrix-container { + position: relative; + width: ($size * 3) + ($defaultSpacing * 2); + height: ($size * 3) + ($defaultSpacing * 2); + transform-style: preserve-3d; + transform: rotateX(45deg) rotateZ(45deg); +} + +.animate-cubematrix-cube, +.animate-cubematrix-cube:before, +.animate-cubematrix-cube:after { + width: $size; + height: $size; + position: absolute; +} + +.animate-cubematrix-cube { + background-color: $cubeTop; + transform-style: preserve-3d; + + &:before { + content: ""; + background-color: $cubeLeft; + transform-origin: 100% 100%; + transform: rotateX(-90deg) translateY($size); + } + + &:after { + content: ""; + transform-origin: 100% 0; + background-color: $cubeRight; + transform: rotateY(90deg) translateX($size); + } +} + +@for $i from 0 through 2 { + @for $j from 0 through 2 { + $index: $i * 3 + $j + 1; + .animate-cubematrix-cube:nth-child(#{$index}) { + // Default position includes spacing + $baseX: ($size + $defaultSpacing) * (2 - $j); + $baseY: ($size + $defaultSpacing) * (2 - $i); + + animation: splitCube#{$index} 4s ease-in-out infinite; + + @keyframes splitCube#{$index} { + 0%, 100% { + transform: translateX($baseX) translateY($baseY); + } + 50% { + transform: translateX($size * (2 - $j)) translateY($size * (2 - $i)); + } + } + } + } +} diff --git a/src/components/Animations/CubeMatrix/CubeMatrix.tsx b/src/components/Animations/CubeMatrix/CubeMatrix.tsx new file mode 100644 index 0000000..2af7d37 --- /dev/null +++ b/src/components/Animations/CubeMatrix/CubeMatrix.tsx @@ -0,0 +1,11 @@ +import styles from './CubeMatrix.module.scss'; + +export const CubeMatrix = () => { + return ( +
+ {Array.from({ length: 9 }, (_, i) => ( +
+ ))} +
+ ); +}; diff --git a/src/components/Animations/CubeOrbit/CubeOrbit.module.scss b/src/components/Animations/CubeOrbit/CubeOrbit.module.scss new file mode 100644 index 0000000..05c6993 --- /dev/null +++ b/src/components/Animations/CubeOrbit/CubeOrbit.module.scss @@ -0,0 +1,100 @@ +:root { + --animate-cubeorbit-size: 80px; + --animate-cubeorbit-face-color: 0, 221, 255; + --animate-cubeorbit-dark-color: 0, 83, 173; +} + +.animate-cubeorbit-wrapper { + position: absolute; + width: calc(var(--animate-cubeorbit-size) * 4); + height: calc(var(--animate-cubeorbit-size) * 4); + top: 50%; + left: 50%; + margin: calc((var(--animate-cubeorbit-size) * 4) / -2) 0 0 calc((var(--animate-cubeorbit-size) * 4) / -2); + animation: animate-cubeorbit-wrapper 15s linear infinite; +} + +.animate-cubeorbit-cube { + display: block; + width: var(--animate-cubeorbit-size); + height: var(--animate-cubeorbit-size); + position: absolute; + top: 50%; + left: 50%; + margin: calc(var(--animate-cubeorbit-size) / -2) 0 0 calc(var(--animate-cubeorbit-size) / -2); + transform-style: preserve-3d; + animation: animate-cubeorbit-cubes 5s linear infinite; + list-style: none; + padding: 0; +} + +.animate-cubeorbit-face { + position: absolute; + display: block; + width: var(--animate-cubeorbit-size); + height: var(--animate-cubeorbit-size); + background: rgba(var(--animate-cubeorbit-face-color), 0.5); + transform-style: preserve-3d; + + &:nth-child(1) { + animation: animate-cubeorbit-pulse 1s alternate infinite; + transform: translate3d(0, 0, calc(var(--animate-cubeorbit-size) / 2)); + } + + &:nth-child(2) { + animation: animate-cubeorbit-pulse 1s 1s alternate infinite; + transform: rotateY(90deg) translate3d(0, 0, calc(var(--animate-cubeorbit-size) / 2)); + } + + &:nth-child(3) { + animation: animate-cubeorbit-pulse 2s 1s alternate infinite; + transform: translate3d(0, 0, calc(var(--animate-cubeorbit-size) / -2)); + } + + &:nth-child(4) { + animation: animate-cubeorbit-pulse 3s 1s alternate infinite; + transform: rotateY(-90deg) translate3d(0, 0, calc(var(--animate-cubeorbit-size) / 2)); + } + + &:nth-child(5) { + animation: animate-cubeorbit-pulse 4s 1s alternate infinite; + transform: translate3d(0, calc(var(--animate-cubeorbit-size) / -2), 0) rotateX(90deg); + } + + &:nth-child(6) { + animation: animate-cubeorbit-pulse 5s 1s alternate infinite; + transform: translate3d(0, calc(var(--animate-cubeorbit-size) / 2), 0) rotateX(90deg); + } +} + +@keyframes animate-cubeorbit-wrapper { + 100% { + transform: rotate(360deg); + } +} + +@keyframes animate-cubeorbit-cubes { + 0% { + transform: rotateX(45deg) rotateY(0deg); + } + 100% { + transform: rotateX(45deg) rotateY(360deg); + } +} + +@keyframes animate-cubeorbit-pulse { + 0% { + background: rgba(var(--animate-cubeorbit-face-color), 0.5); + } + 75% { + background: rgba(var(--animate-cubeorbit-dark-color), 0.5); + } + 100% { + background: rgba(var(--animate-cubeorbit-face-color), 0.9); + } +} + +:global(body) { + background: #000; + overflow: hidden; +} diff --git a/src/components/Animations/CubeOrbit/CubeOrbit.tsx b/src/components/Animations/CubeOrbit/CubeOrbit.tsx new file mode 100644 index 0000000..133e83e --- /dev/null +++ b/src/components/Animations/CubeOrbit/CubeOrbit.tsx @@ -0,0 +1,35 @@ +import { useEffect, useRef } from 'react'; +import styles from './CubeOrbit.module.scss'; + +export const CubeOrbit = () => { + const wrapperRef = useRef(null); + + useEffect(() => { + const container = wrapperRef.current; + if (!container) return; + + const cubes = container.querySelectorAll(`.${styles['animate-cubeorbit-cube']}`); + const increase = (Math.PI * 2) / cubes.length; + let angle = 0; + + cubes.forEach((cube) => { + const x = 200 * Math.cos(angle) + 200; + const y = 200 * Math.sin(angle) + 200; + (cube as HTMLElement).style.left = `${x}px`; + (cube as HTMLElement).style.top = `${y}px`; + angle += increase; + }); + }, []); + + return ( +
+ {Array.from({ length: 8 }, (_, i) => ( +
    + {Array.from({ length: 6 }, (_, j) => ( +
  • + ))} +
+ ))} +
+ ); +}; diff --git a/src/components/Animations/CubeShift/CubeShift.module.scss b/src/components/Animations/CubeShift/CubeShift.module.scss new file mode 100644 index 0000000..bb9f300 --- /dev/null +++ b/src/components/Animations/CubeShift/CubeShift.module.scss @@ -0,0 +1,65 @@ +$cube-color: #ccc; +$cube-size: 100px; +$animation-duration: 1.2s; + +@keyframes animate-cubeshift-color { + 0%, 100% { + background: $cube-color; + } + 33% { + background: lighten($cube-color, 8%); + } + 66% { + background: lighten($cube-color, 16%); + } +} + +.animate-cubeshift-logo { + position: relative; + height: $cube-size * 2; + width: $cube-size * 1.8; + + &.animate-cubeshift-animate span { + animation: animate-cubeshift-color $animation-duration infinite; + + &:nth-child(2) { + animation-delay: -$animation-duration / 3 * 2; + } + + &:nth-child(3) { + animation-delay: -$animation-duration / 3; + } + } +} + +.animate-cubeshift-cube { + position: absolute; + top: 50%; + left: 50%; + + &:nth-child(2) { + transform: rotate(180deg) scale(0.5); + } + + span { + transform-origin: 0 0; + position: absolute; + height: $cube-size; + width: $cube-size; + + &:nth-child(1) { + transform: rotate(210deg) skewX(-30deg) scaleY(0.864); + background: lighten($cube-color, 16%); + } + + &:nth-child(2) { + transform: rotate(90deg) skewX(-30deg) scaleY(0.864); + background: $cube-color; + } + + &:nth-child(3) { + transform: rotate(-30deg) skewX(-30deg) scaleY(0.864); + background: lighten($cube-color, 8%); + } + } +} diff --git a/src/components/Animations/CubeShift/CubeShift.tsx b/src/components/Animations/CubeShift/CubeShift.tsx new file mode 100644 index 0000000..32c8e31 --- /dev/null +++ b/src/components/Animations/CubeShift/CubeShift.tsx @@ -0,0 +1,18 @@ +import styles from './CubeShift.module.scss'; + +export const CubeShift = () => { + return ( +
+
+ + + +
+
+ + + +
+
+ ); +}; diff --git a/src/components/Animations/CubeSlide/CubeSlide.module.scss b/src/components/Animations/CubeSlide/CubeSlide.module.scss new file mode 100644 index 0000000..ce457d5 --- /dev/null +++ b/src/components/Animations/CubeSlide/CubeSlide.module.scss @@ -0,0 +1,89 @@ +.animate-cubeslide-container { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) rotateX(-30deg) rotateY(-45deg); + transform-style: preserve-3d; + perspective: 2000px; +} + +.animate-cubeslide-holder { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) translate3d(0em, 3em, 1.5em); + transform-style: preserve-3d; + + &:last-child { + transform: rotateY(-90deg) rotateX(90deg) translate3d(0, 3em, 1.5em); + } + + &:first-child { + transform: rotateZ(-90deg) rotateX(-90deg) translate3d(0, 3em, 1.5em); + } + + &:nth-child(1) .animate-cubeslide-box { + background-color: #1FBCD3; + &:before { background-color: #126d7a; } + &:after { background-color: #1894a7; } + } + + &:nth-child(2) .animate-cubeslide-box { + background-color: #CBE2B4; + &:before { background-color: #98c66a; } + &:after { background-color: #b2d48f; } + } + + &:nth-child(3) .animate-cubeslide-box { + background-color: #F6B6CA; + &:before { background-color: #eb5b88; } + &:after { background-color: #f089a9; } + } +} + +.animate-cubeslide-box { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + transform-style: preserve-3d; + animation: animate-cubeslide-box 6s infinite; + width: 3em; + height: 3em; + + &:before, &:after { + position: absolute; + width: 100%; + height: 100%; + content: ""; + } + + &:before { + left: 100%; + bottom: 0; + transform: rotateY(90deg); + transform-origin: 0 50%; + } + + &:after { + left: 0; + bottom: 100%; + transform: rotateX(90deg); + transform-origin: 0 100%; + } +} + +@keyframes animate-cubeslide-box { + 8.33% { transform: translate3d(-50%, -50%, 0) scaleZ(2); } + 16.7% { transform: translate3d(-50%, -50%, -3em) scaleZ(1); } + 25% { transform: translate3d(-50%, -100%, -3em) scaleY(2); } + 33.3% { transform: translate3d(-50%, -150%, -3em) scaleY(1); } + 41.7% { transform: translate3d(-100%, -150%, -3em) scaleX(2); } + 50% { transform: translate3d(-150%, -150%, -3em) scaleX(1); } + 58.3% { transform: translate3d(-150%, -150%, 0) scaleZ(2); } + 66.7% { transform: translate3d(-150%, -150%, 0) scaleZ(1); } + 75% { transform: translate3d(-150%, -100%, 0) scaleY(2); } + 83.3% { transform: translate3d(-150%, -50%, 0) scaleY(1); } + 91.7% { transform: translate3d(-100%, -50%, 0) scaleX(2); } + 100% { transform: translate3d(-50%, -50%, 0) scaleX(1); } +} diff --git a/src/components/Animations/CubeSlide/CubeSlide.tsx b/src/components/Animations/CubeSlide/CubeSlide.tsx new file mode 100644 index 0000000..c98ec11 --- /dev/null +++ b/src/components/Animations/CubeSlide/CubeSlide.tsx @@ -0,0 +1,13 @@ +import styles from './CubeSlide.module.scss'; + +export const CubeSlide = () => { + return ( +
+ {[1, 2, 3].map((index) => ( +
+
+
+ ))} +
+ ); +}; diff --git a/src/components/Animations/CubeStretch/CubeStretch.module.scss b/src/components/Animations/CubeStretch/CubeStretch.module.scss new file mode 100644 index 0000000..f3769dc --- /dev/null +++ b/src/components/Animations/CubeStretch/CubeStretch.module.scss @@ -0,0 +1,120 @@ +$n: 10; +$t: 4.5s; +$dimension: 1.75em; +$c_purple_top: #842C8D; +$c_purple_left: #852C8C; +$c_purple_right: #7A2882; +$c_turquoise_top: #3CC6A9; +$c_turquoise_left: #339E89; +$c_turquoise_right: #39B097; + +.animate-cubestretch-wrapper { + width: 100%; + height: 100%; + overflow: hidden; + position: relative; + transform-style: preserve-3d; +} + +.animate-cubestretch-cube { + position: absolute; + transform-style: preserve-3d; + left: 50%; + transform: rotateX(-35deg) rotateY(-45deg); + + @for $i from 0 through $n - 1 { + &:nth-child(#{$i + 1}) { + top: 26% + ($i * 4); + z-index: $n - $i; + + .animate-cubestretch-vis { + background: $c_purple_left; + animation: animate-cubestretch-p $t ease-in-out infinite; + + &:before { + background: $c_purple_right; + } + + &:after { + background: $c_purple_top; + } + } + } + + @if $i == 0 { + &:nth-child(#{$i + 1}) { + .animate-cubestretch-vis:after { + background: $c_purple_top; + } + } + } + + @if $i % 2 != 0 { + &:nth-child(#{$i + 1}) { + .animate-cubestretch-vis { + background: $c_turquoise_left; + animation: animate-cubestretch-t $t ease-in-out infinite; + + &:before { + background: $c_turquoise_right; + } + + &:after { + background: $c_turquoise_top; + } + } + } + } + } +} + +.animate-cubestretch-vis { + position: absolute; + transform-style: preserve-3d; + transform-origin: right; + + &, &:before, &:after { + width: $dimension; + height: $dimension; + content: ''; + position: absolute; + transform-style: preserve-3d; + } + + &:before { + transform: rotateY(90deg) translate3d($dimension * 0.5, 0, $dimension * 0.5); + filter: brightness(1.3); + } + + &:after { + transform: rotateX(90deg) translate3d(0, $dimension * -0.5, $dimension * 0.5); + filter: brightness(1.6); + background: $c_purple_top; + } +} + +@keyframes animate-cubestretch-t { + 0% { transform: scaleX(1); } + 10% { transform: scaleX(4); } + 20% { transform: scaleX(1) translateX(-5.275em); } + 30% { transform: scaleZ(4) translateX(-5.275em); } + 40% { transform: scaleZ(1) translateX(-5.275em) translateZ(-5.275em); } + 50% { transform: scaleX(4) translateZ(-5.275em); } + 60% { transform: scaleX(1) translateZ(-5.275em); } + 70% { transform: scaleZ(4); } + 80% { transform: scaleZ(1); } + 100% { transform: scaleX(1); } +} + +@keyframes animate-cubestretch-p { + 0% { transform: scaleZ(1); } + 10% { transform: scaleZ(4); } + 20% { transform: scaleZ(1) translateZ(-5.275em); } + 30% { transform: scaleX(4) translateZ(-5.275em); } + 40% { transform: scaleX(1) translateZ(-5.275em) translateX(-5.275em); } + 50% { transform: scaleZ(4) translateX(-5.275em); } + 60% { transform: scaleZ(1) translateX(-5.275em); } + 70% { transform: scaleX(4); } + 80% { transform: scaleX(1); } + 100% { transform: scaleZ(1); } +} diff --git a/src/components/Animations/CubeStretch/CubeStretch.tsx b/src/components/Animations/CubeStretch/CubeStretch.tsx new file mode 100644 index 0000000..1241633 --- /dev/null +++ b/src/components/Animations/CubeStretch/CubeStretch.tsx @@ -0,0 +1,13 @@ +import styles from './CubeStretch.module.scss'; + +export const CubeStretch = () => { + return ( +
+ {Array.from({ length: 10 }, (_, i) => ( +
+
+
+ ))} +
+ ); +}; diff --git a/src/components/Animations/PenroseTriangle/PenroseTriangle.module.scss b/src/components/Animations/PenroseTriangle/PenroseTriangle.module.scss new file mode 100644 index 0000000..83532b1 --- /dev/null +++ b/src/components/Animations/PenroseTriangle/PenroseTriangle.module.scss @@ -0,0 +1,75 @@ +$size: 100px; +$half: $size / 2; + +.animate-penrosetriangle-container { + position: relative; + width: 100%; + min-height: 100%; + overflow: hidden; +} + +.animate-penrosetriangle-platform { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + width: $size * 5; + height: $size * 5; + transform: rotateX(54.75deg) rotateZ(45deg) translateX($size * 2); + transform-style: preserve-3d; +} + +.animate-penrosetriangle-cube { + position: absolute; + width: $size; + height: $size; + background: hsl(200, 40%, 40%); + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3); + right: 0; + bottom: 0; + transform: translateZ($size); + transform-style: preserve-3d; + + &:before, + &:after { + content: ''; + position: absolute; + width: 100%; + height: 100%; + background: inherit; + box-shadow: inherit; + } + + &:before { + transform-origin: 100% 0; + transform: rotateY(-90deg); + } + + &:after { + transform-origin: 100% 0; + transform: rotateX(-90deg) translateZ($size); + } + + // Cube positions + &:nth-child(2) { right: $size * 1.5; } + &:nth-child(3) { right: $size * 3; } + &:nth-child(4) { right: $size * 4.5; } + &:nth-child(5) { transform: translateZ($size * 2.5); } + &:nth-child(6) { transform: translateZ($size * 4); } + &:nth-child(7) { transform: translateZ($size * 5.5); } + &:nth-child(8) { + right: $size * 4.5; + bottom: $size * 1.5; + } + &:nth-child(9) { + right: $size * 4.5; + bottom: $size * 3; + } + &:nth-child(10) { + bottom: -$size * 1.5; + transform: translateZ($size * 5.5); + &:after { opacity: 0; } + } +} diff --git a/src/components/Animations/PenroseTriangle/PenroseTriangle.tsx b/src/components/Animations/PenroseTriangle/PenroseTriangle.tsx new file mode 100644 index 0000000..5f56b9d --- /dev/null +++ b/src/components/Animations/PenroseTriangle/PenroseTriangle.tsx @@ -0,0 +1,13 @@ +import styles from './PenroseTriangle.module.scss'; + +export const PenroseTriangle = () => { + return ( +
+
+ {Array.from({ length: 10 }, (_, i) => ( +
+ ))} +
+
+ ); +}; diff --git a/src/components/Animations/SwitchingCubes/SwitchingCubes.module.scss b/src/components/Animations/SwitchingCubes/SwitchingCubes.module.scss new file mode 100644 index 0000000..9a8bd6a --- /dev/null +++ b/src/components/Animations/SwitchingCubes/SwitchingCubes.module.scss @@ -0,0 +1,102 @@ +:root { + --animate-switchingcubes-size: 8em; + --animate-switchingcubes-border: 0.125em; + --animate-switchingcubes-inset: 1.75em; +} + +.animate-switchingcubes-assembly { + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + transform: rotateX(-45deg) rotateY(-135deg); + + &, * { + box-sizing: border-box; + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + } +} + +.animate-switchingcubes-positioner { + @for $i from 0 through 5 { + $j: $i % 3; + $k: floor($i / 3); + + &:nth-child(#{$i + 1}) { + transform: + if($j != 2, + rotateY(($i + 1 - $k) * 90deg), + rotateX(pow(-1, $i + 1) * 90deg) + ) + translateZ(var(--animate-switchingcubes-size)) + if($j == 0, (), rotate(($i - $k) * 90deg)); + } + } + + &:last-child { + transform: scale3d(0.99, 0.99, 0.99); + -webkit-transform: none; + } +} + +.animate-switchingcubes-cube { + .animate-switchingcubes-positioner:not(:last-child) & { + transform-origin: calc(var(--animate-switchingcubes-size) / 2) 0 calc(var(--animate-switchingcubes-size) * -0.5); + animation: animate-switchingcubes-rotate 1s ease-in-out infinite; + } +} + +.animate-switchingcubes-face { + margin: calc(var(--animate-switchingcubes-size) * -0.5); + border: solid var(--animate-switchingcubes-border); + width: var(--animate-switchingcubes-size); + height: var(--animate-switchingcubes-size); + box-shadow: + inset 0 0 0 var(--animate-switchingcubes-inset) grey, + inset 0 0 0 calc(2 * var(--animate-switchingcubes-border) + var(--animate-switchingcubes-inset)); + + @for $i from 0 through 5 { + &:nth-child(#{$i + 1}) { + transform: if($i < 4, + rotateY($i * 90deg), + rotateX(pow(-1, $i) * 90deg) + ) translateZ(calc(var(--animate-switchingcubes-size) * 0.5)); + } + } +} + +.animate-switchingcubes-inner { + --inner-width: calc(var(--animate-switchingcubes-size) - 2 * (2 * var(--animate-switchingcubes-border) + var(--animate-switchingcubes-inset))); + --inner-height: calc(2 * var(--animate-switchingcubes-border) + var(--animate-switchingcubes-inset)); + + margin: calc(var(--inner-height) * -0.5) calc(var(--inner-width) * -0.5); + border: solid var(--animate-switchingcubes-border); + width: var(--inner-width); + height: var(--inner-height); + backface-visibility: hidden; + background: silver; + + @for $i from 0 through 3 { + &:nth-child(#{$i + 1}) { + transform: rotate($i * 90deg) rotateX(-90deg) translate3d( + 0, + calc(var(--inner-height) * 0.5), + calc(var(--inner-height) * -1) + ); + } + } +} + +@keyframes animate-switchingcubes-rotate { + to { + transform: rotateY(180deg); + } +} + +:global(body) { + background: black; + overflow: hidden; +} diff --git a/src/components/Animations/SwitchingCubes/SwitchingCubes.tsx b/src/components/Animations/SwitchingCubes/SwitchingCubes.tsx new file mode 100644 index 0000000..d10f173 --- /dev/null +++ b/src/components/Animations/SwitchingCubes/SwitchingCubes.tsx @@ -0,0 +1,29 @@ +import styles from './SwitchingCubes.module.scss'; + +const CubeFace = () => ( +
+ {Array.from({ length: 4 }, (_, i) => ( +
+ ))} +
+); + +const Cube = () => ( +
+ {Array.from({ length: 6 }, (_, i) => ( + + ))} +
+); + +export const SwitchingCubes = () => { + return ( +
+ {Array.from({ length: 7 }, (_, i) => ( +
+ +
+ ))} +
+ ); +}; diff --git a/src/components/Animations/TricklingCubes/TricklingCubes.module.scss b/src/components/Animations/TricklingCubes/TricklingCubes.module.scss new file mode 100644 index 0000000..6ca06aa --- /dev/null +++ b/src/components/Animations/TricklingCubes/TricklingCubes.module.scss @@ -0,0 +1,118 @@ +:root { + --animate-tricklingcubes-size: 2em; + --animate-tricklingcubes-duration: 0.5s; + --animate-tricklingcubes-distance: 11.2em; + --animate-tricklingcubes-scale: 0.75; + --animate-tricklingcubes-color1: #222; + --animate-tricklingcubes-color2: #401a2a; + --animate-tricklingcubes-color3: #741a38; + --animate-tricklingcubes-color4: #9b123c; + --animate-tricklingcubes-color5: #c10a40; +} + +.animate-tricklingcubes-assembly { + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + transform: rotateX(-35deg) rotateY(-45deg); +} + +.animate-tricklingcubes-assembly > *, +[class*='animate-tricklingcubes'] { + position: absolute; + top: 50%; + left: 50%; + transform-style: preserve-3d; + background: currentColor; +} + +@for $i from 0 through 4 { + $j: $i + 1; + + .animate-tricklingcubes-assembly > :nth-child(5n + #{$j}) { + --offset: calc((2 - #{$i}) * 2.5 * var(--animate-tricklingcubes-size)); + top: calc(50% + var(--offset)); + color: var(--animate-tricklingcubes-color#{$j}); + } + + .animate-tricklingcubes-switch-out:nth-child(5n + #{$j}) { + &, * { + animation-delay: calc(#{-$i} * var(--animate-tricklingcubes-duration) / 5); + } + } +} + +[class*='animate-tricklingcubes-switch'] { + animation: animate-tricklingcubes-switch calc(var(--animate-tricklingcubes-duration) * 4) steps(4) infinite; +} + +.animate-tricklingcubes-switch-out { + animation-direction: reverse; + animation-timing-function: steps(4, start); +} + +.animate-tricklingcubes-mover { + animation: animate-tricklingcubes-move var(--animate-tricklingcubes-duration) ease-in-out infinite; +} + +.animate-tricklingcubes-cube { + margin: calc(var(--animate-tricklingcubes-size) * -0.5); + width: var(--animate-tricklingcubes-size); + height: var(--animate-tricklingcubes-size); + transform-style: preserve-3d; + transform: translateZ(calc(var(--animate-tricklingcubes-size) * 0.5)); + + &:before, + &:after { + content: ''; + position: absolute; + width: 100%; + height: 100%; + background: inherit; + transform-style: preserve-3d; + } + + &:before { + top: 0; + left: 100%; + transform-origin: 0 50%; + transform: rotateY(90deg); + filter: brightness(1.15); + } + + &:after { + top: -100%; + left: 0; + transform-origin: 50% 100%; + transform: rotateX(90deg); + filter: brightness(1.3); + } +} + +.animate-tricklingcubes-switch-in, +.animate-tricklingcubes-switch-out { + transform-style: preserve-3d; +} + +@keyframes animate-tricklingcubes-switch { + to { + transform: rotateY(360deg); + } +} + +@keyframes animate-tricklingcubes-move { + 75% { + transform: translate(calc(0.75 * var(--animate-tricklingcubes-distance))) + scale3d(1, 1, 1); + } + 100% { + transform: translate(var(--animate-tricklingcubes-distance)) + scale3d(0, 0, 0); + } +} + +:global(body) { + background: #444; + overflow: hidden; +} diff --git a/src/components/Animations/TricklingCubes/TricklingCubes.tsx b/src/components/Animations/TricklingCubes/TricklingCubes.tsx new file mode 100644 index 0000000..2dfbfed --- /dev/null +++ b/src/components/Animations/TricklingCubes/TricklingCubes.tsx @@ -0,0 +1,28 @@ +import styles from './TricklingCubes.module.scss'; + +const Cube = () => ( +
+); + +const SwitchingCube = () => ( +
+
+
+ +
+
+
+); + +export const TricklingCubes = () => { + return ( +
+ {Array.from({ length: 5 }, (_, i) => ( + + ))} + {Array.from({ length: 5 }, (_, i) => ( + + ))} +
+ ); +}; diff --git a/src/components/Animations/TunnelBall/TunnelBall.module.scss b/src/components/Animations/TunnelBall/TunnelBall.module.scss new file mode 100644 index 0000000..0aaef0d --- /dev/null +++ b/src/components/Animations/TunnelBall/TunnelBall.module.scss @@ -0,0 +1,220 @@ +@property --animate-tunnelball-angle { + syntax: ""; + inherits: true; + initial-value: 0deg; +} + +@property --animate-tunnelball-circle-diameter { + syntax: ""; + inherits: true; + initial-value: 0; +} + +$walls: 6; +$half: $walls / 2; +$surfaces: $walls * 2; + +:root { + --animate-tunnelball-c1: #6eccee; + --animate-tunnelball-c2: #ffdc99; + --animate-tunnelball-c3: #e3a4d0; + --animate-tunnelball-c4: #d455ff; + --animate-tunnelball-duration: 2.8s; + --animate-tunnelball-border: 0.3vmin; + --animate-tunnelball-glow: drop-shadow(0 0 3vmin rgba(255, 255, 255, 0.19)); + --animate-tunnelball-hole-y: 20%; + --animate-tunnelball-hole-radius: 11vmin; + --animate-tunnelball-surface-offset: calc(360deg / 24); +} + +.animate-tunnelball-container { + position: relative; + width: 25vmin; + aspect-ratio: 1/1.2; + --animate-tunnelball-angle: 30deg; + animation: animate-tunnelball-rotate var(--animate-tunnelball-duration) linear infinite; + transform-style: preserve-3d; + transform: rotateX(-45deg) rotateY(45deg); +} + +.animate-tunnelball-wall { + position: absolute; + inset: 0; + --wall-gap: 5vmin; + filter: var(--animate-tunnelball-glow); + + @for $i from 1 through $walls { + $index: $i - 3; + $surfaceIndex: ($i - 1) * 2 + 1; + + &:nth-of-type(#{$i}) { + transform: translateZ(calc(var(--wall-gap) * #{$index - 1})); + --index: #{$i}; + + .animate-tunnelball-surface { + --index: #{$surfaceIndex}; + + &:nth-child(2) { + --index: #{$surfaceIndex + 1}; + } + } + } + } +} + +.animate-tunnelball-surface { + position: absolute; + inset: 0; + --angle-offset: calc(var(--index) * var(--animate-tunnelball-surface-offset)); + --circle-diameter: calc( + var(--animate-tunnelball-hole-radius) * cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset))) + ); + -webkit-mask: radial-gradient( + circle at 50% var(--animate-tunnelball-hole-y), + transparent var(--circle-diameter), + black var(--circle-diameter) + ); + mask: radial-gradient( + circle at 50% var(--animate-tunnelball-hole-y), + transparent var(--circle-diameter), + black var(--circle-diameter) + ); + background: radial-gradient( + circle at 50% var(--animate-tunnelball-hole-y), + var(--animate-tunnelball-c4) calc(var(--circle-diameter) + var(--animate-tunnelball-border)), + transparent var(--circle-diameter) + ), + linear-gradient( + 45deg, + var(--animate-tunnelball-c1), + var(--animate-tunnelball-c3), + var(--animate-tunnelball-c2), + var(--animate-tunnelball-c1), + var(--animate-tunnelball-c4), + var(--animate-tunnelball-c3), + var(--animate-tunnelball-c2) + ); + + &:nth-child(2) { + transform: translate(2vmin, 2.85vmin); + } +} + +.animate-tunnelball-left { + position: absolute; + transform: skewY(55deg) translateY(1.45vmin); + inset: 0; + width: 2.25vmin; + background: linear-gradient( + to top, + var(--animate-tunnelball-c1), + var(--animate-tunnelball-c3), + var(--animate-tunnelball-c2), + var(--animate-tunnelball-c1) + ); +} + +.animate-tunnelball-top { + position: absolute; + transform: skewX(36deg) translateX(1vmin); + inset: 0; + height: 3vmin; + background: linear-gradient( + to right, + var(--animate-tunnelball-c1), + var(--animate-tunnelball-c3), + var(--animate-tunnelball-c2), + var(--animate-tunnelball-c1) + ); + --angle-offset: calc(var(--index) * var(--animate-tunnelball-surface-offset)); + --circle-diameter: calc( + var(--animate-tunnelball-hole-radius) * cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset))) + ); + -webkit-mask: radial-gradient( + calc(var(--circle-diameter) * 0.86) at 50% + calc(60% / cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset)))), + transparent var(--circle-diameter), + black var(--circle-diameter) + ); + mask: radial-gradient( + calc(var(--circle-diameter) * 0.86) at 50% + calc(60% / cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset)))), + transparent var(--circle-diameter), + black var(--circle-diameter) + ); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-size: 100% 100%; + mask-size: 100% 100%; + -webkit-mask-position: 0 0; + mask-position: 0 0; + z-index: 1; + + .animate-tunnelball-wall:nth-last-child(-n+2) & { + --circle-diameter: calc( + var(--animate-tunnelball-hole-radius) * 1.15 * cos(calc(var(--animate-tunnelball-angle) + var(--angle-offset) - 15deg)) + ); + } +} + +.animate-tunnelball-ball-container { + z-index: 0; + display: grid; + place-items: center; + position: absolute; + inset: 0; + transform: translateZ(-35vmin); + animation: animate-tunnelball-ball var(--animate-tunnelball-duration) linear infinite; +} + +.animate-tunnelball-ball { + width: 21vmin; + aspect-ratio: 1; + border-radius: 50%; + filter: var(--animate-tunnelball-glow); + background: radial-gradient( + 10.75vmin 10.75vmin at center, + black 10vmin, + transparent 10vmin + ), + conic-gradient( + var(--animate-tunnelball-c1), + var(--animate-tunnelball-c3), + var(--animate-tunnelball-c2), + var(--animate-tunnelball-c4), + var(--animate-tunnelball-c3), + var(--animate-tunnelball-c1), + var(--animate-tunnelball-c2), + var(--animate-tunnelball-c1) + ); + box-shadow: 0 0 5vmin rgba(255, 255, 255, 0.08); + transform: rotateX(45deg) rotateY(45deg) translateY(-10vmin); +} + +@keyframes animate-tunnelball-rotate { + from { + --animate-tunnelball-angle: 360deg; + } + to { + --animate-tunnelball-angle: 0deg; + } +} + +@keyframes animate-tunnelball-ball { + from { + transform: translateZ(-25vmin); + opacity: 0; + } + 10% { + transform: translateZ(-15vmin); + opacity: 1; + } + 80% { + opacity: 1; + transform: translateZ(30vmin); + } + to { + opacity: 0; + transform: translateZ(40vmin); + } +} diff --git a/src/components/Animations/TunnelBall/TunnelBall.tsx b/src/components/Animations/TunnelBall/TunnelBall.tsx new file mode 100644 index 0000000..1f6662f --- /dev/null +++ b/src/components/Animations/TunnelBall/TunnelBall.tsx @@ -0,0 +1,21 @@ +import styles from './TunnelBall.module.scss'; + +export const TunnelBall = () => { + return ( +
+
+ {Array.from({ length: 6 }, (_, i) => ( +
+
+
+
+
+
+ ))} +
+
+
+
+
+ ); +}; diff --git a/src/components/Animations/index.ts b/src/components/Animations/index.ts new file mode 100644 index 0000000..72691a2 --- /dev/null +++ b/src/components/Animations/index.ts @@ -0,0 +1,16 @@ +export * from "./CubeOrbit/CubeOrbit"; +export * from "./CubeDots/CubeDots"; +export * from "./CubeGradient/CubeGradient"; +export * from "./CubeAssembly/CubeAssembly"; +export * from "./CubeMatrix/CubeMatrix"; +export * from './CubeStretch/CubeStretch'; +export * from './CubeFrame/CubeFrame'; +export * from './CubeSlide/CubeSlide'; +export * from './CubeExpand/CubeExpand'; +export * from './CubeGrid/CubeGrid'; +export * from './BackrollCubes/BackrollCubes'; +export * from './CubeShift/CubeShift'; +export * from './TunnelBall/TunnelBall'; +export * from './PenroseTriangle/PenroseTriangle'; +export * from './SwitchingCubes/SwitchingCubes'; +export * from './TricklingCubes/TricklingCubes'; diff --git a/src/components/index.ts b/src/components/index.ts index 25280ee..a73a929 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -11,5 +11,6 @@ export * from "./FixedBottomGradient"; export * from "./RenderOnViewport"; export * from "./SpinnerLoading"; export * from "./AssetsVideoLoader"; +export * from "./Animations"; export type { ModalRef } from "./Modal"; diff --git a/src/pages/animations.astro b/src/pages/animations.astro new file mode 100644 index 0000000..30ad455 --- /dev/null +++ b/src/pages/animations.astro @@ -0,0 +1,147 @@ +--- +import MainLayout from "@src/layouts/MainLayout.astro"; +import { CubeDots, CubeOrbit, CubeGradient, CubeAssembly, CubeMatrix, CubeStretch, CubeFrame, CubeSlide, CubeExpand, CubeGrid, BackrollCubes, CubeShift, PenroseTriangle, SwitchingCubes, TunnelBall, TricklingCubes } from '@src/components/Animations'; +--- + + +
+
+

Animation Showcase

+ +
+
+

Cube Dots

+
+ +
+
+ +
+

Cube Orbit

+
+ +
+
+ +
+

Cube Gradient

+
+ +
+
+ +
+

Cube Assembly

+
+ +
+
+ +
+

Cube Matrix

+
+ +
+
+ +
+

Cube Stretch

+
+ +
+
+ +
+

Cube Frame

+
+ +
+
+ +
+

Cube Slide

+
+ +
+
+ +
+

Cube Expand

+
+ +
+
+ +
+

Cube Grid

+
+ +
+
+ +
+

Backroll Cubes

+
+ +
+
+ +
+

Cube Shift

+
+ +
+
+ +
+

Penrose Triangle

+
+ +
+
+ +
+

Tunnel Ball

+
+ +
+
+ +
+

Switching Cubes

+
+ +
+
+ +
+

Trickling Cubes

+
+ +
+
+
+
+
+
+ + diff --git a/src/sections/home/IntroSection.tsx b/src/sections/home/IntroSection.tsx index 6162d1f..bf1ef1f 100644 --- a/src/sections/home/IntroSection.tsx +++ b/src/sections/home/IntroSection.tsx @@ -1,13 +1,11 @@ -import { AssetsVideoLoader, Button, SectionTitle } from "@src/components"; +import { Button, SectionTitle } from "@src/components"; +import { CubeMatrix } from "@src/components/Animations"; import clsx from "clsx"; export const IntroSection = () => { return (
- +