chore: replace glb files and lottie with optimized variants (#19503)

As per title. Replaced .json file with .lottie file - it is 10x smaller.

Illustrations folder went from 150mb to 5.9mb with compressed variants
of 3D models. The DracoLoader, GLTFLoader and some other logic logic is
repetitive across files, but Thomas is working on the same files at the
moment, so not touching too much to avoid conflicts with his PR. Once
he's done styling, we can extract shared logic into helpers.
This commit is contained in:
Abdullah.
2026-04-09 19:27:13 +05:00
committed by GitHub
parent caac791421
commit 5116002ca2
63 changed files with 264 additions and 89 deletions
+1 -1
View File
@@ -13,6 +13,7 @@
"@gsap/react": "2.1.2",
"@linaria/core": "^7.0.0",
"@linaria/react": "^7.0.1",
"@lottiefiles/dotlottie-react": "^0.18.10",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.5.0",
"@tabler/icons-react": "^3.41.1",
@@ -20,7 +21,6 @@
"axios": "^1.14.0",
"gray-matter": "^4.0.3",
"gsap": "^3.14.2",
"lottie-react": "^2.4.1",
"next": "16.1.7",
"next-with-linaria": "^1.3.0",
"react": "19.2.3",
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

@@ -4,6 +4,7 @@ import { theme } from '@/theme';
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/common/faq/faq.glb';
@@ -178,7 +179,14 @@ export function FaqBackground() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -241,6 +249,7 @@ export function FaqBackground() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/common/footer/footer.glb';
@@ -188,7 +189,14 @@ export function FooterBackground() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -329,6 +337,7 @@ export function FooterBackground() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/home/helped/money.glb';
@@ -191,7 +192,14 @@ export function Money() {
const cameraWorldPosition = new THREE.Vector3();
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -357,6 +365,7 @@ export function Money() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/home/helped/spaceship.glb';
@@ -191,7 +192,14 @@ export function Spaceship() {
const cameraWorldPosition = new THREE.Vector3();
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -357,6 +365,7 @@ export function Spaceship() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/home/helped/target.glb';
@@ -191,7 +192,14 @@ export function Target() {
const cameraWorldPosition = new THREE.Vector3();
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -357,6 +365,7 @@ export function Target() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -4,6 +4,7 @@ import { theme } from '@/theme';
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/product/hero/hero.glb';
@@ -179,7 +180,14 @@ export function Product() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -320,6 +328,7 @@ export function Product() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/why-twenty/hero/hero.glb';
@@ -221,7 +222,14 @@ export function WhyTwenty() {
let targetRotationX = 0;
let targetRotationY = 0;
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(GLB_URL, (gltf) => {
if (cancelled) {
disposeObjectSubtree(gltf.scene);
@@ -315,6 +323,7 @@ export function WhyTwenty() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
}
@@ -2,6 +2,7 @@
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/pricing/Price/organization.glb';
@@ -171,7 +172,14 @@ export function Organization() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -324,6 +332,7 @@ export function Organization() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -2,6 +2,7 @@
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/pricing/Price/pro.glb';
@@ -171,7 +172,14 @@ export function Pro() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -324,6 +332,7 @@ export function Pro() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -4,6 +4,7 @@ import { theme } from '@/theme';
import { styled } from '@linaria/react';
import { useLayoutEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/why-twenty/quote/quote.glb';
@@ -189,7 +190,14 @@ export function Quotes() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -329,6 +337,7 @@ export function Quotes() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -4,6 +4,7 @@ import { theme } from '@/theme';
import { styled } from '@linaria/react';
import { useLayoutEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/home/testimonials/hourglass.glb';
@@ -188,7 +189,14 @@ export function Hourglass() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -332,6 +340,7 @@ export function Hourglass() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -4,6 +4,7 @@ import { theme } from '@/theme';
import { styled } from '@linaria/react';
import { useLayoutEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/partner/testimonials/quote.glb';
@@ -188,7 +189,14 @@ export function Partner() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -328,6 +336,7 @@ export function Partner() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/partner/three-cards/connect.glb';
@@ -182,7 +183,14 @@ export function Connect() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function Connect() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/home/three-cards/diamond.glb';
@@ -182,7 +183,14 @@ export function Diamond() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function Diamond() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/product/three-cards/eye.glb';
@@ -182,7 +183,14 @@ export function Eye() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function Eye() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/home/three-cards/flash.glb';
@@ -182,7 +183,14 @@ export function Flash() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function Flash() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/partner/three-cards/grow.glb';
@@ -182,7 +183,14 @@ export function Grow() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function Grow() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/home/three-cards/lock.glb';
@@ -182,7 +183,14 @@ export function Lock() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function Lock() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/partner/three-cards/programming.glb';
@@ -182,7 +183,14 @@ export function Programming() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function Programming() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/product/three-cards/single-screen.glb';
@@ -182,7 +183,14 @@ export function SingleScreen() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function SingleScreen() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -3,6 +3,7 @@
import { styled } from '@linaria/react';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/product/three-cards/speed.glb';
@@ -182,7 +183,14 @@ export function Speed() {
const clock = new THREE.Clock();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(
GLB_URL,
(gltf) => {
@@ -346,6 +354,7 @@ export function Speed() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
@@ -4,6 +4,7 @@ import { theme } from '@/theme';
import { styled } from '@linaria/react';
import { useLayoutEffect, useRef } from 'react';
import * as THREE from 'three';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const GLB_URL = '/illustrations/why-twenty/stepper/logo.glb';
@@ -264,7 +265,14 @@ export function Logo() {
syncResolutionUniforms();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
'https://www.gstatic.com/draco/versioned/decoders/1.5.6/',
);
const loader = new GLTFLoader();
loader.setDRACOLoader(dracoLoader);
loader.load(GLB_URL, (gltf) => {
if (cancelled) {
disposeObjectSubtree(gltf.scene);
@@ -353,6 +361,7 @@ export function Logo() {
window.cancelAnimationFrame(animationFrameId);
disposeObjectSubtree(scene);
renderer.dispose();
dracoLoader.dispose();
if (canvas.parentNode === container) {
container.removeChild(canvas);
}
@@ -1,12 +1,19 @@
'use client';
import { styled } from '@linaria/react';
import { DotLottieReact, type DotLottie } from '@lottiefiles/dotlottie-react';
import {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
import { scrollProgressToHomeStepperLottieFrame } from '@/sections/HomeStepper/utils/home-stepper-lottie-frame-map';
import { theme } from '@/theme';
import { styled } from '@linaria/react';
import Lottie, { type LottieRefCurrentProps } from 'lottie-react';
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
export const HOME_STEPPER_LOTTIE_SRC = '/images/home/stepper/lottie.json';
export const HOME_STEPPER_LOTTIE_SRC = '/lottie/stepper/stepper.lottie';
const LottieSlot = styled.div`
box-sizing: border-box;
@@ -17,100 +24,72 @@ const LottieSlot = styled.div`
width: 100%;
`;
type LottieAnimationData = {
ip?: number;
op?: number;
} & Record<string, unknown>;
type StepperLottieProps = {
scrollProgress: number;
};
function lottieTotalFrameCount(animationData: LottieAnimationData): number {
const op = typeof animationData.op === 'number' ? animationData.op : 0;
const ip = typeof animationData.ip === 'number' ? animationData.ip : 0;
const rounded = Math.round(op - ip);
return rounded >= 1 ? rounded : 0;
}
function applyScrollToLottie(
lottieApi: LottieRefCurrentProps | null,
function applyScrollToDotLottie(
dotLottie: DotLottie | null,
scrollProgress: number,
totalFrames: number,
) {
if (!lottieApi?.animationLoaded || totalFrames <= 0) {
if (!dotLottie?.isLoaded || totalFrames <= 0) {
return;
}
const frame = scrollProgressToHomeStepperLottieFrame(
scrollProgress,
totalFrames,
);
lottieApi.goToAndStop(frame, true);
dotLottie.setFrame(frame);
}
export function StepperLottie({ scrollProgress }: StepperLottieProps) {
const [animationData, setAnimationData] =
useState<LottieAnimationData | null>(null);
const [player, setPlayer] = useState<DotLottie | null>(null);
const [totalFrames, setTotalFrames] = useState(0);
const lottieRef = useRef<LottieRefCurrentProps | null>(null);
const scrollProgressRef = useRef(scrollProgress);
scrollProgressRef.current = scrollProgress;
const totalFramesRef = useRef(totalFrames);
totalFramesRef.current = totalFrames;
useEffect(() => {
const controller = new AbortController();
fetch(HOME_STEPPER_LOTTIE_SRC, { signal: controller.signal })
.then((response) => {
if (!response.ok) {
throw new Error(`Lottie fetch failed: ${response.status}`);
}
return response.json() as Promise<LottieAnimationData>;
})
.then((data) => {
setAnimationData(data);
setTotalFrames(lottieTotalFrameCount(data));
})
.catch((error: unknown) => {
if (error instanceof Error && error.name === 'AbortError') {
return;
}
setAnimationData(null);
});
return () => {
controller.abort();
};
const dotLottieRefCallback = useCallback((instance: DotLottie | null) => {
setPlayer(instance);
}, []);
useLayoutEffect(() => {
if (!animationData) {
useEffect(() => {
if (!player) {
setTotalFrames(0);
return;
}
applyScrollToLottie(lottieRef.current, scrollProgress, totalFrames);
}, [animationData, scrollProgress, totalFrames]);
if (!animationData) {
return <LottieSlot aria-hidden />;
}
const onReady = () => {
const frames = player.totalFrames;
setTotalFrames(frames);
applyScrollToDotLottie(player, scrollProgressRef.current, frames);
};
if (player.isLoaded) {
onReady();
return;
}
player.addEventListener('load', onReady);
return () => {
player.removeEventListener('load', onReady);
};
}, [player]);
useLayoutEffect(() => {
applyScrollToDotLottie(player, scrollProgress, totalFrames);
}, [player, scrollProgress, totalFrames]);
return (
<LottieSlot aria-hidden>
<Lottie
animationData={animationData}
<DotLottieReact
autoplay={false}
dotLottieRefCallback={dotLottieRefCallback}
layout={{ align: [0.5, 0.5], fit: 'contain' }}
loop={false}
lottieRef={lottieRef}
src={HOME_STEPPER_LOTTIE_SRC}
style={{ height: '100%', maxWidth: '100%', width: '100%' }}
onDOMLoaded={() => {
lottieRef.current?.setSubframe(true);
applyScrollToLottie(
lottieRef.current,
scrollProgressRef.current,
totalFramesRef.current,
);
}}
useFrameInterpolation
/>
</LottieSlot>
);
+19 -20
View File
@@ -11200,6 +11200,24 @@ __metadata:
languageName: node
linkType: hard
"@lottiefiles/dotlottie-react@npm:^0.18.10":
version: 0.18.10
resolution: "@lottiefiles/dotlottie-react@npm:0.18.10"
dependencies:
"@lottiefiles/dotlottie-web": "npm:0.70.0"
peerDependencies:
react: ^17 || ^18 || ^19
checksum: 10c0/5cd7350dbe44717bb920dd6534a9826a801128f396853c04371b05b0adb34a2a8b62b33f5c44f50597f5f32919f47ba22c24f9ee0276cc67b9feb7d6db7a15c9
languageName: node
linkType: hard
"@lottiefiles/dotlottie-web@npm:0.70.0":
version: 0.70.0
resolution: "@lottiefiles/dotlottie-web@npm:0.70.0"
checksum: 10c0/814df634325393c376d05541f4c3e1e92b383c16f9673e443c671fbaef5414fa9da0d4bcfc1453d6ffe2390c82f79cf68e04785a66ba75a030d3d9d3cd799aa2
languageName: node
linkType: hard
"@lukeed/csprng@npm:^1.0.0":
version: 1.1.0
resolution: "@lukeed/csprng@npm:1.1.0"
@@ -46887,25 +46905,6 @@ __metadata:
languageName: node
linkType: hard
"lottie-react@npm:^2.4.1":
version: 2.4.1
resolution: "lottie-react@npm:2.4.1"
dependencies:
lottie-web: "npm:^5.10.2"
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
checksum: 10c0/7f6a0a2cf2af25deabe7b4d4c9917e47d12016673613c59dcdd8ef52e10e914dd0c1f71d4a275d56aaba03fe9d85b333087f7c8d40a8f967ece4a0444c7a3cdf
languageName: node
linkType: hard
"lottie-web@npm:^5.10.2":
version: 5.13.0
resolution: "lottie-web@npm:5.13.0"
checksum: 10c0/b463ad462169df3f7e04b7f3716274e269842554f1d72b8303ca7245628f2512982a59ae31698b3d57fbcda886228ae160fcb2aa518c6cecd8a95974db2458ee
languageName: node
linkType: hard
"loupe@npm:^3.1.0":
version: 3.2.0
resolution: "loupe@npm:3.2.0"
@@ -60905,6 +60904,7 @@ __metadata:
"@gsap/react": "npm:2.1.2"
"@linaria/core": "npm:^7.0.0"
"@linaria/react": "npm:^7.0.1"
"@lottiefiles/dotlottie-react": "npm:^0.18.10"
"@react-three/drei": "npm:^10.7.7"
"@react-three/fiber": "npm:^9.5.0"
"@tabler/icons-react": "npm:^3.41.1"
@@ -60917,7 +60917,6 @@ __metadata:
babel-plugin-react-compiler: "npm:1.0.0"
gray-matter: "npm:^4.0.3"
gsap: "npm:^3.14.2"
lottie-react: "npm:^2.4.1"
next: "npm:16.1.7"
next-with-linaria: "npm:^1.3.0"
react: "npm:19.2.3"