utm-scroll-scene 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -0
- package/dist/index.cjs +981 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +944 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/public/3d-models/assets/drone/license.txt +11 -0
- package/public/3d-models/assets/drone/scene.bin +0 -0
- package/public/3d-models/assets/drone/scene.gltf +11373 -0
- package/public/3d-models/assets/drone/textures/CARBONE_baseColor.jpeg +0 -0
- package/public/3d-models/assets/smart_box/license.txt +11 -0
- package/public/3d-models/assets/smart_box/scene.bin +0 -0
- package/public/3d-models/assets/smart_box/scene.gltf +386 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/styles/zipline-scroll-scene.css +242 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.tsx
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ScrollScene: () => ScrollScene
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// components/ScrollScene.tsx
|
|
38
|
+
var import_react6 = require("react");
|
|
39
|
+
var import_gsap2 = __toESM(require("gsap"), 1);
|
|
40
|
+
|
|
41
|
+
// components/DroneScene.tsx
|
|
42
|
+
var import_react = require("react");
|
|
43
|
+
|
|
44
|
+
// lib/three/setup.ts
|
|
45
|
+
var THREE2 = __toESM(require("three"), 1);
|
|
46
|
+
var import_GLTFLoader = require("three/addons/loaders/GLTFLoader.js");
|
|
47
|
+
var import_RoundedBoxGeometry = require("three/addons/geometries/RoundedBoxGeometry.js");
|
|
48
|
+
|
|
49
|
+
// lib/three/path.ts
|
|
50
|
+
var THREE = __toESM(require("three"), 1);
|
|
51
|
+
var launch0 = new THREE.Vector3(1.92, 0.35, 1.48);
|
|
52
|
+
var launch1 = new THREE.Vector3(1.75, 0.42, 1.22);
|
|
53
|
+
var launch2 = new THREE.Vector3(1.42, 0.5, 1);
|
|
54
|
+
var launch3 = new THREE.Vector3(1, 0.58, 0.7);
|
|
55
|
+
var flight0 = new THREE.Vector3(1, 0.58, 0.7);
|
|
56
|
+
var flight1 = new THREE.Vector3(0.4, 1.5, -0.4);
|
|
57
|
+
var flight2 = new THREE.Vector3(0, 1.75, -1.8);
|
|
58
|
+
var flight3 = new THREE.Vector3(0.65, 0.5, 0.1);
|
|
59
|
+
var flight2_0 = new THREE.Vector3(0.65, 0.5, 0.1);
|
|
60
|
+
var flight2_1 = new THREE.Vector3(0.4, 1.5, -1.5);
|
|
61
|
+
var flight2_2 = new THREE.Vector3(0.1, 2.2, -4.5);
|
|
62
|
+
var flight2_3 = new THREE.Vector3(-0.05, 1.5, -3.5);
|
|
63
|
+
var flight2_4 = new THREE.Vector3(-0.08, 1, -3.12);
|
|
64
|
+
var exit0 = new THREE.Vector3(-0.08, 0.35, -3.12);
|
|
65
|
+
var exit1 = new THREE.Vector3(-0.08, 1.3, -4.5);
|
|
66
|
+
var exit2 = new THREE.Vector3(-0.08, 2.4, -6.5);
|
|
67
|
+
var launchPath = new THREE.CatmullRomCurve3(
|
|
68
|
+
[launch0, launch1, launch2, launch3],
|
|
69
|
+
false,
|
|
70
|
+
"catmullrom",
|
|
71
|
+
0.5
|
|
72
|
+
);
|
|
73
|
+
var flightPath = new THREE.CatmullRomCurve3(
|
|
74
|
+
[flight0, flight1, flight2, flight3],
|
|
75
|
+
false,
|
|
76
|
+
"catmullrom",
|
|
77
|
+
0.42
|
|
78
|
+
);
|
|
79
|
+
var flightPath2 = new THREE.CatmullRomCurve3(
|
|
80
|
+
[flight2_0, flight2_1, flight2_2, flight2_3, flight2_4],
|
|
81
|
+
false,
|
|
82
|
+
"catmullrom",
|
|
83
|
+
0.42
|
|
84
|
+
);
|
|
85
|
+
var INTRO_DRONE_POS = new THREE.Vector3(1.4, 0.5, 0.5);
|
|
86
|
+
var INTRO_DRONE_SCALE = 1.8;
|
|
87
|
+
var PICKUP_KIOSK_POS = new THREE.Vector3(0.65, -0.15, 0.1);
|
|
88
|
+
var exitPath = new THREE.CatmullRomCurve3(
|
|
89
|
+
[exit0, exit1, exit2],
|
|
90
|
+
false,
|
|
91
|
+
"catmullrom",
|
|
92
|
+
0.5
|
|
93
|
+
);
|
|
94
|
+
var KIOSK_CENTER = new THREE.Vector3(-0.08, -0.175, -3.12);
|
|
95
|
+
var KIOSK_TOP_Y = 0;
|
|
96
|
+
function clamp01(value) {
|
|
97
|
+
return Math.max(0, Math.min(1, value));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// lib/three/setup.ts
|
|
101
|
+
function createFallbackDrone() {
|
|
102
|
+
const group = new THREE2.Group();
|
|
103
|
+
const bodyGeo = new THREE2.BoxGeometry(0.12, 0.06, 0.16);
|
|
104
|
+
const bodyMat = new THREE2.MeshStandardMaterial({
|
|
105
|
+
color: "#d7d1c4",
|
|
106
|
+
metalness: 0.3,
|
|
107
|
+
roughness: 0.6
|
|
108
|
+
});
|
|
109
|
+
const body = new THREE2.Mesh(bodyGeo, bodyMat);
|
|
110
|
+
body.castShadow = true;
|
|
111
|
+
group.add(body);
|
|
112
|
+
const propGeo = new THREE2.CylinderGeometry(0.02, 0.02, 5e-3, 8);
|
|
113
|
+
const propMat = new THREE2.MeshStandardMaterial({ color: "#333" });
|
|
114
|
+
for (let i = 0; i < 4; i++) {
|
|
115
|
+
const prop = new THREE2.Mesh(propGeo, propMat);
|
|
116
|
+
const angle = i / 4 * Math.PI * 2;
|
|
117
|
+
prop.position.set(
|
|
118
|
+
Math.cos(angle) * 0.1,
|
|
119
|
+
0.04,
|
|
120
|
+
Math.sin(angle) * 0.1
|
|
121
|
+
);
|
|
122
|
+
group.add(prop);
|
|
123
|
+
}
|
|
124
|
+
group.rotation.x = Math.PI / 2;
|
|
125
|
+
return group;
|
|
126
|
+
}
|
|
127
|
+
function createPackageMesh() {
|
|
128
|
+
const geo = new import_RoundedBoxGeometry.RoundedBoxGeometry(0.08, 0.05, 0.12, 2, 8e-3);
|
|
129
|
+
const mat = new THREE2.MeshStandardMaterial({
|
|
130
|
+
color: "#8B6F47",
|
|
131
|
+
metalness: 0,
|
|
132
|
+
roughness: 0.92,
|
|
133
|
+
envMapIntensity: 0.3
|
|
134
|
+
});
|
|
135
|
+
const mesh = new THREE2.Mesh(geo, mat);
|
|
136
|
+
mesh.castShadow = true;
|
|
137
|
+
return mesh;
|
|
138
|
+
}
|
|
139
|
+
function createKioskMesh() {
|
|
140
|
+
const geo = new THREE2.BoxGeometry(0.5, 0.35, 0.4);
|
|
141
|
+
const mat = new THREE2.MeshStandardMaterial({
|
|
142
|
+
color: "#e8e4dc",
|
|
143
|
+
metalness: 0.1,
|
|
144
|
+
roughness: 0.8
|
|
145
|
+
});
|
|
146
|
+
const mesh = new THREE2.Mesh(geo, mat);
|
|
147
|
+
mesh.castShadow = true;
|
|
148
|
+
mesh.receiveShadow = true;
|
|
149
|
+
return mesh;
|
|
150
|
+
}
|
|
151
|
+
function createDroneRig(canvas) {
|
|
152
|
+
const renderer = new THREE2.WebGLRenderer({
|
|
153
|
+
canvas,
|
|
154
|
+
antialias: true,
|
|
155
|
+
alpha: true
|
|
156
|
+
});
|
|
157
|
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
158
|
+
renderer.toneMapping = THREE2.ACESFilmicToneMapping;
|
|
159
|
+
renderer.toneMappingExposure = 1;
|
|
160
|
+
renderer.outputColorSpace = THREE2.SRGBColorSpace;
|
|
161
|
+
renderer.shadowMap.enabled = true;
|
|
162
|
+
renderer.shadowMap.type = THREE2.PCFSoftShadowMap;
|
|
163
|
+
const scene = new THREE2.Scene();
|
|
164
|
+
scene.background = null;
|
|
165
|
+
const camera = new THREE2.PerspectiveCamera(
|
|
166
|
+
45,
|
|
167
|
+
canvas.clientWidth / canvas.clientHeight,
|
|
168
|
+
0.1,
|
|
169
|
+
100
|
|
170
|
+
);
|
|
171
|
+
camera.position.set(0, 0.5, 4);
|
|
172
|
+
camera.lookAt(0, 0.3, 0);
|
|
173
|
+
const ambient = new THREE2.AmbientLight(16777215, 0.7);
|
|
174
|
+
scene.add(ambient);
|
|
175
|
+
const dirLight = new THREE2.DirectionalLight(16777215, 0.9);
|
|
176
|
+
dirLight.position.set(2, 4, 3);
|
|
177
|
+
dirLight.castShadow = true;
|
|
178
|
+
dirLight.shadow.mapSize.set(1024, 1024);
|
|
179
|
+
dirLight.shadow.camera.near = 0.5;
|
|
180
|
+
dirLight.shadow.camera.far = 20;
|
|
181
|
+
dirLight.shadow.camera.left = -4;
|
|
182
|
+
dirLight.shadow.camera.right = 4;
|
|
183
|
+
dirLight.shadow.camera.top = 4;
|
|
184
|
+
dirLight.shadow.camera.bottom = -4;
|
|
185
|
+
scene.add(dirLight);
|
|
186
|
+
const dronePayload = new THREE2.Group();
|
|
187
|
+
const drone = createFallbackDrone();
|
|
188
|
+
dronePayload.add(drone);
|
|
189
|
+
const loader = new import_GLTFLoader.GLTFLoader();
|
|
190
|
+
loader.load(
|
|
191
|
+
"/3d-models/assets/drone/scene.gltf",
|
|
192
|
+
(gltf) => {
|
|
193
|
+
const root = gltf.scene;
|
|
194
|
+
root.traverse((child) => {
|
|
195
|
+
if (child instanceof THREE2.Mesh) {
|
|
196
|
+
child.castShadow = true;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
root.scale.setScalar(0.15);
|
|
200
|
+
while (drone.children.length) drone.remove(drone.children[0]);
|
|
201
|
+
drone.add(root);
|
|
202
|
+
},
|
|
203
|
+
void 0,
|
|
204
|
+
() => {
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
const pkg = createPackageMesh();
|
|
208
|
+
pkg.position.set(PICKUP_KIOSK_POS.x, PICKUP_KIOSK_POS.y + 0.175 + PACKAGE_HEIGHT / 2, PICKUP_KIOSK_POS.z);
|
|
209
|
+
pkg.visible = false;
|
|
210
|
+
scene.add(pkg);
|
|
211
|
+
scene.add(dronePayload);
|
|
212
|
+
const pickupKiosk = new THREE2.Group();
|
|
213
|
+
pickupKiosk.position.copy(PICKUP_KIOSK_POS);
|
|
214
|
+
pickupKiosk.visible = false;
|
|
215
|
+
const pickupKioskFallback = createKioskMesh();
|
|
216
|
+
pickupKiosk.add(pickupKioskFallback);
|
|
217
|
+
scene.add(pickupKiosk);
|
|
218
|
+
const pickupKioskLoader = new import_GLTFLoader.GLTFLoader();
|
|
219
|
+
pickupKioskLoader.load(
|
|
220
|
+
"/3d-models/assets/smart_box/scene.gltf",
|
|
221
|
+
(gltf) => {
|
|
222
|
+
const root = gltf.scene;
|
|
223
|
+
root.traverse((child) => {
|
|
224
|
+
if (child instanceof THREE2.Mesh) {
|
|
225
|
+
child.castShadow = true;
|
|
226
|
+
child.receiveShadow = true;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
root.scale.setScalar(0.2);
|
|
230
|
+
root.position.set(0, 0, 0);
|
|
231
|
+
pickupKiosk.remove(pickupKioskFallback);
|
|
232
|
+
pickupKioskFallback.geometry.dispose();
|
|
233
|
+
pickupKioskFallback.material.dispose();
|
|
234
|
+
pickupKiosk.add(root);
|
|
235
|
+
},
|
|
236
|
+
void 0,
|
|
237
|
+
() => {
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
const kiosk = new THREE2.Group();
|
|
241
|
+
kiosk.position.copy(KIOSK_CENTER);
|
|
242
|
+
kiosk.visible = false;
|
|
243
|
+
const kioskFallback = createKioskMesh();
|
|
244
|
+
kiosk.add(kioskFallback);
|
|
245
|
+
scene.add(kiosk);
|
|
246
|
+
const kioskLoader = new import_GLTFLoader.GLTFLoader();
|
|
247
|
+
kioskLoader.load(
|
|
248
|
+
"/3d-models/assets/smart_box/scene.gltf",
|
|
249
|
+
(gltf) => {
|
|
250
|
+
const root = gltf.scene;
|
|
251
|
+
root.traverse((child) => {
|
|
252
|
+
if (child instanceof THREE2.Mesh) {
|
|
253
|
+
child.castShadow = true;
|
|
254
|
+
child.receiveShadow = true;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
root.scale.setScalar(0.2);
|
|
258
|
+
root.position.set(0, 0, 0);
|
|
259
|
+
kiosk.remove(kioskFallback);
|
|
260
|
+
kioskFallback.geometry.dispose();
|
|
261
|
+
kioskFallback.material.dispose();
|
|
262
|
+
kiosk.add(root);
|
|
263
|
+
},
|
|
264
|
+
void 0,
|
|
265
|
+
() => {
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
return {
|
|
269
|
+
renderer,
|
|
270
|
+
scene,
|
|
271
|
+
camera,
|
|
272
|
+
dronePayload,
|
|
273
|
+
drone,
|
|
274
|
+
package: pkg,
|
|
275
|
+
pickupKiosk,
|
|
276
|
+
kiosk
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function resizeDroneRig(rig, width, height, dpr) {
|
|
280
|
+
rig.renderer.setPixelRatio(Math.min(dpr, 2));
|
|
281
|
+
rig.renderer.setSize(width, height);
|
|
282
|
+
rig.camera.aspect = width / height;
|
|
283
|
+
rig.camera.updateProjectionMatrix();
|
|
284
|
+
}
|
|
285
|
+
function lerp(a, b, t) {
|
|
286
|
+
return a + (b - a) * clamp01(t);
|
|
287
|
+
}
|
|
288
|
+
var HOVER_ALTITUDE = 1;
|
|
289
|
+
var PACKAGE_HEIGHT = 0.05;
|
|
290
|
+
var PACKAGE_LAND_Y = KIOSK_TOP_Y + PACKAGE_HEIGHT / 2;
|
|
291
|
+
var PACKAGE_OFFSET_BELOW_DRONE = 0.4;
|
|
292
|
+
var RELEASE_ALTITUDE = PACKAGE_LAND_Y + PACKAGE_OFFSET_BELOW_DRONE + 0.06;
|
|
293
|
+
function renderDroneState(rig, state) {
|
|
294
|
+
const { dronePayload, drone, package: pkg, pickupKiosk, kiosk, camera } = rig;
|
|
295
|
+
const introDroneT = clamp01(state.introDroneT);
|
|
296
|
+
const emergeT = clamp01(state.emergeT);
|
|
297
|
+
const flightT = clamp01(state.flightT);
|
|
298
|
+
const flight2T = clamp01(state.flight2T);
|
|
299
|
+
const pickupT = clamp01(state.pickupT);
|
|
300
|
+
const dropT = clamp01(state.drop);
|
|
301
|
+
const releaseT = clamp01(state.release);
|
|
302
|
+
const departT = clamp01(state.depart);
|
|
303
|
+
const hoverT = clamp01(state.hover);
|
|
304
|
+
const bank = state.bank;
|
|
305
|
+
const pickupKioskReveal = clamp01(state.pickupKioskReveal);
|
|
306
|
+
const kioskReveal = clamp01(state.kioskReveal);
|
|
307
|
+
let scale;
|
|
308
|
+
let pos;
|
|
309
|
+
if (introDroneT > 0) {
|
|
310
|
+
const launchStart = launchPath.getPoint(0);
|
|
311
|
+
pos = introDroneT >= 1 ? INTRO_DRONE_POS.clone() : INTRO_DRONE_POS.clone().lerp(launchStart, 1 - introDroneT);
|
|
312
|
+
scale = lerp(1, INTRO_DRONE_SCALE, introDroneT);
|
|
313
|
+
} else if (departT > 0) {
|
|
314
|
+
scale = lerp(1, 0, departT);
|
|
315
|
+
pos = exitPath.getPoint(departT);
|
|
316
|
+
} else if (emergeT < 1) {
|
|
317
|
+
scale = 1;
|
|
318
|
+
pos = launchPath.getPoint(emergeT);
|
|
319
|
+
} else {
|
|
320
|
+
scale = 1;
|
|
321
|
+
if (flightT < 1) {
|
|
322
|
+
pos = flightPath.getPoint(flightT);
|
|
323
|
+
} else if (pickupT < 1) {
|
|
324
|
+
const flightEnd = flightPath.getPoint(1);
|
|
325
|
+
const pickupHover = new THREE2.Vector3(0.65, 0.5, 0.1);
|
|
326
|
+
pos = flightEnd.clone().lerp(pickupHover, pickupT);
|
|
327
|
+
} else if (flight2T < 1) {
|
|
328
|
+
pos = flightPath2.getPoint(flight2T);
|
|
329
|
+
const flyOutEnd = 0.35;
|
|
330
|
+
const flyInStart = 0.65;
|
|
331
|
+
if (flight2T <= flyOutEnd) {
|
|
332
|
+
scale = lerp(1, 0.12, flight2T / flyOutEnd);
|
|
333
|
+
} else if (flight2T >= flyInStart) {
|
|
334
|
+
scale = lerp(0.12, 1, (flight2T - flyInStart) / (1 - flyInStart));
|
|
335
|
+
} else {
|
|
336
|
+
scale = 0.12;
|
|
337
|
+
}
|
|
338
|
+
} else if (dropT > 0) {
|
|
339
|
+
const descendY = lerp(HOVER_ALTITUDE, RELEASE_ALTITUDE, dropT);
|
|
340
|
+
pos = new THREE2.Vector3(-0.08, descendY, -3.12);
|
|
341
|
+
} else {
|
|
342
|
+
const flight2End = flightPath2.getPoint(1);
|
|
343
|
+
const hoverPos = new THREE2.Vector3(-0.08, HOVER_ALTITUDE, -3.12);
|
|
344
|
+
pos = flight2End.clone().lerp(hoverPos, hoverT);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
dronePayload.position.copy(pos);
|
|
348
|
+
dronePayload.scale.setScalar(scale);
|
|
349
|
+
dronePayload.updateMatrixWorld(true);
|
|
350
|
+
const bankRad = bank * 0.45;
|
|
351
|
+
dronePayload.rotation.order = "YXZ";
|
|
352
|
+
dronePayload.rotation.x = Math.PI / 2;
|
|
353
|
+
dronePayload.rotation.y = 0;
|
|
354
|
+
dronePayload.rotation.z = departT > 0 ? -0.2 : -bankRad;
|
|
355
|
+
const depthT = state.flightDepth;
|
|
356
|
+
const camOffsetX = -1;
|
|
357
|
+
const camOffsetZ = 2;
|
|
358
|
+
const camX = pos.x + camOffsetX;
|
|
359
|
+
const camZ = pos.z + camOffsetZ;
|
|
360
|
+
const camY = lerp(0.5, pos.y * 0.3 + 0.4, depthT);
|
|
361
|
+
camera.position.set(camX, camY, camZ);
|
|
362
|
+
const lookZ = pos.z - 0.4;
|
|
363
|
+
const lookY = pos.y * 0.6 + 0.15;
|
|
364
|
+
camera.lookAt(pos.x + 0.3, lookY, lookZ);
|
|
365
|
+
const pickupKioskRiseY = lerp(PICKUP_KIOSK_POS.y - 0.5, PICKUP_KIOSK_POS.y, pickupKioskReveal);
|
|
366
|
+
const pickupKioskTopY = pickupKioskRiseY + 0.175;
|
|
367
|
+
const pickupPackageY = pickupKioskTopY + PACKAGE_HEIGHT / 2;
|
|
368
|
+
const deliveryKioskPos = new THREE2.Vector3(-0.08, PACKAGE_LAND_Y, -3.12);
|
|
369
|
+
pkg.visible = introDroneT <= 0 && pickupKioskReveal > 0;
|
|
370
|
+
if (pkg.visible) {
|
|
371
|
+
if (pickupT < 1) {
|
|
372
|
+
if (pkg.parent) {
|
|
373
|
+
pkg.parent.remove(pkg);
|
|
374
|
+
rig.scene.add(pkg);
|
|
375
|
+
}
|
|
376
|
+
const pickupPos = new THREE2.Vector3(0.65, pickupPackageY, 0.1);
|
|
377
|
+
const attachWorld = new THREE2.Vector3(0, 0.4, 0).applyMatrix4(drone.matrixWorld);
|
|
378
|
+
pkg.position.lerpVectors(pickupPos, attachWorld, pickupT);
|
|
379
|
+
pkg.rotation.set(0, 0, 0);
|
|
380
|
+
pkg.scale.setScalar(1);
|
|
381
|
+
} else if (releaseT >= 1) {
|
|
382
|
+
if (pkg.parent) {
|
|
383
|
+
pkg.parent.remove(pkg);
|
|
384
|
+
rig.scene.add(pkg);
|
|
385
|
+
}
|
|
386
|
+
pkg.position.copy(deliveryKioskPos);
|
|
387
|
+
pkg.rotation.set(0, 0, 0);
|
|
388
|
+
pkg.scale.setScalar(1);
|
|
389
|
+
} else if (releaseT > 0) {
|
|
390
|
+
if (pkg.parent) {
|
|
391
|
+
pkg.parent.remove(pkg);
|
|
392
|
+
rig.scene.add(pkg);
|
|
393
|
+
}
|
|
394
|
+
const attachedWorld = new THREE2.Vector3(0, 0.4, 0).applyMatrix4(drone.matrixWorld);
|
|
395
|
+
pkg.position.lerpVectors(attachedWorld, deliveryKioskPos, releaseT);
|
|
396
|
+
pkg.rotation.set(0, 0, 0);
|
|
397
|
+
pkg.scale.setScalar(lerp(scale, 1, releaseT));
|
|
398
|
+
} else {
|
|
399
|
+
if (pkg.parent !== rig.drone) {
|
|
400
|
+
rig.scene.remove(pkg);
|
|
401
|
+
rig.drone.add(pkg);
|
|
402
|
+
}
|
|
403
|
+
pkg.position.set(0, 0.4, 0);
|
|
404
|
+
pkg.rotation.set(0, 0, 0);
|
|
405
|
+
pkg.scale.setScalar(1);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
pickupKiosk.visible = pickupKioskReveal > 0;
|
|
409
|
+
pickupKiosk.position.set(PICKUP_KIOSK_POS.x, pickupKioskRiseY, PICKUP_KIOSK_POS.z);
|
|
410
|
+
kiosk.visible = kioskReveal > 0;
|
|
411
|
+
kiosk.traverse((obj) => {
|
|
412
|
+
if (obj instanceof THREE2.Mesh && obj.material) {
|
|
413
|
+
const mat = Array.isArray(obj.material) ? obj.material[0] : obj.material;
|
|
414
|
+
if (mat instanceof THREE2.MeshStandardMaterial) {
|
|
415
|
+
mat.opacity = kioskReveal;
|
|
416
|
+
mat.transparent = kioskReveal < 1;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
rig.renderer.render(rig.scene, rig.camera);
|
|
421
|
+
}
|
|
422
|
+
function disposeDroneRig(rig) {
|
|
423
|
+
rig.renderer.dispose();
|
|
424
|
+
rig.dronePayload.traverse((obj) => {
|
|
425
|
+
var _a, _b;
|
|
426
|
+
if (obj instanceof THREE2.Mesh) {
|
|
427
|
+
(_a = obj.geometry) == null ? void 0 : _a.dispose();
|
|
428
|
+
if (Array.isArray(obj.material)) {
|
|
429
|
+
obj.material.forEach((m) => m.dispose());
|
|
430
|
+
} else {
|
|
431
|
+
(_b = obj.material) == null ? void 0 : _b.dispose();
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
rig.package.geometry.dispose();
|
|
436
|
+
rig.package.material.dispose();
|
|
437
|
+
rig.pickupKiosk.traverse((obj) => {
|
|
438
|
+
var _a, _b;
|
|
439
|
+
if (obj instanceof THREE2.Mesh) {
|
|
440
|
+
(_a = obj.geometry) == null ? void 0 : _a.dispose();
|
|
441
|
+
if (Array.isArray(obj.material)) {
|
|
442
|
+
obj.material.forEach((m) => m.dispose());
|
|
443
|
+
} else {
|
|
444
|
+
(_b = obj.material) == null ? void 0 : _b.dispose();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
rig.kiosk.traverse((obj) => {
|
|
449
|
+
var _a, _b;
|
|
450
|
+
if (obj instanceof THREE2.Mesh) {
|
|
451
|
+
(_a = obj.geometry) == null ? void 0 : _a.dispose();
|
|
452
|
+
if (Array.isArray(obj.material)) {
|
|
453
|
+
obj.material.forEach((m) => m.dispose());
|
|
454
|
+
} else {
|
|
455
|
+
(_b = obj.material) == null ? void 0 : _b.dispose();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// components/DroneScene.tsx
|
|
462
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
463
|
+
var DroneScene = (0, import_react.forwardRef)(function DroneScene2({ className }, ref) {
|
|
464
|
+
const canvasRef = (0, import_react.useRef)(null);
|
|
465
|
+
const rigRef = (0, import_react.useRef)(null);
|
|
466
|
+
const stateRef = (0, import_react.useRef)(null);
|
|
467
|
+
(0, import_react.useImperativeHandle)(ref, () => ({
|
|
468
|
+
setState(nextState) {
|
|
469
|
+
stateRef.current = nextState;
|
|
470
|
+
},
|
|
471
|
+
resize(width, height, dpr) {
|
|
472
|
+
if (!rigRef.current) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
resizeDroneRig(rigRef.current, width, height, dpr);
|
|
476
|
+
},
|
|
477
|
+
renderFrame() {
|
|
478
|
+
if (!rigRef.current || !stateRef.current) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
renderDroneState(rigRef.current, stateRef.current);
|
|
482
|
+
}
|
|
483
|
+
}));
|
|
484
|
+
(0, import_react.useEffect)(() => {
|
|
485
|
+
if (!canvasRef.current) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const rig = createDroneRig(canvasRef.current);
|
|
489
|
+
rigRef.current = rig;
|
|
490
|
+
return () => {
|
|
491
|
+
if (!rigRef.current) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
disposeDroneRig(rigRef.current);
|
|
495
|
+
rigRef.current = null;
|
|
496
|
+
};
|
|
497
|
+
}, []);
|
|
498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("canvas", { ref: canvasRef, className });
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// components/NarrationRevolver.tsx
|
|
502
|
+
var import_react2 = require("react");
|
|
503
|
+
var import_react3 = require("motion/react");
|
|
504
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
505
|
+
var NARRATIONS = [
|
|
506
|
+
{
|
|
507
|
+
text: "K\u1EF7 nguy\xEAn m\u1EDBi c\u1EE7a v\u1EADn t\u1EA3i th\xF4ng minh l\u1EA5y UAV l\xE0m c\u1ED1t l\xF5i",
|
|
508
|
+
tag: "h1",
|
|
509
|
+
className: "zipline-narration-intro"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
text: "\u0110\u1EB7t h\xE0ng ti\u1EC7n l\u1EE3i tr\xEAn \u1EE9ng d\u1EE5ng",
|
|
513
|
+
tag: "h2",
|
|
514
|
+
className: "zipline-narration-order"
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
text: "Giao h\xE0ng si\xEAu t\u1ED1c trong v\xE0i ph\xFAt",
|
|
518
|
+
tag: "h2",
|
|
519
|
+
className: "zipline-narration-order"
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
text: "Nh\u1EADn h\xE0ng trong v\xE0i gi\xE2y",
|
|
523
|
+
tag: "h2",
|
|
524
|
+
className: "zipline-narration-delivers"
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
text: "Kh\xF4ng c\u1EA7n ch\u1EDD. UAV giao ngay. K\u1EBFt n\u1ED1i tr\u0103m tri\u1EC7u kh\xE1ch h\xE0ng. Tham gia LAE Sandbox ngay",
|
|
528
|
+
tag: "h3",
|
|
529
|
+
className: "zipline-narration-cta"
|
|
530
|
+
}
|
|
531
|
+
];
|
|
532
|
+
var NarrationRevolver = (0, import_react2.forwardRef)(function NarrationRevolver2(_, ref) {
|
|
533
|
+
const activeHeading = (0, import_react3.useMotionValue)(0);
|
|
534
|
+
const springHeading = (0, import_react3.useSpring)(activeHeading, {
|
|
535
|
+
stiffness: 100,
|
|
536
|
+
damping: 25,
|
|
537
|
+
restDelta: 1e-3
|
|
538
|
+
});
|
|
539
|
+
(0, import_react2.useImperativeHandle)(ref, () => ({
|
|
540
|
+
setActiveHeading(value) {
|
|
541
|
+
activeHeading.set(value);
|
|
542
|
+
}
|
|
543
|
+
}));
|
|
544
|
+
const stripY = (0, import_react3.useTransform)(springHeading, (v) => `${-v * 100}%`);
|
|
545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "zipline-narration-viewport", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react3.motion.div, { className: "zipline-narration-strip", style: { y: stripY }, children: NARRATIONS.map((item, index) => {
|
|
546
|
+
const Tag = item.tag;
|
|
547
|
+
const slotRotateX = (0, import_react3.useTransform)(
|
|
548
|
+
springHeading,
|
|
549
|
+
(v) => (v - index) * 18
|
|
550
|
+
);
|
|
551
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
552
|
+
import_react3.motion.div,
|
|
553
|
+
{
|
|
554
|
+
className: `zipline-narration-slot ${item.className}`,
|
|
555
|
+
style: {
|
|
556
|
+
rotateX: slotRotateX,
|
|
557
|
+
transformOrigin: "left top -300px",
|
|
558
|
+
backfaceVisibility: "hidden"
|
|
559
|
+
},
|
|
560
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Tag, { children: item.text })
|
|
561
|
+
},
|
|
562
|
+
index
|
|
563
|
+
);
|
|
564
|
+
}) }) });
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// components/PhoneUI.tsx
|
|
568
|
+
var import_react4 = require("react");
|
|
569
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
570
|
+
var PhoneUI = (0, import_react4.forwardRef)(
|
|
571
|
+
function PhoneUI2({ screenRef }, ref) {
|
|
572
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
573
|
+
"div",
|
|
574
|
+
{
|
|
575
|
+
ref,
|
|
576
|
+
className: "phone-container",
|
|
577
|
+
style: {
|
|
578
|
+
width: "280px",
|
|
579
|
+
perspective: "1000px",
|
|
580
|
+
position: "relative"
|
|
581
|
+
},
|
|
582
|
+
children: [
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
584
|
+
"svg",
|
|
585
|
+
{
|
|
586
|
+
viewBox: "0 0 280 560",
|
|
587
|
+
fill: "none",
|
|
588
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
589
|
+
style: { width: "100%", height: "auto", display: "block" },
|
|
590
|
+
children: [
|
|
591
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("defs", { children: [
|
|
592
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("clipPath", { id: "screenClip", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "12", y: "12", width: "256", height: "536", rx: "28" }) }),
|
|
593
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("linearGradient", { id: "phoneBg", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
594
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0%", stopColor: "#f7f9fc" }),
|
|
595
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "100%", stopColor: "#edf1f7" })
|
|
596
|
+
] }),
|
|
597
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("filter", { id: "phoneShadow", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("feDropShadow", { dx: "0", dy: "8", stdDeviation: "16", floodOpacity: "0.15" }) })
|
|
598
|
+
] }),
|
|
599
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
600
|
+
"rect",
|
|
601
|
+
{
|
|
602
|
+
x: "2",
|
|
603
|
+
y: "2",
|
|
604
|
+
width: "276",
|
|
605
|
+
height: "556",
|
|
606
|
+
rx: "36",
|
|
607
|
+
fill: "#1a1a1a",
|
|
608
|
+
filter: "url(#phoneShadow)"
|
|
609
|
+
}
|
|
610
|
+
),
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
612
|
+
"rect",
|
|
613
|
+
{
|
|
614
|
+
x: "6",
|
|
615
|
+
y: "6",
|
|
616
|
+
width: "268",
|
|
617
|
+
height: "548",
|
|
618
|
+
rx: "33",
|
|
619
|
+
fill: "#2a2a2a"
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { clipPath: "url(#screenClip)", children: [
|
|
623
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "12", y: "12", width: "256", height: "536", fill: "url(#phoneBg)" }),
|
|
624
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "92", y: "22", width: "96", height: "24", rx: "12", fill: "#1a1a1a" }),
|
|
625
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "140", cy: "34", r: "5", fill: "#333" }),
|
|
626
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
627
|
+
"text",
|
|
628
|
+
{
|
|
629
|
+
x: "140",
|
|
630
|
+
y: "72",
|
|
631
|
+
textAnchor: "middle",
|
|
632
|
+
fill: "#1a6bff",
|
|
633
|
+
fontSize: "11",
|
|
634
|
+
fontWeight: "700",
|
|
635
|
+
fontFamily: "system-ui, sans-serif",
|
|
636
|
+
children: "LAE Sandbox"
|
|
637
|
+
}
|
|
638
|
+
),
|
|
639
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "32", y: "90", width: "216", height: "100", rx: "16", fill: "#ffffff" }),
|
|
640
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
641
|
+
"rect",
|
|
642
|
+
{
|
|
643
|
+
x: "32",
|
|
644
|
+
y: "90",
|
|
645
|
+
width: "216",
|
|
646
|
+
height: "100",
|
|
647
|
+
rx: "16",
|
|
648
|
+
fill: "none",
|
|
649
|
+
stroke: "#e5e7eb",
|
|
650
|
+
strokeWidth: "1"
|
|
651
|
+
}
|
|
652
|
+
),
|
|
653
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "60", cy: "120", r: "16", fill: "#eef4ff" }),
|
|
654
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
655
|
+
"text",
|
|
656
|
+
{
|
|
657
|
+
x: "60",
|
|
658
|
+
y: "124",
|
|
659
|
+
textAnchor: "middle",
|
|
660
|
+
fill: "#1a6bff",
|
|
661
|
+
fontSize: "14",
|
|
662
|
+
children: "\u{1F4E6}"
|
|
663
|
+
}
|
|
664
|
+
),
|
|
665
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
666
|
+
"text",
|
|
667
|
+
{
|
|
668
|
+
x: "88",
|
|
669
|
+
y: "118",
|
|
670
|
+
fill: "#111",
|
|
671
|
+
fontSize: "12",
|
|
672
|
+
fontWeight: "600",
|
|
673
|
+
fontFamily: "system-ui, sans-serif",
|
|
674
|
+
children: "\u0110\u01A1n h\xE0ng h\u1ECFa t\u1ED1c"
|
|
675
|
+
}
|
|
676
|
+
),
|
|
677
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
678
|
+
"text",
|
|
679
|
+
{
|
|
680
|
+
x: "88",
|
|
681
|
+
y: "136",
|
|
682
|
+
fill: "#6b7280",
|
|
683
|
+
fontSize: "10",
|
|
684
|
+
fontFamily: "system-ui, sans-serif",
|
|
685
|
+
children: "\u0110\u1EBFn trong ~4 ph\xFAt"
|
|
686
|
+
}
|
|
687
|
+
),
|
|
688
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "88", y: "152", width: "140", height: "6", rx: "3", fill: "#e5e7eb" }),
|
|
689
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "88", y: "152", width: "95", height: "6", rx: "3", fill: "#1a6bff" }),
|
|
690
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "32", y: "210", width: "216", height: "180", rx: "16", fill: "#e8f0fe" }),
|
|
691
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "140", cy: "280", r: "28", fill: "#ffffff", stroke: "#1a6bff", strokeWidth: "2" }),
|
|
692
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
693
|
+
"text",
|
|
694
|
+
{
|
|
695
|
+
x: "140",
|
|
696
|
+
y: "276",
|
|
697
|
+
textAnchor: "middle",
|
|
698
|
+
fill: "#1a6bff",
|
|
699
|
+
fontSize: "16",
|
|
700
|
+
fontWeight: "700",
|
|
701
|
+
fontFamily: "system-ui, sans-serif",
|
|
702
|
+
children: "4"
|
|
703
|
+
}
|
|
704
|
+
),
|
|
705
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
706
|
+
"text",
|
|
707
|
+
{
|
|
708
|
+
x: "140",
|
|
709
|
+
y: "292",
|
|
710
|
+
textAnchor: "middle",
|
|
711
|
+
fill: "#1a6bff",
|
|
712
|
+
fontSize: "8",
|
|
713
|
+
fontFamily: "system-ui, sans-serif",
|
|
714
|
+
children: "MIN"
|
|
715
|
+
}
|
|
716
|
+
),
|
|
717
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "100", cy: "340", r: "4", fill: "#1a6bff" }),
|
|
718
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "140", cy: "340", r: "4", fill: "#1a6bff", opacity: "0.5" }),
|
|
719
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "180", cy: "340", r: "4", fill: "#d1d5db" }),
|
|
720
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
721
|
+
"text",
|
|
722
|
+
{
|
|
723
|
+
x: "140",
|
|
724
|
+
y: "370",
|
|
725
|
+
textAnchor: "middle",
|
|
726
|
+
fill: "#9ca3af",
|
|
727
|
+
fontSize: "9",
|
|
728
|
+
fontFamily: "system-ui, sans-serif",
|
|
729
|
+
children: "Theo d\xF5i tr\u1EF1c ti\u1EBFp"
|
|
730
|
+
}
|
|
731
|
+
),
|
|
732
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: "32", y: "420", width: "216", height: "44", rx: "22", fill: "#1a6bff" }),
|
|
733
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
734
|
+
"text",
|
|
735
|
+
{
|
|
736
|
+
x: "140",
|
|
737
|
+
y: "447",
|
|
738
|
+
textAnchor: "middle",
|
|
739
|
+
fill: "#ffffff",
|
|
740
|
+
fontSize: "13",
|
|
741
|
+
fontWeight: "600",
|
|
742
|
+
fontFamily: "system-ui, sans-serif",
|
|
743
|
+
children: "Theo d\xF5i \u0111\u01A1n h\xE0ng"
|
|
744
|
+
}
|
|
745
|
+
)
|
|
746
|
+
] })
|
|
747
|
+
]
|
|
748
|
+
}
|
|
749
|
+
),
|
|
750
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
751
|
+
"div",
|
|
752
|
+
{
|
|
753
|
+
ref: screenRef,
|
|
754
|
+
style: {
|
|
755
|
+
position: "absolute",
|
|
756
|
+
top: "12px",
|
|
757
|
+
left: "12px",
|
|
758
|
+
right: "12px",
|
|
759
|
+
bottom: "12px",
|
|
760
|
+
pointerEvents: "none"
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
)
|
|
764
|
+
]
|
|
765
|
+
}
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
);
|
|
769
|
+
var PhoneUI_default = PhoneUI;
|
|
770
|
+
|
|
771
|
+
// lib/animation/timeline.ts
|
|
772
|
+
var import_gsap = __toESM(require("gsap"), 1);
|
|
773
|
+
var import_ScrollTrigger = require("gsap/ScrollTrigger");
|
|
774
|
+
import_gsap.default.registerPlugin(import_ScrollTrigger.ScrollTrigger);
|
|
775
|
+
function createMasterTimeline(root, state, onUpdate) {
|
|
776
|
+
const timeline = import_gsap.default.timeline({
|
|
777
|
+
defaults: { ease: "none" },
|
|
778
|
+
onUpdate,
|
|
779
|
+
scrollTrigger: {
|
|
780
|
+
trigger: root,
|
|
781
|
+
start: "top top",
|
|
782
|
+
end: "+=8000",
|
|
783
|
+
pin: true,
|
|
784
|
+
scrub: true,
|
|
785
|
+
anticipatePin: 1,
|
|
786
|
+
invalidateOnRefresh: true
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
timeline.addLabel("intro", 0).set(state, {
|
|
790
|
+
introDroneT: 1,
|
|
791
|
+
phoneOpacity: 0,
|
|
792
|
+
phoneMaskOpen: 0,
|
|
793
|
+
phoneTilt: 0,
|
|
794
|
+
phoneScale: 1,
|
|
795
|
+
portalBloom: 0,
|
|
796
|
+
uiPulse: 0,
|
|
797
|
+
emergeT: 0,
|
|
798
|
+
flightT: 0,
|
|
799
|
+
flight2T: 0,
|
|
800
|
+
pickupT: 0,
|
|
801
|
+
flightDepth: 0,
|
|
802
|
+
bank: 0,
|
|
803
|
+
hover: 0,
|
|
804
|
+
drop: 0,
|
|
805
|
+
release: 0,
|
|
806
|
+
reel: 0,
|
|
807
|
+
depart: 0,
|
|
808
|
+
settle: 0,
|
|
809
|
+
cloudFarShift: 0,
|
|
810
|
+
cloudNearShift: 0,
|
|
811
|
+
groundRise: 0,
|
|
812
|
+
pickupKioskReveal: 0,
|
|
813
|
+
kioskReveal: 0,
|
|
814
|
+
activeHeading: 0
|
|
815
|
+
}).addLabel("order_intro", 0.5).to(state, { introDroneT: 0, duration: 0.55 }, "order_intro").to(state, { phoneOpacity: 1, duration: 0.5 }, "order_intro").to(state, { activeHeading: 1, duration: 0.55, ease: "power2.inOut" }, "order_intro").addLabel("order_confirmed", 1.15).to(state, { uiPulse: 1, duration: 0.28 }, "order_confirmed").to(state, { phoneMaskOpen: 0.3, duration: 0.45 }, "order_confirmed+=0.05").to(state, { phoneTilt: -6, phoneScale: 0.95, duration: 0.48 }, "order_confirmed+=0.08").addLabel("drone_launch", 1.7).to(state, { portalBloom: 1, duration: 0.32 }, "drone_launch").to(state, { emergeT: 1, duration: 0.75 }, "drone_launch+=0.04").to(state, { phoneMaskOpen: 1, duration: 0.7 }, "drone_launch+=0.08").to(state, { phoneOpacity: 0.35, duration: 0.55 }, "drone_launch+=0.1").to(state, { activeHeading: 2, duration: 0.5 }, "drone_launch+=0.22").addLabel("flight", 2.65).to(state, { flightT: 1, duration: 2 }, "flight").to(state, { flightDepth: 1, duration: 1.8 }, "flight+=0.08").to(state, { bank: 1.15, duration: 0.5 }, "flight").to(state, { bank: -0.88, duration: 0.5 }, "flight+=0.5").to(state, { bank: 0.25, duration: 0.6 }, "flight+=1.1").to(state, { cloudFarShift: 1, duration: 2 }, "flight").to(state, { cloudNearShift: 1, duration: 2 }, "flight+=0.08").addLabel("pickup", 4.7).to(state, { pickupKioskReveal: 1, duration: 0.4 }, "pickup").to(state, { pickupT: 1, duration: 0.22 }, "pickup+=0.45").addLabel("flight_to_delivery", 5.42).to(state, { flight2T: 1, duration: 2.2 }, "flight_to_delivery").addLabel("package_drop", 6.8).to(state, { kioskReveal: 1, duration: 0.52 }, "package_drop").to(state, { hover: 1, duration: 0.45 }, "package_drop").to(state, { activeHeading: 3, duration: 0.45, ease: "power2.inOut" }, "package_drop+=0.06").to(state, { drop: 1, duration: 1.25 }, "package_drop+=0.12").to(state, { groundRise: 1, duration: 1.25 }, "package_drop+=0.12").to(state, { settle: 1, duration: 0.3 }, "package_drop+=1.28").to(state, { release: 1, duration: 0.08 }, "package_drop+=1.3").to(state, { reel: 1, duration: 0.01 }, "package_drop+=1.3").to(state, { depart: 1, duration: 2 }, "package_drop+=1.58").addLabel("exit", 8.5).to(state, { activeHeading: 4, duration: 0.5, ease: "power2.inOut" }, "exit").to(state, { phoneOpacity: 0, portalBloom: 0, duration: 0.5 }, "exit").to(state, { exit: 1, duration: 0.65 }, "exit+=0.08");
|
|
816
|
+
return timeline;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// lib/animation/types.ts
|
|
820
|
+
function createInitialNarrativeState() {
|
|
821
|
+
return {
|
|
822
|
+
introDroneT: 1,
|
|
823
|
+
phoneOpacity: 0,
|
|
824
|
+
phoneMaskOpen: 0,
|
|
825
|
+
phoneTilt: 0,
|
|
826
|
+
phoneScale: 1,
|
|
827
|
+
portalBloom: 0,
|
|
828
|
+
uiPulse: 0,
|
|
829
|
+
emergeT: 0,
|
|
830
|
+
flightT: 0,
|
|
831
|
+
flight2T: 0,
|
|
832
|
+
pickupT: 0,
|
|
833
|
+
flightDepth: 0,
|
|
834
|
+
bank: 0,
|
|
835
|
+
hover: 0,
|
|
836
|
+
drop: 0,
|
|
837
|
+
release: 0,
|
|
838
|
+
reel: 0,
|
|
839
|
+
depart: 0,
|
|
840
|
+
settle: 0,
|
|
841
|
+
cloudFarShift: 0,
|
|
842
|
+
cloudNearShift: 0,
|
|
843
|
+
groundRise: 0,
|
|
844
|
+
pickupKioskReveal: 0,
|
|
845
|
+
kioskReveal: 0,
|
|
846
|
+
activeHeading: 0,
|
|
847
|
+
exit: 0
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// lib/hooks/useReducedMotion.ts
|
|
852
|
+
var import_react5 = require("react");
|
|
853
|
+
function useReducedMotion() {
|
|
854
|
+
const [prefersReduced, setPrefersReduced] = (0, import_react5.useState)(false);
|
|
855
|
+
(0, import_react5.useEffect)(() => {
|
|
856
|
+
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
857
|
+
setPrefersReduced(media.matches);
|
|
858
|
+
const handler = (e) => setPrefersReduced(e.matches);
|
|
859
|
+
media.addEventListener("change", handler);
|
|
860
|
+
return () => media.removeEventListener("change", handler);
|
|
861
|
+
}, []);
|
|
862
|
+
return prefersReduced;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// components/ScrollScene.tsx
|
|
866
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
867
|
+
function ScrollScene() {
|
|
868
|
+
const rootRef = (0, import_react6.useRef)(null);
|
|
869
|
+
const droneRef = (0, import_react6.useRef)(null);
|
|
870
|
+
const narrationRef = (0, import_react6.useRef)(null);
|
|
871
|
+
const reducedMotion = useReducedMotion();
|
|
872
|
+
const [mobileViewport, setMobileViewport] = (0, import_react6.useState)(false);
|
|
873
|
+
const stateRef = (0, import_react6.useRef)(createInitialNarrativeState());
|
|
874
|
+
(0, import_react6.useEffect)(() => {
|
|
875
|
+
const media = window.matchMedia("(max-width: 900px)");
|
|
876
|
+
const update = () => setMobileViewport(media.matches);
|
|
877
|
+
update();
|
|
878
|
+
media.addEventListener("change", update);
|
|
879
|
+
return () => media.removeEventListener("change", update);
|
|
880
|
+
}, []);
|
|
881
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
882
|
+
if (!rootRef.current) {
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
const root = rootRef.current;
|
|
886
|
+
const webglDisabled = reducedMotion || mobileViewport;
|
|
887
|
+
const state = stateRef.current;
|
|
888
|
+
const updateLayers = () => {
|
|
889
|
+
var _a, _b, _c;
|
|
890
|
+
root.style.setProperty("--phone-opacity", state.phoneOpacity.toFixed(4));
|
|
891
|
+
root.style.setProperty("--phone-mask-open", state.phoneMaskOpen.toFixed(4));
|
|
892
|
+
root.style.setProperty("--phone-tilt", `${state.phoneTilt.toFixed(3)}deg`);
|
|
893
|
+
root.style.setProperty("--phone-scale", state.phoneScale.toFixed(4));
|
|
894
|
+
root.style.setProperty("--portal-bloom", state.portalBloom.toFixed(4));
|
|
895
|
+
root.style.setProperty("--ui-pulse", state.uiPulse.toFixed(4));
|
|
896
|
+
root.style.setProperty("--emerge", state.emergeT.toFixed(4));
|
|
897
|
+
root.style.setProperty("--flight-progress", state.flightT.toFixed(4));
|
|
898
|
+
root.style.setProperty("--flight-depth", state.flightDepth.toFixed(4));
|
|
899
|
+
root.style.setProperty("--hover", state.hover.toFixed(4));
|
|
900
|
+
root.style.setProperty("--drop", state.drop.toFixed(4));
|
|
901
|
+
root.style.setProperty("--cloud-far", state.cloudFarShift.toFixed(4));
|
|
902
|
+
root.style.setProperty("--cloud-near", state.cloudNearShift.toFixed(4));
|
|
903
|
+
root.style.setProperty("--ground-rise", state.groundRise.toFixed(4));
|
|
904
|
+
root.style.setProperty("--kiosk-reveal", state.kioskReveal.toFixed(4));
|
|
905
|
+
root.style.setProperty("--active-heading", state.activeHeading.toFixed(4));
|
|
906
|
+
(_a = narrationRef.current) == null ? void 0 : _a.setActiveHeading(state.activeHeading);
|
|
907
|
+
root.style.setProperty("--exit", state.exit.toFixed(4));
|
|
908
|
+
if (!webglDisabled) {
|
|
909
|
+
const width = root.clientWidth;
|
|
910
|
+
const height = root.clientHeight;
|
|
911
|
+
(_b = droneRef.current) == null ? void 0 : _b.resize(width, height, window.devicePixelRatio || 1);
|
|
912
|
+
(_c = droneRef.current) == null ? void 0 : _c.setState(state);
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
const timeline = createMasterTimeline(root, state, updateLayers);
|
|
916
|
+
const renderFromTicker = () => {
|
|
917
|
+
var _a;
|
|
918
|
+
if (!webglDisabled) {
|
|
919
|
+
(_a = droneRef.current) == null ? void 0 : _a.renderFrame();
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
import_gsap2.default.ticker.add(renderFromTicker);
|
|
923
|
+
updateLayers();
|
|
924
|
+
const onResize = () => {
|
|
925
|
+
var _a;
|
|
926
|
+
updateLayers();
|
|
927
|
+
(_a = timeline.scrollTrigger) == null ? void 0 : _a.refresh();
|
|
928
|
+
};
|
|
929
|
+
window.addEventListener("resize", onResize);
|
|
930
|
+
return () => {
|
|
931
|
+
var _a;
|
|
932
|
+
window.removeEventListener("resize", onResize);
|
|
933
|
+
import_gsap2.default.ticker.remove(renderFromTicker);
|
|
934
|
+
(_a = timeline.scrollTrigger) == null ? void 0 : _a.kill();
|
|
935
|
+
timeline.kill();
|
|
936
|
+
};
|
|
937
|
+
}, [mobileViewport, reducedMotion]);
|
|
938
|
+
const showWebgl = !reducedMotion && !mobileViewport;
|
|
939
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { ref: rootRef, className: "zipline-scroll-scene", children: [
|
|
940
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "zipline-dom-layer", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NarrationRevolver, { ref: narrationRef }) }),
|
|
941
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
942
|
+
"svg",
|
|
943
|
+
{
|
|
944
|
+
className: "zipline-svg-layer",
|
|
945
|
+
viewBox: "0 0 1440 900",
|
|
946
|
+
preserveAspectRatio: "xMidYMid slice",
|
|
947
|
+
"aria-hidden": "true",
|
|
948
|
+
children: [
|
|
949
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("linearGradient", { id: "zip-sky", x1: "0%", y1: "0%", x2: "0%", y2: "100%", children: [
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("stop", { offset: "0%", stopColor: "#a9d4ff" }),
|
|
951
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("stop", { offset: "52%", stopColor: "#d5ebff" }),
|
|
952
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("stop", { offset: "100%", stopColor: "#f5f9ff" })
|
|
953
|
+
] }) }),
|
|
954
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { className: "zipline-sky", x: "0", y: "0", width: "1440", height: "900", fill: "url(#zip-sky)" }),
|
|
955
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { className: "zipline-cloud-far", children: [
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ellipse", { cx: "1100", cy: "225", rx: "360", ry: "128", fill: "rgba(255,255,255,0.35)" }),
|
|
957
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ellipse", { cx: "540", cy: "272", rx: "300", ry: "106", fill: "rgba(255,255,255,0.3)" }),
|
|
958
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ellipse", { cx: "880", cy: "412", rx: "460", ry: "132", fill: "rgba(255,255,255,0.28)" })
|
|
959
|
+
] }),
|
|
960
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { className: "zipline-cloud-near", children: [
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ellipse", { cx: "360", cy: "610", rx: "350", ry: "110", fill: "rgba(255,255,255,0.45)" }),
|
|
962
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ellipse", { cx: "920", cy: "620", rx: "320", ry: "92", fill: "rgba(255,255,255,0.4)" }),
|
|
963
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ellipse", { cx: "1220", cy: "540", rx: "230", ry: "74", fill: "rgba(255,255,255,0.34)" })
|
|
964
|
+
] }),
|
|
965
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { className: "zipline-ground", x: "0", y: "705", width: "1440", height: "240" })
|
|
966
|
+
]
|
|
967
|
+
}
|
|
968
|
+
),
|
|
969
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "zipline-phone-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PhoneUI_default, {}) }),
|
|
970
|
+
showWebgl ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DroneScene, { ref: droneRef, className: "zipline-webgl-layer" }) : null,
|
|
971
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "zipline-mobile-fallback", "aria-hidden": showWebgl, children: [
|
|
972
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "zipline-fallback-drone" }),
|
|
973
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "zipline-fallback-package" })
|
|
974
|
+
] })
|
|
975
|
+
] });
|
|
976
|
+
}
|
|
977
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
978
|
+
0 && (module.exports = {
|
|
979
|
+
ScrollScene
|
|
980
|
+
});
|
|
981
|
+
//# sourceMappingURL=index.cjs.map
|