1// Une seule position pilote l'objet, la caméra, les masques et les démonstrations. 2export const clamp = (n, a = 0, b = 1) => Math.max(a, Math.min(b, n)); 3export const lerp = (a, b, t) => a + (b - a) * t; 4// Jeton hérité --ease-in-out: cubic-bezier(.77, 0, .175, 1). 5export function ease(x) { 6 x = clamp(x); let lo = 0, hi = 1, t = x; 7 if (x <= 0 || x >= 1) return x; 8 for (let i = 0; i < 22; i++) { 9 t = (lo + hi) / 2; const u = 1 - t; 10 if (3 * u * u * t * .77 + 3 * u * t * t * .175 + t * t * t < x) lo = t; else hi = t; 11 } 12 return 3 * (1 - t) * t * t + t * t * t; 13} 14export const phase = (p, a, b) => ease((p - a) / (b - a)); 15export const windowAt = (p, a, b, c, d) => phase(p, a, b) * (1 - phase(p, c, d)); 16// Keep the opening copy above the projected screenshot, including its idle tilt. 17export function openingProductFrame(copyBottom, height, scale, mobile = false) { 18 const shortMobile=mobile&&height<740; 19 const top = copyBottom + (shortMobile?12:24); 20 const bottom = Math.max(top, height - (mobile ? shortMobile?64:128 : 72)); 21 return {y: (top + bottom) / 2 / height * 100, scale: Math.min(scale, (bottom - top) / (625 * 1.18))}; 22} 23export const chapters = [ 24 {id: 'application', at: 0, label: 'Lâapplication', title: 'Stelliverse.', text: 'Votre compagnon pour Star Citizen.'}, 25 {id: 'preparer', at: .235, label: 'Préparer', title: 'Le départ se prépare ici.', text: 'Votre hangar. Le bon équipement. Vos objectifs. Avant même de rejoindre le Verse.'}, 26 {id: 'hub', at: .393, label: 'En jeu', title: 'Tout se replie. Rien ne vous quitte.', text: 'Une touche : le hub sâouvre. Vos outils sont toujours à portée de main.'}, 27 {id: 'prix', at: .524, label: 'Comparer', title: 'Une recherche. Une décision.', text: 'Quantanium. Les offres apparaissent. Vous comparez les lieux, puis gardez la main sur votre choix.'}, 28 {id: 'missions', at: .68, label: 'Organiser', title: 'Chaque arrêt a une raison.', text: 'Prendre ici. Livrer là . Regroupez vos étapes et voyez ce quâil reste à faire.'}, 29 {id: 'carte', at: .79, label: 'Se repérer', title: 'Le lieu devient un monde.', text: 'Les noms prennent leur place dans la carte stellaire. Retrouvez vos repères et votre destination.'}, 30 {id: 'ensemble', at: .94, label: 'Votre session', title: 'Tout votre Verse. Un seul compagnon.', text: 'Minage, Wikelo, missions, amis, traductions⦠Toute la session se retrouve dans Stelliverse.'} 31]; 32const poses = [ 33 [0, 50, 61, 1, 7, -8, -2], [.07, 50, 60, 1.12, 2, -2, 0], 34 [.17, 67, 57, .91, 4, -17, 1], [.26, 67, 57, .98, 0, -7, 0], 35 [.315, 63, 53, .95, 0, 0, 0], [.385, 65, 57, 1.05, 0, -6, 0], 36 [.465, 66, 58, .96, 2, -10, 0], [.575, 65, 58, 1.03, 0, -3, 0], 37 [.665, 66, 57, 1.02, 0, -7, 0], [.705, 63, 57, .91, 6, -15, -2], 38 [.78, 34, 69, .58, 14, 18, -5], [.85, 34, 69, .58, 6, 8, -2], 39 [.925, 67, 57, .86, 4, -9, 0], [1, 67, 57, .93, 0, -3, 0] 40]; 41export function poseAt(progress) { 42 const p = clamp(progress); 43 let i = poses.findIndex((k, n) => n < poses.length - 1 && p <= poses[n + 1][0]); 44 if (i < 0) i = poses.length - 2; 45 const a = poses[i], b = poses[i + 1], t = phase(p, a[0], b[0]); 46 return {x: lerp(a[1], b[1], t), y: lerp(a[2], b[2], t), scale: lerp(a[3], b[3], t), rx: lerp(a[4], b[4], t), ry: lerp(a[5], b[5], t), rz: lerp(a[6], b[6], t)}; 47} 48export function sceneAt(p) { 49 return p < .125 ? 0 : p < .29 ? 1 : p < .425 ? 2 : p < .605 ? 3 : p < .725 ? 4 : p < .885 ? 5 : 6; 50} 51export function presentationPose(p, mobile = false) { 52 const pose = poseAt(p); 53 if (!mobile) return pose; 54 // Keep the full-width mobile preview; only desktop moves beside the planet. 55 const mapScale=.40*windowAt(p,.705,.78,.85,.925); 56 return {...pose, x: 50, y: 58 + 3 * phase(p, .085, .16) + 5 * phase(p, .86, .925), scale:pose.scale+mapScale}; 57}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.