scrollcraft 2.0.5 → 2.0.8
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 +61 -21
- package/dist/cli/fal-service.js +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +148 -72
- package/dist/cli/processor.d.ts +1 -0
- package/dist/cli/processor.js +13 -6
- package/dist/core/CoreEngine.d.ts +31 -13
- package/dist/core/index.d.ts +3 -0
- package/dist/core/scrollcraft.umd.min.js +1 -1
- package/dist/core/types.d.ts +6 -76
- package/dist/core/types.js +1 -1
- package/dist/pipeline/browser-driver.d.ts +31 -0
- package/dist/pipeline/browser-driver.js +176 -0
- package/dist/pipeline/fal-service.d.ts +15 -0
- package/dist/pipeline/fal-service.js +101 -0
- package/dist/pipeline/index.d.ts +21 -0
- package/dist/pipeline/index.js +223 -0
- package/dist/pipeline/node-driver.d.ts +18 -0
- package/dist/pipeline/node-driver.js +108 -0
- package/dist/pipeline/types.d.ts +43 -0
- package/dist/pipeline/types.js +2 -0
- package/dist/react/ScrollCraftProvider.d.ts +9 -8
- package/dist/react/index.js +1 -1
- package/package.json +20 -19
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { IPipelineDriver, VariantConfig } from './types';
|
|
2
|
+
export declare class NodeDriver implements IPipelineDriver {
|
|
3
|
+
private ffmpegPath;
|
|
4
|
+
constructor();
|
|
5
|
+
readFile(filePath: string): Promise<Uint8Array>;
|
|
6
|
+
writeFile(filePath: string, data: Uint8Array | string): Promise<void>;
|
|
7
|
+
mkdir(dirPath: string): Promise<void>;
|
|
8
|
+
exists(filePath: string): Promise<boolean>;
|
|
9
|
+
readdir(dirPath: string): Promise<string[]>;
|
|
10
|
+
remove(filePath: string): Promise<void>;
|
|
11
|
+
join(...parts: string[]): string;
|
|
12
|
+
resolve(...parts: string[]): string;
|
|
13
|
+
extractFrames(videoSource: string, outputDir: string, onProgress?: (percent: number) => void): Promise<void>;
|
|
14
|
+
processImage(input: Uint8Array | string, config: VariantConfig, options?: {
|
|
15
|
+
grayscale?: boolean;
|
|
16
|
+
blur?: number;
|
|
17
|
+
}): Promise<Uint8Array>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.NodeDriver = void 0;
|
|
40
|
+
const fs = __importStar(require("fs-extra"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const sharp_1 = __importDefault(require("sharp"));
|
|
43
|
+
const child_process_1 = require("child_process");
|
|
44
|
+
const ffmpeg_static_1 = __importDefault(require("ffmpeg-static"));
|
|
45
|
+
class NodeDriver {
|
|
46
|
+
ffmpegPath;
|
|
47
|
+
constructor() {
|
|
48
|
+
this.ffmpegPath = ffmpeg_static_1.default || 'ffmpeg';
|
|
49
|
+
}
|
|
50
|
+
async readFile(filePath) {
|
|
51
|
+
return await fs.readFile(filePath);
|
|
52
|
+
}
|
|
53
|
+
async writeFile(filePath, data) {
|
|
54
|
+
if (typeof data === 'string') {
|
|
55
|
+
await fs.writeFile(filePath, data);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
await fs.writeFile(filePath, Buffer.from(data));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async mkdir(dirPath) {
|
|
62
|
+
await fs.ensureDir(dirPath);
|
|
63
|
+
}
|
|
64
|
+
async exists(filePath) {
|
|
65
|
+
return await fs.pathExists(filePath);
|
|
66
|
+
}
|
|
67
|
+
async readdir(dirPath) {
|
|
68
|
+
return await fs.readdir(dirPath);
|
|
69
|
+
}
|
|
70
|
+
async remove(filePath) {
|
|
71
|
+
await fs.remove(filePath);
|
|
72
|
+
}
|
|
73
|
+
join(...parts) {
|
|
74
|
+
return path.join(...parts);
|
|
75
|
+
}
|
|
76
|
+
resolve(...parts) {
|
|
77
|
+
return path.resolve(...parts);
|
|
78
|
+
}
|
|
79
|
+
async extractFrames(videoSource, outputDir, onProgress) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
// For simplicity, we use execSync in a promise or spawn for progress
|
|
82
|
+
try {
|
|
83
|
+
// ffmpeg -i input output%04d.png
|
|
84
|
+
// For now, let's keep it simple like existing CLI
|
|
85
|
+
(0, child_process_1.execSync)(`"${this.ffmpegPath}" -i "${videoSource}" "${outputDir}/frame_%04d.png"`, { stdio: 'inherit' });
|
|
86
|
+
resolve();
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
reject(err);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async processImage(input, config, options = {}) {
|
|
94
|
+
let pipeline = (0, sharp_1.default)(input)
|
|
95
|
+
.resize(config.width, config.height, {
|
|
96
|
+
fit: 'cover',
|
|
97
|
+
position: 'center' // Placeholder for smart-crop logic if we move it here
|
|
98
|
+
});
|
|
99
|
+
if (options.grayscale) {
|
|
100
|
+
pipeline = pipeline.grayscale();
|
|
101
|
+
}
|
|
102
|
+
if (options.blur) {
|
|
103
|
+
pipeline = pipeline.blur(options.blur);
|
|
104
|
+
}
|
|
105
|
+
return await pipeline.webp({ quality: 80 }).toBuffer();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.NodeDriver = NodeDriver;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface PipelineProgress {
|
|
2
|
+
percent: number;
|
|
3
|
+
message: string;
|
|
4
|
+
step: 'initializing' | 'extracting' | 'tracking' | 'depth' | 'processing' | 'saving';
|
|
5
|
+
}
|
|
6
|
+
export interface VariantConfig {
|
|
7
|
+
id: string;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
media: string;
|
|
11
|
+
orientation: 'portrait' | 'landscape';
|
|
12
|
+
aspectRatio: string;
|
|
13
|
+
}
|
|
14
|
+
export interface IPipelineDriver {
|
|
15
|
+
readFile(path: string): Promise<Uint8Array>;
|
|
16
|
+
writeFile(path: string, data: Uint8Array | string): Promise<void>;
|
|
17
|
+
mkdir(path: string): Promise<void>;
|
|
18
|
+
exists(path: string): Promise<boolean>;
|
|
19
|
+
readdir(path: string): Promise<string[]>;
|
|
20
|
+
remove(path: string): Promise<void>;
|
|
21
|
+
join(...parts: string[]): string;
|
|
22
|
+
resolve(...parts: string[]): string;
|
|
23
|
+
extractFrames(videoSource: string | File | Blob, outputDir: string, onProgress?: (percent: number) => void): Promise<void>;
|
|
24
|
+
processImage(input: Uint8Array | string, config: VariantConfig, options: {
|
|
25
|
+
grayscale?: boolean;
|
|
26
|
+
blur?: number;
|
|
27
|
+
}): Promise<Uint8Array>;
|
|
28
|
+
zipProject?(outDir: string): Promise<Uint8Array>;
|
|
29
|
+
}
|
|
30
|
+
export interface PipelineOptions {
|
|
31
|
+
apiKey?: string;
|
|
32
|
+
proxyUrl?: string;
|
|
33
|
+
onProgress?: (progress: PipelineProgress) => void;
|
|
34
|
+
}
|
|
35
|
+
export interface CreateCommandOptions {
|
|
36
|
+
input: string | File | Blob;
|
|
37
|
+
name: string;
|
|
38
|
+
track?: string;
|
|
39
|
+
hasDepth?: boolean;
|
|
40
|
+
variants: number[] | VariantConfig[];
|
|
41
|
+
step?: number;
|
|
42
|
+
outputZip?: boolean;
|
|
43
|
+
}
|
|
@@ -4,21 +4,22 @@ import { ProjectConfiguration } from '../core/types';
|
|
|
4
4
|
interface SCFTContext {
|
|
5
5
|
progress: number;
|
|
6
6
|
frame: number;
|
|
7
|
-
subjectCoords: {
|
|
8
|
-
x: number;
|
|
9
|
-
y: number;
|
|
10
|
-
};
|
|
11
7
|
engine: CoreEngine | null;
|
|
12
8
|
}
|
|
13
|
-
export
|
|
14
|
-
project: ProjectConfiguration;
|
|
9
|
+
export interface ScrollCraftProviderProps {
|
|
10
|
+
project: ProjectConfiguration | string;
|
|
15
11
|
children: React.ReactNode;
|
|
16
|
-
|
|
12
|
+
containerHeight?: string;
|
|
13
|
+
canvasHeight?: string;
|
|
14
|
+
offset?: any;
|
|
15
|
+
scrub?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare const ScrollCraftProvider: React.FC<ScrollCraftProviderProps>;
|
|
17
18
|
export declare const ScrollCraftCanvas: React.FC<{
|
|
18
|
-
assetId?: string;
|
|
19
19
|
style?: React.CSSProperties;
|
|
20
20
|
}>;
|
|
21
21
|
export declare const SubjectLayer: React.FC<{
|
|
22
|
+
id?: string;
|
|
22
23
|
offset?: {
|
|
23
24
|
x: number;
|
|
24
25
|
y: number;
|
package/dist/react/index.js
CHANGED
|
@@ -11,4 +11,4 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):"object"==typeof exports?exports.ScrollCraftReact=e(require("react")):t.ScrollCraftReact=e(t.react)}(this,t=>(()=>{"use strict";var e={197(t,e){var i=Symbol.for("react.transitional.element");function r(t,e,r){var s=null;if(void 0!==r&&(s=""+r),void 0!==e.key&&(s=""+e.key),"key"in e)for(var a in r={},e)"key"!==a&&(r[a]=e[a]);else r=e;return e=r.ref,{$$typeof:i,type:t,key:s,ref:void 0!==e?e:null,props:r}}Symbol.for("react.fragment"),e.jsx=r},85(t,e,i){t.exports=i(197)},155(e){e.exports=t}},i={};function r(t){var s=i[t];if(void 0!==s)return s.exports;var a=i[t]={exports:{}};return e[t](a,a.exports,r),a.exports}r.d=(t,e)=>{for(var i in e)r.o(e,i)&&!r.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var s={};r.r(s),r.d(s,{ScrollCraftCanvas:()=>u,ScrollCraftProvider:()=>c,SubjectLayer:()=>g,useScrollCraft:()=>d});var a=r(85),n=r(155);class o{constructor(t){if(this.targetMouse={x:0,y:0},this.currentMouse={x:0,y:0},this.animationFrameId=0,this.animate=()=>{if(this.currentMouse.x+=.1*(this.targetMouse.x-this.currentMouse.x),this.currentMouse.y+=.1*(this.targetMouse.y-this.currentMouse.y),this.gl&&this.program){this.gl.useProgram(this.program);const t=this.gl.getUniformLocation(this.program,"u_mouse");this.gl.uniform2f(t,this.currentMouse.x,this.currentMouse.y),this.draw()}this.animationFrameId=requestAnimationFrame(this.animate)},this.gl=t.getContext("webgl",{alpha:!1,antialias:!1}),!this.gl)throw new Error("WebGL not supported");this.program=this.createProgram("\n attribute vec2 a_position;\n varying vec2 v_texCoord;\n void main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n // Convert -1 -> 1 to 0 -> 1 for UVs\n v_texCoord = a_position * 0.5 + 0.5;\n v_texCoord.y = 1.0 - v_texCoord.y;\n }\n ","\n precision mediump float;\n uniform sampler2D u_image;\n uniform sampler2D u_depthMap;\n uniform vec2 u_resolution;\n uniform vec2 u_imageResolution;\n uniform vec2 u_mouse;\n uniform bool u_hasDepth;\n varying vec2 v_texCoord;\n\n void main() {\n // object-fit: cover math\n vec2 ratio = vec2(\n min((u_resolution.x / u_resolution.y) / (u_imageResolution.x / u_imageResolution.y), 1.0),\n min((u_resolution.y / u_resolution.x) / (u_imageResolution.y / u_imageResolution.x), 1.0)\n );\n vec2 uv = vec2(\n v_texCoord.x * ratio.x + (1.0 - ratio.x) * 0.5,\n v_texCoord.y * ratio.y + (1.0 - ratio.y) * 0.5\n );\n\n if (u_hasDepth) {\n float depth = texture2D(u_depthMap, uv).r;\n // White is close (1), Black is far (0). We move close objects more.\n vec2 parallax = u_mouse * depth * 0.04;\n uv += parallax;\n }\n \n gl_FragColor = texture2D(u_image, uv);\n }\n "),this.gl.useProgram(this.program),this.positionBuffer=this.gl.createBuffer(),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),this.gl.STATIC_DRAW),this.texture=this.gl.createTexture(),this.depthTexture=this.gl.createTexture(),window.addEventListener("mousemove",t=>{this.targetMouse.x=t.clientX/window.innerWidth*2-1,this.targetMouse.y=-t.clientY/window.innerHeight*2+1}),this.animate()}createProgram(t,e){const i=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(i,t),this.gl.compileShader(i);const r=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(r,e),this.gl.compileShader(r);const s=this.gl.createProgram();return this.gl.attachShader(s,i),this.gl.attachShader(s,r),this.gl.linkProgram(s),s}render(t,e,i,r){this.gl.useProgram(this.program),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,t),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.activeTexture(this.gl.TEXTURE1),this.gl.bindTexture(this.gl.TEXTURE_2D,this.depthTexture),e&&(this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,e),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR)),this.gl.uniform1i(this.gl.getUniformLocation(this.program,"u_image"),0),this.gl.uniform1i(this.gl.getUniformLocation(this.program,"u_depthMap"),1),this.gl.uniform1i(this.gl.getUniformLocation(this.program,"u_hasDepth"),e?1:0),this.gl.uniform2f(this.gl.getUniformLocation(this.program,"u_resolution"),i,r),this.gl.uniform2f(this.gl.getUniformLocation(this.program,"u_imageResolution"),t.naturalWidth,t.naturalHeight);const s=this.gl.getAttribLocation(this.program,"a_position");this.gl.enableVertexAttribArray(s),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.gl.vertexAttribPointer(s,2,this.gl.FLOAT,!1,0,0),this.gl.viewport(0,0,i,r),this.draw()}draw(){this.gl.drawArrays(this.gl.TRIANGLES,0,6)}destroy(){cancelAnimationFrame(this.animationFrameId)}}class h{constructor(t){this.currentFrame=-1,this.activeVariant=null,this.canvas=null,this.ctx=null,this.renderer=null,this.imageCache=new Map,this.depthCache=new Map,this.scrollTimeout=null,this.config=t,this.detectBestVariant(),window.addEventListener("resize",()=>{this.detectBestVariant(),this.resizeCanvas(),this.render()})}attachCanvas(t){this.canvas=t;try{this.renderer=new o(t)}catch(e){console.warn("WebGL failed, falling back to 2D",e),this.ctx=t.getContext("2d",{alpha:!1})}this.resizeCanvas(),this.render()}resizeCanvas(){if(!this.canvas)return;const t=window.innerWidth,e=window.innerHeight,i=window.devicePixelRatio||1;this.canvas.width=t*i,this.canvas.height=e*i,this.ctx&&this.ctx.scale(i,i)}detectBestVariant(){var t;const e=this.config.assets[0];if(!e)return;const i=e.variants.find(t=>window.matchMedia(t.media).matches)||e.variants[0];i?(null===(t=this.activeVariant)||void 0===t?void 0:t.id)!==i.id&&(console.log(`🎯 Variant Switched: ${i.id}`),this.activeVariant=i,console.log("[CoreEngine] Variant hasDepthMap:",this.activeVariant.hasDepthMap),this.clearCache(),this.preloadInitial()):console.warn("[CoreEngine] No best match found")}clearCache(){this.imageCache.clear(),this.depthCache.clear()}preloadInitial(){for(let t=0;t<15;t++)this.getImage(t)}update(t){const e=this.config.timeline.scenes[0];if(!e)return;const i=e.assetRange[1]-e.assetRange[0],r=Math.floor(e.assetRange[0]+t*i);return r!==this.currentFrame&&(this.currentFrame=r,this.render(),this.getImage(this.currentFrame+5),this.getImage(this.currentFrame+10),this.scrollTimeout&&clearTimeout(this.scrollTimeout),this.scrollTimeout=setTimeout(()=>{this.loadDepthMap(this.currentFrame)},100)),{frame:this.currentFrame,subjectCoords:this.getSubjectCoords(this.currentFrame)}}getSubjectCoords(t){var e;if(!(null===(e=this.activeVariant)||void 0===e?void 0:e.subjectTracking))return{x:.5,y:.5};const i=this.activeVariant.subjectTracking.find(e=>e.frame===t);return i?{x:i.x,y:i.y}:{x:.5,y:.5}}render(){var t;if(!this.canvas||-1===this.currentFrame)return;const e=this.getImage(this.currentFrame);if(!e||!e.complete)return;const i=window.innerWidth,r=window.innerHeight;let s=null;if((null===(t=this.activeVariant)||void 0===t?void 0:t.hasDepthMap)&&(s=this.getDepthImage(this.currentFrame),s&&!s.complete&&(s=null)),this.renderer)this.renderer.render(e,s,i*(window.devicePixelRatio||1),r*(window.devicePixelRatio||1));else if(this.ctx){const t=e.naturalWidth/e.naturalHeight;let s,a,n,o;t>i/r?(a=r,s=r*t,n=(i-s)/2,o=0):(s=i,a=i/t,n=0,o=(r-a)/2),this.ctx.clearRect(0,0,i,r),this.ctx.drawImage(e,n,o,s,a)}}getImage(t){if(!this.activeVariant)return null;if(t<0||t>=this.activeVariant.frameCount)return null;const e=`${this.activeVariant.id}_${t}`;if(this.imageCache.has(e))return this.imageCache.get(e);const i=new Image;return i.crossOrigin="anonymous",i.src=`${this.activeVariant.path}/index_${t}.webp`,i.onload=()=>{this.currentFrame===t&&this.render()},this.imageCache.set(e,i),i}loadDepthMap(t){var e;if(!(null===(e=this.activeVariant)||void 0===e?void 0:e.hasDepthMap))return void console.log("[CoreEngine] activeVariant does not define hasDepthMap=true");console.log(`[CoreEngine] Lazy requesting depth map for frame: ${t}`);this.getDepthImage(t)}getDepthImage(t){var e;if(!(null===(e=this.activeVariant)||void 0===e?void 0:e.hasDepthMap))return null;if(t<0||t>=this.activeVariant.frameCount)return null;const i=`${this.activeVariant.id}_depth_${t}`;if(this.depthCache.has(i))return this.depthCache.get(i);console.log(`[CoreEngine] Downloading: ${this.activeVariant.path}/index_${t}_depth.webp`);const r=new Image;return r.crossOrigin="anonymous",r.src=`${this.activeVariant.path}/index_${t}_depth.webp`,r.onload=()=>{console.log(`[CoreEngine] Depth map loaded for frame: ${t}`),this.currentFrame===t&&this.render()},r.onerror=e=>{console.error(`[CoreEngine] Depth map failed to load for frame: ${t}`,e)},this.depthCache.set(i,r),r}}const l=(0,n.createContext)(null),c=({project:t,children:e})=>{const[i,r]=(0,n.useState)({progress:0,frame:-1,subjectCoords:{x:.5,y:.5}}),s=(0,n.useRef)(null);return(0,n.useEffect)(()=>{const e=new h(t);s.current=e;const i=()=>{const t=window.scrollY,i=document.body.scrollHeight-window.innerHeight,s=i<=0?0:Math.max(0,Math.min(1,t/i)),a=e.update(s);a&&r({progress:s,frame:a.frame,subjectCoords:a.subjectCoords})};return window.addEventListener("scroll",i,{passive:!0}),setTimeout(i,100),()=>window.removeEventListener("scroll",i)},[t]),(0,a.jsx)(l.Provider,{value:Object.assign(Object.assign({},i),{engine:s.current}),children:(0,a.jsx)("div",{className:"scft-wrapper",style:{position:"relative"},children:e})})},u=({style:t})=>{const e=(0,n.useContext)(l),i=(0,n.useRef)(null);return(0,n.useEffect)(()=>{i.current&&(null==e?void 0:e.engine)&&e.engine.attachCanvas(i.current)},[null==e?void 0:e.engine]),(0,a.jsx)("canvas",{ref:i,style:Object.assign({width:"100%",height:"100%",display:"block"},t)})},g=({offset:t={x:0,y:0},children:e})=>{const i=(0,n.useContext)(l);if(!i||-1===i.frame)return null;const r={position:"fixed",left:`${100*i.subjectCoords.x+t.x}%`,top:`${100*i.subjectCoords.y+t.y}%`,transform:"translate(-50%, -50%)",pointerEvents:"auto",zIndex:10,transition:"left 0.1s linear, top 0.1s linear"};return(0,a.jsx)("div",{style:r,children:e})},d=()=>{const t=(0,n.useContext)(l);if(!t)throw new Error("useScrollCraft must be used within a ScrollCraftProvider");return t};return s})());
|
|
14
|
+
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react")):"function"==typeof define&&define.amd?define(["react"],e):"object"==typeof exports?exports.ScrollCraftReact=e(require("react")):t.ScrollCraftReact=e(t.react)}(this,t=>(()=>{"use strict";var e={197(t,e){var n=Symbol.for("react.transitional.element");function r(t,e,r){var i=null;if(void 0!==r&&(i=""+r),void 0!==e.key&&(i=""+e.key),"key"in e)for(var s in r={},e)"key"!==s&&(r[s]=e[s]);else r=e;return e=r.ref,{$$typeof:n,type:t,key:i,ref:void 0!==e?e:null,props:r}}Symbol.for("react.fragment"),e.jsx=r},85(t,e,n){t.exports=n(197)},155(e){e.exports=t}},n={};function r(t){var i=n[t];if(void 0!==i)return i.exports;var s=n[t]={exports:{}};return e[t](s,s.exports,r),s.exports}r.d=(t,e)=>{for(var n in e)r.o(e,n)&&!r.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var i={};r.r(i),r.d(i,{ScrollCraftCanvas:()=>ne,ScrollCraftProvider:()=>ee,SubjectLayer:()=>re,useScrollCraft:()=>ie});var s=r(85),o=r(155);class a{constructor(t){if(this.targetMouse={x:0,y:0},this.currentMouse={x:0,y:0},this.animationFrameId=0,this.animate=()=>{if(this.currentMouse.x+=.1*(this.targetMouse.x-this.currentMouse.x),this.currentMouse.y+=.1*(this.targetMouse.y-this.currentMouse.y),this.gl&&this.program){this.gl.useProgram(this.program);const t=this.gl.getUniformLocation(this.program,"u_mouse");this.gl.uniform2f(t,this.currentMouse.x,this.currentMouse.y),this.draw()}this.animationFrameId=requestAnimationFrame(this.animate)},this.gl=t.getContext("webgl",{alpha:!1,antialias:!1}),!this.gl)throw new Error("WebGL not supported");this.program=this.createProgram("\n attribute vec2 a_position;\n varying vec2 v_texCoord;\n void main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n // Convert -1 -> 1 to 0 -> 1 for UVs\n v_texCoord = a_position * 0.5 + 0.5;\n v_texCoord.y = 1.0 - v_texCoord.y;\n }\n ","\n precision mediump float;\n uniform sampler2D u_image;\n uniform sampler2D u_depthMap;\n uniform vec2 u_resolution;\n uniform vec2 u_imageResolution;\n uniform vec2 u_mouse;\n uniform bool u_hasDepth;\n varying vec2 v_texCoord;\n\n void main() {\n // object-fit: cover math\n vec2 ratio = vec2(\n min((u_resolution.x / u_resolution.y) / (u_imageResolution.x / u_imageResolution.y), 1.0),\n min((u_resolution.y / u_resolution.x) / (u_imageResolution.y / u_imageResolution.x), 1.0)\n );\n vec2 uv = vec2(\n v_texCoord.x * ratio.x + (1.0 - ratio.x) * 0.5,\n v_texCoord.y * ratio.y + (1.0 - ratio.y) * 0.5\n );\n\n if (u_hasDepth) {\n float depth = texture2D(u_depthMap, uv).r;\n // White is close (1), Black is far (0). We move close objects more.\n vec2 parallax = u_mouse * depth * 0.04;\n uv += parallax;\n }\n \n gl_FragColor = texture2D(u_image, uv);\n }\n "),this.gl.useProgram(this.program),this.positionBuffer=this.gl.createBuffer(),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),this.gl.STATIC_DRAW),this.texture=this.gl.createTexture(),this.depthTexture=this.gl.createTexture(),window.addEventListener("mousemove",t=>{this.targetMouse.x=t.clientX/window.innerWidth*2-1,this.targetMouse.y=-t.clientY/window.innerHeight*2+1}),this.animate()}createProgram(t,e){const n=this.gl.createShader(this.gl.VERTEX_SHADER);this.gl.shaderSource(n,t),this.gl.compileShader(n);const r=this.gl.createShader(this.gl.FRAGMENT_SHADER);this.gl.shaderSource(r,e),this.gl.compileShader(r);const i=this.gl.createProgram();return this.gl.attachShader(i,n),this.gl.attachShader(i,r),this.gl.linkProgram(i),i}render(t,e,n,r){this.gl.useProgram(this.program),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,t),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.activeTexture(this.gl.TEXTURE1),this.gl.bindTexture(this.gl.TEXTURE_2D,this.depthTexture),e&&(this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,e),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR)),this.gl.uniform1i(this.gl.getUniformLocation(this.program,"u_image"),0),this.gl.uniform1i(this.gl.getUniformLocation(this.program,"u_depthMap"),1),this.gl.uniform1i(this.gl.getUniformLocation(this.program,"u_hasDepth"),e?1:0),this.gl.uniform2f(this.gl.getUniformLocation(this.program,"u_resolution"),n,r),this.gl.uniform2f(this.gl.getUniformLocation(this.program,"u_imageResolution"),t.naturalWidth,t.naturalHeight);const i=this.gl.getAttribLocation(this.program,"a_position");this.gl.enableVertexAttribArray(i),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.gl.vertexAttribPointer(i,2,this.gl.FLOAT,!1,0,0),this.gl.viewport(0,0,n,r),this.draw()}draw(){this.gl.drawArrays(this.gl.TRIANGLES,0,6)}destroy(){cancelAnimationFrame(this.animationFrameId)}}var c=function(t,e,n,r){return new(n||(n=Promise))(function(i,s){function o(t){try{c(r.next(t))}catch(t){s(t)}}function a(t){try{c(r.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?i(t.value):(e=t.value,e instanceof n?e:new n(function(t){t(e)})).then(o,a)}c((r=r.apply(t,e||[])).next())})};class l{constructor(t,e={}){this.currentFrame=-1,this.activeVariant=null,this.canvas=null,this.ctx=null,this.renderer=null,this.basePath="",this.scrub=0,this.targetProgress=0,this.currentProgress=0,this.rafId=0,this.destroyed=!1,this.imageCache=new Map,this.depthCache=new Map,this.scrollTimeout=null,this.trackingDataCache=new Map,this.config=t,this.basePath=t.settings.basePath||"",this.scrub=e.scrub||0,this.detectBestVariant(),this.boundResize=()=>{this.detectBestVariant(),this.resizeCanvas(),this.render()},window.addEventListener("resize",this.boundResize),this.updateLoop=this.updateLoop.bind(this),this.rafId=requestAnimationFrame(this.updateLoop)}destroy(){this.destroyed=!0,this.rafId&&cancelAnimationFrame(this.rafId),window.removeEventListener("resize",this.boundResize),this.scrollTimeout&&clearTimeout(this.scrollTimeout),this.clearCache(),this.trackingDataCache.clear(),this.canvas=null,this.ctx=null,this.renderer=null,this.onFrameChange=void 0}static init(t,e){return c(this,void 0,void 0,function*(){const n=yield fetch(e);if(!n.ok)throw new Error(`Failed to load config: ${n.statusText}`);const r=yield n.json(),i=e.substring(0,e.lastIndexOf("/"));r.settings||(r.settings={baseResolution:{width:1920,height:1080},scrollMode:"vh"}),r.settings.basePath=r.settings.basePath||i;const s=new l(r,{scrub:"object"==typeof r.settings?r.settings.scrub:0});let o=t.querySelector("canvas");return o||(o=document.createElement("canvas"),o.style.width="100%",o.style.height="100%",o.style.display="block",o.style.objectFit="cover",t.appendChild(o)),s.attachCanvas(o),s})}attachCanvas(t){this.canvas=t;try{this.renderer=new a(t)}catch(e){console.warn("WebGL failed, falling back to 2D",e),this.ctx=t.getContext("2d",{alpha:!1})}this.resizeCanvas(),this.render()}resizeCanvas(){if(!this.canvas)return;const t=window.innerWidth,e=window.innerHeight,n=window.devicePixelRatio||1;this.canvas.width=t*n,this.canvas.height=e*n,this.ctx&&this.ctx.scale(n,n)}detectBestVariant(){var t;const e=this.config.assets[0];if(!e)return;const n=this.canvas?this.canvas.getBoundingClientRect():{width:window.innerWidth,height:window.innerHeight},r=n.height>n.width,i=n.width*(window.devicePixelRatio||1),s=e.variants.filter(t=>{const e="portrait"===t.orientation||parseInt(t.aspectRatio.split(":")[1])>parseInt(t.aspectRatio.split(":")[0]);return r===e});s.sort((t,e)=>t.frameCount-e.frameCount);const o=s.find(t=>t.width>=i)||s[s.length-1];o?(null===(t=this.activeVariant)||void 0===t?void 0:t.id)!==o.id&&(console.log(`🎯 Variant Switched: ${o.id} (${r?"Portrait":"Landscape"})`),this.activeVariant=o,this.clearCache(),this.preloadInitial()):console.warn("[CoreEngine] No suitable variant found")}clearCache(){this.imageCache.clear(),this.depthCache.clear()}preloadInitial(){for(let t=0;t<15;t++)this.getImage(t)}update(t){this.targetProgress=Math.max(0,Math.min(1,t))}updateLoop(){if(this.destroyed)return;this.rafId=requestAnimationFrame(this.updateLoop);const t=this.scrub;if(t>0){const e=Math.max(.01,1-t);this.currentProgress+=(this.targetProgress-this.currentProgress)*e}else this.currentProgress=this.targetProgress;Math.abs(this.targetProgress-this.currentProgress)<1e-4&&(this.currentProgress=this.targetProgress),this.calculateFrame(this.currentProgress)}calculateFrame(t){const e=this.config.timeline.scenes[0];if(!e)return;const n=e.assetRange[1]-e.assetRange[0],r=Math.floor(e.assetRange[0]+t*n),i=Math.max(0,Math.min(r,e.assetRange[1]));i!==this.currentFrame&&(this.currentFrame=i,this.render(),this.getImage(this.currentFrame+5),this.getImage(this.currentFrame+10),this.scrollTimeout&&clearTimeout(this.scrollTimeout),this.scrollTimeout=setTimeout(()=>{this.loadDepthMap(this.currentFrame)},100),this.onFrameChange&&this.onFrameChange(this.currentFrame,t))}loadTrackingData(t){return c(this,void 0,void 0,function*(){var e;if(!this.activeVariant)return;if(!(null===(e=this.activeVariant.subjects)||void 0===e?void 0:e.includes(t)))return void console.warn(`[CoreEngine] Subject ${t} not found in active variant ${this.activeVariant.id}`);const n=`${this.activeVariant.id}_${t}`;if(!this.trackingDataCache.has(n))try{const e=`${this.basePath?`${this.basePath}/`:""}${this.activeVariant.path}/000_tracking-${t}.json`;console.log(`[CoreEngine] Fetching tracking data: ${e}`);const r=yield fetch(e);if(!r.ok)throw new Error(r.statusText);const i=yield r.json();this.trackingDataCache.set(n,i)}catch(e){console.error(`[CoreEngine] Failed to load tracking data for ${t}`,e)}})}getTrackedCoords(t,e){if(!this.activeVariant)return{x:.5,y:.5};const n=`${this.activeVariant.id}_${t}`,r=this.trackingDataCache.get(n);if(!r)return{x:.5,y:.5};const i=r.find(t=>t.frame===e);return i?{x:i.x,y:i.y,scale:i.scale}:{x:.5,y:.5}}render(){var t;if(!this.canvas||-1===this.currentFrame)return;const e=this.getImage(this.currentFrame);if(!e||!e.complete)return;const n=window.innerWidth,r=window.innerHeight;let i=null;if((null===(t=this.activeVariant)||void 0===t?void 0:t.hasDepthMap)&&(i=this.getDepthImage(this.currentFrame),i&&!i.complete&&(i=null)),this.renderer)this.renderer.render(e,i,n*(window.devicePixelRatio||1),r*(window.devicePixelRatio||1));else if(this.ctx){const t=e.naturalWidth/e.naturalHeight;let i,s,o,a;t>n/r?(s=r,i=r*t,o=(n-i)/2,a=0):(i=n,s=n/t,o=0,a=(r-s)/2),this.ctx.clearRect(0,0,n,r),this.ctx.drawImage(e,o,a,i,s)}}getImage(t){if(!this.activeVariant)return null;if(t<0||t>=this.activeVariant.frameCount)return null;const e=`${this.activeVariant.id}_${t}`;if(this.imageCache.has(e))return this.imageCache.get(e);const n=this.basePath?`${this.basePath}/`:"",r=new Image;return r.crossOrigin="anonymous",r.src=`${n}${this.activeVariant.path}/index_${t}.webp`,r.onload=()=>{this.currentFrame===t&&this.render()},this.imageCache.set(e,r),r}loadDepthMap(t){var e;if(!(null===(e=this.activeVariant)||void 0===e?void 0:e.hasDepthMap))return void console.log("[CoreEngine] activeVariant does not define hasDepthMap=true");console.log(`[CoreEngine] Lazy requesting depth map for frame: ${t}`);this.getDepthImage(t)}getDepthImage(t){var e;if(!(null===(e=this.activeVariant)||void 0===e?void 0:e.hasDepthMap))return null;if(t<0||t>=this.activeVariant.frameCount)return null;const n=`${this.activeVariant.id}_depth_${t}`;if(this.depthCache.has(n))return this.depthCache.get(n);const r=this.basePath?`${this.basePath}/`:"";console.log(`[CoreEngine] Downloading: ${r}${this.activeVariant.path}/index_${t}_depth.webp`);const i=new Image;return i.crossOrigin="anonymous",i.src=`${r}${this.activeVariant.path}/index_${t}_depth.webp`,i.onload=()=>{console.log(`[CoreEngine] Depth map loaded for frame: ${t}`),this.currentFrame===t&&this.render()},i.onerror=e=>{console.error(`[CoreEngine] Depth map failed to load for frame: ${t}`,e)},this.depthCache.set(n,i),i}}const h=t=>t,u={},g=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"],f={value:null,addProjectionMetrics:null};function d(t,e){let n=!1,r=!0;const i={delta:0,timestamp:0,isProcessing:!1},s=()=>n=!0,o=g.reduce((t,n)=>(t[n]=function(t,e){let n=new Set,r=new Set,i=!1,s=!1;const o=new WeakSet;let a={delta:0,timestamp:0,isProcessing:!1},c=0;function l(e){o.has(e)&&(h.schedule(e),t()),c++,e(a)}const h={schedule:(t,e=!1,s=!1)=>{const a=s&&i?n:r;return e&&o.add(t),a.add(t),t},cancel:t=>{r.delete(t),o.delete(t)},process:t=>{if(a=t,i)return void(s=!0);i=!0;const o=n;n=r,r=o,n.forEach(l),e&&f.value&&f.value.frameloop[e].push(c),c=0,n.clear(),i=!1,s&&(s=!1,h.process(t))}};return h}(s,e?n:void 0),t),{}),{setup:a,read:c,resolveKeyframes:l,preUpdate:h,update:d,preRender:p,render:m,postRender:v}=o,y=()=>{const s=u.useManualTiming,o=s?i.timestamp:performance.now();n=!1,s||(i.delta=r?1e3/60:Math.max(Math.min(o-i.timestamp,40),1)),i.timestamp=o,i.isProcessing=!0,a.process(i),c.process(i),l.process(i),h.process(i),d.process(i),p.process(i),m.process(i),v.process(i),i.isProcessing=!1,n&&e&&(r=!1,t(y))};return{schedule:g.reduce((e,s)=>{const a=o[s];return e[s]=(e,s=!1,o=!1)=>(n||(n=!0,r=!0,i.isProcessing||t(y)),a.schedule(e,s,o)),e},{}),cancel:t=>{for(let e=0;e<g.length;e++)o[g[e]].cancel(t)},state:i,steps:o}}const{schedule:p,cancel:m,state:v,steps:y}=d("undefined"!=typeof requestAnimationFrame?requestAnimationFrame:h,!0);function x(t,e){let n;const r=()=>{const{currentTime:r}=e,i=(null===r?0:r.value)/100;n!==i&&t(i),n=i};return p.preUpdate(r,!0),()=>m(r)}const w={};function b(t,e){const n=function(t){let e;return()=>(void 0===e&&(e=t()),e)}(t);return()=>w[e]??n()}const E=b(()=>void 0!==window.ScrollTimeline,"scrollTimeline"),T=b(()=>void 0!==window.ViewTimeline,"viewTimeline");function _(t){return"undefined"!=typeof window&&(t?T():E())}function R(t){return"object"==typeof t&&null!==t}const C=new WeakMap;let P;const M=(t,e,n)=>(r,i)=>{return i&&i[0]?i[0][t+"Size"]:R(s=r)&&"ownerSVGElement"in s&&"getBBox"in r?r.getBBox()[e]:r[n];var s},A=M("inline","width","offsetWidth"),S=M("block","height","offsetHeight");function F({target:t,borderBoxSize:e}){C.get(t)?.forEach(n=>{n(t,{get width(){return A(t,e)},get height(){return S(t,e)}})})}function L(t){t.forEach(F)}function D(t,e){P||"undefined"!=typeof ResizeObserver&&(P=new ResizeObserver(L));const n=function(t,e,n){if(null==t)return[];if(t instanceof EventTarget)return[t];if("string"==typeof t){let r=document;e&&(r=e.current);const i=n?.[t]??r.querySelectorAll(t);return i?Array.from(i):[]}return Array.from(t).filter(t=>null!=t)}(t);return n.forEach(t=>{let n=C.get(t);n||(n=new Set,C.set(t,n)),n.add(e),P?.observe(t)}),()=>{n.forEach(t=>{const n=C.get(t);n?.delete(e),n?.size||P?.unobserve(t)})}}const $=new Set;let W;function I(t){return $.add(t),W||(W=()=>{const t={get width(){return window.innerWidth},get height(){return window.innerHeight}};$.forEach(e=>e(t))},window.addEventListener("resize",W)),()=>{$.delete(t),$.size||"function"!=typeof W||(window.removeEventListener("resize",W),W=void 0)}}const B=(t,e,n)=>{const r=e-t;return 0===r?1:(n-t)/r};const j={x:{length:"Width",position:"Left"},y:{length:"Height",position:"Top"}};function k(t,e,n,r){const i=n[e],{length:s,position:o}=j[e],a=i.current,c=n.time;i.current=Math.abs(t[`scroll${o}`]),i.scrollLength=t[`scroll${s}`]-t[`client${s}`],i.offset.length=0,i.offset[0]=0,i.offset[1]=i.scrollLength,i.progress=B(0,i.scrollLength,i.current);const l=r-c;var h,u;i.velocity=l>50?0:(h=i.current-a,(u=l)?h*(1e3/u):0)}const U=(t,e)=>n=>e(t(n)),V=(...t)=>t.reduce(U);const O=(t,e,n)=>n>e?e:n<t?t:n,H=t=>e=>"string"==typeof e&&e.startsWith(t),z=H("var(--"),N=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;const X={test:t=>"number"==typeof t,parse:parseFloat,transform:t=>t},G={...X,transform:t=>O(0,1,t)},q=t=>Math.round(1e5*t)/1e5,Y=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;const K=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,J=(t,e)=>n=>Boolean("string"==typeof n&&K.test(n)&&n.startsWith(t)||e&&!function(t){return null==t}(n)&&Object.prototype.hasOwnProperty.call(n,e)),Q=(t,e,n)=>r=>{if("string"!=typeof r)return r;const[i,s,o,a]=r.match(Y);return{[t]:parseFloat(i),[e]:parseFloat(s),[n]:parseFloat(o),alpha:void 0!==a?parseFloat(a):1}},Z={...X,transform:t=>Math.round((t=>O(0,255,t))(t))},tt={test:J("rgb","red"),parse:Q("red","green","blue"),transform:({red:t,green:e,blue:n,alpha:r=1})=>"rgba("+Z.transform(t)+", "+Z.transform(e)+", "+Z.transform(n)+", "+q(G.transform(r))+")"};const et={test:J("#"),parse:function(t){let e="",n="",r="",i="";return t.length>5?(e=t.substring(1,3),n=t.substring(3,5),r=t.substring(5,7),i=t.substring(7,9)):(e=t.substring(1,2),n=t.substring(2,3),r=t.substring(3,4),i=t.substring(4,5),e+=e,n+=n,r+=r,i+=i),{red:parseInt(e,16),green:parseInt(n,16),blue:parseInt(r,16),alpha:i?parseInt(i,16)/255:1}},transform:tt.transform},nt=t=>({test:e=>"string"==typeof e&&e.endsWith(t)&&1===e.split(" ").length,parse:parseFloat,transform:e=>`${e}${t}`}),rt=nt("%"),it={test:J("hsl","hue"),parse:Q("hue","saturation","lightness"),transform:({hue:t,saturation:e,lightness:n,alpha:r=1})=>"hsla("+Math.round(t)+", "+rt.transform(q(e))+", "+rt.transform(q(n))+", "+q(G.transform(r))+")"},st={test:t=>tt.test(t)||et.test(t)||it.test(t),parse:t=>tt.test(t)?tt.parse(t):it.test(t)?it.parse(t):et.parse(t),transform:t=>"string"==typeof t?t:t.hasOwnProperty("red")?tt.transform(t):it.transform(t),getAnimatableNone:t=>{const e=st.parse(t);return e.alpha=0,st.transform(e)}},ot=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;const at="number",ct="color",lt=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function ht(t){const e=t.toString(),n=[],r={color:[],number:[],var:[]},i=[];let s=0;const o=e.replace(lt,t=>(st.test(t)?(r.color.push(s),i.push(ct),n.push(st.parse(t))):t.startsWith("var(")?(r.var.push(s),i.push("var"),n.push(t)):(r.number.push(s),i.push(at),n.push(parseFloat(t))),++s,"${}")).split("${}");return{values:n,split:o,indexes:r,types:i}}function ut({split:t,types:e}){const n=t.length;return r=>{let i="";for(let s=0;s<n;s++)if(i+=t[s],void 0!==r[s]){const t=e[s];i+=t===at?q(r[s]):t===ct?st.transform(r[s]):r[s]}return i}}const gt=(t,e)=>{return"number"==typeof t?e?.trim().endsWith("/")?t:0:"number"==typeof(n=t)?0:st.test(n)?st.getAnimatableNone(n):n;var n};const ft={test:function(t){return isNaN(t)&&"string"==typeof t&&(t.match(Y)?.length||0)+(t.match(ot)?.length||0)>0},parse:function(t){return ht(t).values},createTransformer:function(t){return ut(ht(t))},getAnimatableNone:function(t){const e=ht(t);return ut(e)(e.values.map((t,n)=>gt(t,e.split[n])))}};function dt(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t}function pt(t,e){return n=>n>0?e:t}const mt=(t,e,n)=>t+(e-t)*n,vt=(t,e,n)=>{const r=t*t,i=n*(e*e-r)+r;return i<0?0:Math.sqrt(i)},yt=[et,tt,it];function xt(t){const e=(n=t,yt.find(t=>t.test(n)));var n;if(Boolean(e),!Boolean(e))return!1;let r=e.parse(t);return e===it&&(r=function({hue:t,saturation:e,lightness:n,alpha:r}){t/=360,n/=100;let i=0,s=0,o=0;if(e/=100){const r=n<.5?n*(1+e):n+e-n*e,a=2*n-r;i=dt(a,r,t+1/3),s=dt(a,r,t),o=dt(a,r,t-1/3)}else i=s=o=n;return{red:Math.round(255*i),green:Math.round(255*s),blue:Math.round(255*o),alpha:r}}(r)),r}const wt=(t,e)=>{const n=xt(t),r=xt(e);if(!n||!r)return pt(t,e);const i={...n};return t=>(i.red=vt(n.red,r.red,t),i.green=vt(n.green,r.green,t),i.blue=vt(n.blue,r.blue,t),i.alpha=mt(n.alpha,r.alpha,t),tt.transform(i))},bt=new Set(["none","hidden"]);function Et(t,e){return n=>mt(t,e,n)}function Tt(t){return"number"==typeof t?Et:"string"==typeof t?z(e=t)&&N.test(e.split("/*")[0].trim())?pt:st.test(t)?wt:Ct:Array.isArray(t)?_t:"object"==typeof t?st.test(t)?wt:Rt:pt;var e}function _t(t,e){const n=[...t],r=n.length,i=t.map((t,n)=>Tt(t)(t,e[n]));return t=>{for(let e=0;e<r;e++)n[e]=i[e](t);return n}}function Rt(t,e){const n={...t,...e},r={};for(const i in n)void 0!==t[i]&&void 0!==e[i]&&(r[i]=Tt(t[i])(t[i],e[i]));return t=>{for(const e in r)n[e]=r[e](t);return n}}const Ct=(t,e)=>{const n=ft.createTransformer(e),r=ht(t),i=ht(e);return r.indexes.var.length===i.indexes.var.length&&r.indexes.color.length===i.indexes.color.length&&r.indexes.number.length>=i.indexes.number.length?bt.has(t)&&!i.values.length||bt.has(e)&&!r.values.length?function(t,e){return bt.has(t)?n=>n<=0?t:e:n=>n>=1?e:t}(t,e):V(_t(function(t,e){const n=[],r={color:0,var:0,number:0};for(let i=0;i<e.values.length;i++){const s=e.types[i],o=t.indexes[s][r[s]],a=t.values[o]??0;n[i]=a,r[s]++}return n}(r,i),i.values),n):pt(t,e)};function Pt(t,e,n){if("number"==typeof t&&"number"==typeof e&&"number"==typeof n)return mt(t,e,n);return Tt(t)(t,e)}function Mt(t,e,{clamp:n=!0,ease:r,mixer:i}={}){const s=t.length;if(e.length,1===s)return()=>e[0];if(2===s&&e[0]===e[1])return()=>e[1];const o=t[0]===t[1];t[0]>t[s-1]&&(t=[...t].reverse(),e=[...e].reverse());const a=function(t,e,n){const r=[],i=n||u.mix||Pt,s=t.length-1;for(let n=0;n<s;n++){let s=i(t[n],t[n+1]);if(e){const t=Array.isArray(e)?e[n]||h:e;s=V(t,s)}r.push(s)}return r}(e,r,i),c=a.length,l=n=>{if(o&&n<t[0])return e[0];let r=0;if(c>1)for(;r<t.length-2&&!(n<t[r+1]);r++);const i=B(t[r],t[r+1],n);return a[r](i)};return n?e=>l(O(t[0],t[s-1],e)):l}function At(t){const e=[0];return function(t,e){const n=t[t.length-1];for(let r=1;r<=e;r++){const i=B(0,e,r);t.push(mt(n,1,i))}}(e,t.length-1),e}function St(t){return R(t)&&"offsetHeight"in t&&!("ownerSVGElement"in t)}const Ft={start:0,center:.5,end:1};function Lt(t,e,n=0){let r=0;if(t in Ft&&(t=Ft[t]),"string"==typeof t){const e=parseFloat(t);t.endsWith("px")?r=e:t.endsWith("%")?t=e/100:t.endsWith("vw")?r=e/100*document.documentElement.clientWidth:t.endsWith("vh")?r=e/100*document.documentElement.clientHeight:t=e}return"number"==typeof t&&(r=e*t),n+r}const Dt=[0,0];function $t(t,e,n,r){let i=Array.isArray(t)?t:Dt,s=0,o=0;return"number"==typeof t?i=[t,t]:"string"==typeof t&&(i=(t=t.trim()).includes(" ")?t.split(" "):[t,Ft[t]?t:"0"]),s=Lt(i[0],n,r),o=Lt(i[1],e),s-o}const Wt={Enter:[[0,1],[1,1]],Exit:[[0,0],[1,0]],Any:[[1,0],[0,1]],All:[[0,0],[1,1]]},It={x:0,y:0};function Bt(t,e,n){const{offset:r=Wt.All}=n,{target:i=t,axis:s="y"}=n,o="y"===s?"height":"width",a=i!==t?function(t,e){const n={x:0,y:0};let r=t;for(;r&&r!==e;)if(St(r))n.x+=r.offsetLeft,n.y+=r.offsetTop,r=r.offsetParent;else if("svg"===r.tagName){const t=r.getBoundingClientRect();r=r.parentElement;const e=r.getBoundingClientRect();n.x+=t.left-e.left,n.y+=t.top-e.top}else{if(!(r instanceof SVGGraphicsElement))break;{const{x:t,y:e}=r.getBBox();n.x+=t,n.y+=e;let i=null,s=r.parentNode;for(;!i;)"svg"===s.tagName&&(i=s),s=r.parentNode;r=i}}return n}(i,t):It,c=i===t?{width:t.scrollWidth,height:t.scrollHeight}:function(t){return"getBBox"in t&&"svg"!==t.tagName?t.getBBox():{width:t.clientWidth,height:t.clientHeight}}(i),l={width:t.clientWidth,height:t.clientHeight};e[s].offset.length=0;let h=!e[s].interpolate;const u=r.length;for(let t=0;t<u;t++){const n=$t(r[t],l[o],c[o],a[s]);h||n===e[s].interpolatorOffsets[t]||(h=!0),e[s].offset[t]=n}h&&(e[s].interpolate=Mt(e[s].offset,At(r),{clamp:!1}),e[s].interpolatorOffsets=[...e[s].offset]),e[s].progress=O(0,1,e[s].interpolate(e[s].current))}function jt(t,e,n,r={}){return{measure:e=>{!function(t,e=t,n){if(n.x.targetOffset=0,n.y.targetOffset=0,e!==t){let r=e;for(;r&&r!==t;)n.x.targetOffset+=r.offsetLeft,n.y.targetOffset+=r.offsetTop,r=r.offsetParent}n.x.targetLength=e===t?e.scrollWidth:e.clientWidth,n.y.targetLength=e===t?e.scrollHeight:e.clientHeight,n.x.containerLength=t.clientWidth,n.y.containerLength=t.clientHeight}(t,r.target,n),function(t,e,n){k(t,"x",e,n),k(t,"y",e,n),e.time=n}(t,n,e),(r.offset||r.target)&&Bt(t,n,r)},notify:()=>e(n)}}const kt=new WeakMap,Ut=new WeakMap,Vt=new WeakMap,Ot=new WeakMap,Ht=new WeakMap,zt=t=>t===document.scrollingElement?window:t;function Nt(t,{container:e=document.scrollingElement,trackContentSize:n=!1,...r}={}){if(!e)return h;let i=Vt.get(e);i||(i=new Set,Vt.set(e,i));const s=jt(e,t,{time:0,x:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0},y:{current:0,offset:[],progress:0,scrollLength:0,targetOffset:0,targetLength:0,containerLength:0,velocity:0}},r);if(i.add(s),!kt.has(e)){const t=()=>{for(const t of i)t.measure(v.timestamp);p.preUpdate(n)},n=()=>{for(const t of i)t.notify()},r=()=>p.read(t);kt.set(e,r);const s=zt(e);window.addEventListener("resize",r),e!==document.documentElement&&Ut.set(e,(a=r,"function"==typeof(o=e)?I(o):D(o,a))),s.addEventListener("scroll",r),r()}var o,a;if(n&&!Ht.has(e)){const t=kt.get(e),n={width:e.scrollWidth,height:e.scrollHeight};Ot.set(e,n);const r=()=>{const r=e.scrollWidth,i=e.scrollHeight;n.width===r&&n.height===i||(t(),n.width=r,n.height=i)},i=p.read(r,!0);Ht.set(e,i)}const c=kt.get(e);return p.read(c,!1,!0),()=>{m(c);const t=Vt.get(e);if(!t)return;if(t.delete(s),t.size)return;const n=kt.get(e);kt.delete(e),n&&(zt(e).removeEventListener("scroll",n),Ut.get(e)?.(),window.removeEventListener("resize",n));const r=Ht.get(e);r&&(m(r),Ht.delete(e)),Ot.delete(e)}}const Xt=[[Wt.Enter,"entry"],[Wt.Exit,"exit"],[Wt.Any,"cover"],[Wt.All,"contain"]];function Gt(t,e){if(2!==t.length)return!1;for(let n=0;n<2;n++){const r=t[n],i=e[n];if(!Array.isArray(r)||2!==r.length||r[0]!==i[0]||r[1]!==i[1])return!1}return!0}function qt(t){if(!t)return{rangeStart:"contain 0%",rangeEnd:"contain 100%"};for(const[e,n]of Xt)if(Gt(t,e))return{rangeStart:`${n} 0%`,rangeEnd:`${n} 100%`}}const Yt=new Map;function Kt(t){const e={value:0},n=Nt(n=>{e.value=100*n[t.axis].progress},t);return{currentTime:e,cancel:n}}function Jt({source:t,container:e,...n}){const{axis:r}=n;t&&(e=t);let i=Yt.get(e);i||(i=new Map,Yt.set(e,i));const s=n.target??"self";let o=i.get(s);o||(o={},i.set(s,o));const a=r+(n.offset??[]).join(",");if(!o[a])if(n.target&&_(n.target)){const t=qt(n.offset);o[a]=t?new ViewTimeline({subject:n.target,axis:r}):Kt({container:e,...n})}else _()?o[a]=new ScrollTimeline({source:e,axis:r}):o[a]=Kt({container:e,...n});return o[a]}function Qt(t,{axis:e="y",container:n=document.scrollingElement,...r}={}){if(!n)return h;const i={axis:e,container:n,...r};return"function"==typeof t?function(t,e){return function(t){return 2===t.length}(t)?Nt(n=>{t(n[e.axis].progress,n)},e):x(t,Jt(e))}(t,i):function(t,e){const n=Jt(e),r=e.target?qt(e.offset):void 0,i=e.target?_(e.target)&&!!r:_();return t.attachTimeline({timeline:i?n:void 0,...r&&i&&{rangeStart:r.rangeStart,rangeEnd:r.rangeEnd},observe:t=>(t.pause(),x(e=>{t.time=t.iterationDuration*e},n))})}(t,i)}var Zt=function(t,e,n,r){return new(n||(n=Promise))(function(i,s){function o(t){try{c(r.next(t))}catch(t){s(t)}}function a(t){try{c(r.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?i(t.value):(e=t.value,e instanceof n?e:new n(function(t){t(e)})).then(o,a)}c((r=r.apply(t,e||[])).next())})};const te=(0,o.createContext)(null),ee=({project:t,children:e,containerHeight:n="400vh",canvasHeight:r="100vh",offset:i=["start end","end start"],scrub:a=0})=>{const[c,h]=(0,o.useState)({progress:0,frame:-1}),[u,g]=(0,o.useState)(!1),f=(0,o.useRef)(null),d=(0,o.useRef)(null);return(0,o.useEffect)(()=>{let e,n=!0;return g(!1),Zt(void 0,void 0,void 0,function*(){if("string"==typeof t){const n=yield fetch(t),r=yield n.json(),i=t.substring(0,t.lastIndexOf("/"));r.settings||(r.settings={baseResolution:{width:1920,height:1080},scrollMode:"vh"}),r.settings.basePath=r.settings.basePath||i,e=new l(r,{scrub:a})}else e=new l(t,{scrub:a});n?(e.onFrameChange=(t,e)=>{h({frame:t,progress:e})},f.current=e,g(!0)):e.destroy()}),()=>{n=!1,f.current&&(f.current.destroy(),f.current=null)}},[t]),(0,o.useEffect)(()=>{if(!u||!d.current||!f.current)return;const t=Qt(t=>{f.current&&f.current.update(t)},{target:d.current,offset:i});return()=>{t()}},[u,i]),(0,s.jsx)(te.Provider,{value:Object.assign(Object.assign({},c),{engine:f.current}),children:(0,s.jsx)("div",{ref:d,className:"scft-container",style:{position:"relative",height:n},children:(0,s.jsx)("div",{style:{position:"sticky",top:0,height:r,overflow:"hidden"},children:e})})})},ne=({style:t})=>{const e=(0,o.useContext)(te),n=(0,o.useRef)(null);return(0,o.useEffect)(()=>{n.current&&(null==e?void 0:e.engine)&&e.engine.attachCanvas(n.current)},[null==e?void 0:e.engine]),(0,s.jsx)("canvas",{ref:n,style:Object.assign({width:"100%",height:"100%",display:"block",objectFit:"cover"},t)})},re=({id:t="main",offset:e={x:0,y:0},children:n})=>{const r=(0,o.useContext)(te),[i,a]=(0,o.useState)({x:.5,y:.5});if((0,o.useEffect)(()=>{(null==r?void 0:r.engine)&&r.frame>=0&&r.engine.loadTrackingData(t).then(()=>{const{x:e,y:n}=r.engine.getTrackedCoords(t,r.frame);a({x:e,y:n})})},[null==r?void 0:r.engine,null==r?void 0:r.frame,t]),!r||-1===r.frame)return null;const c={position:"absolute",left:`${100*i.x+e.x}%`,top:`${100*i.y+e.y}%`,transform:"translate(-50%, -50%)",pointerEvents:"auto",zIndex:10,transition:"left 0.5s linear, top 0.5s linear"};return(0,s.jsx)("div",{style:c,children:n})},ie=()=>{const t=(0,o.useContext)(te);if(!t)throw new Error("useScrollCraft must be used within a ScrollCraftProvider");return t};return i})());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scrollcraft",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"description": "ScrollCraft is a web-based tool for scroll-triggered animations.",
|
|
5
5
|
"main": "dist/core/scrollcraft.umd.min.js",
|
|
6
6
|
"module": "dist/core/scrollcraft.umd.min.js",
|
|
@@ -10,17 +10,16 @@
|
|
|
10
10
|
"import": "./dist/core/scrollcraft.umd.min.js",
|
|
11
11
|
"require": "./dist/core/scrollcraft.umd.min.js"
|
|
12
12
|
},
|
|
13
|
-
"./core": {
|
|
14
|
-
"types": "./dist/core/index.d.ts",
|
|
15
|
-
"import": "./dist/core/scrollcraft.umd.min.js",
|
|
16
|
-
"require": "./dist/core/scrollcraft.umd.min.js"
|
|
17
|
-
},
|
|
18
13
|
"./react": {
|
|
19
14
|
"types": "./dist/react/index.d.ts",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
15
|
+
"import": "./dist/react/index.js",
|
|
16
|
+
"require": "./dist/react/index.js"
|
|
22
17
|
},
|
|
23
|
-
"./
|
|
18
|
+
"./pipeline": {
|
|
19
|
+
"types": "./dist/pipeline/index.d.ts",
|
|
20
|
+
"import": "./dist/pipeline/index.js",
|
|
21
|
+
"require": "./dist/pipeline/index.js"
|
|
22
|
+
}
|
|
24
23
|
},
|
|
25
24
|
"types": "dist/core/index.d.ts",
|
|
26
25
|
"bin": {
|
|
@@ -31,15 +30,15 @@
|
|
|
31
30
|
"README.md"
|
|
32
31
|
],
|
|
33
32
|
"scripts": {
|
|
34
|
-
"build": "npm run sync-readme && npm run build:
|
|
33
|
+
"build": "npm run sync-readme && npm run build:web && npm run build:pipeline",
|
|
35
34
|
"sync-readme": "node -e \"fs.copyFileSync('../../README.md', './README.md')\"",
|
|
36
|
-
"build:
|
|
37
|
-
"build:types": "tsc --project tsconfig.json --emitDeclarationOnly",
|
|
38
|
-
"build:
|
|
39
|
-
"
|
|
40
|
-
"dev
|
|
41
|
-
"dev:
|
|
42
|
-
"dev:
|
|
35
|
+
"build:web": "webpack --config webpack.config.js --mode production && npm run build:types:web",
|
|
36
|
+
"build:types:web": "tsc --project tsconfig.json --emitDeclarationOnly",
|
|
37
|
+
"build:pipeline": "tsc --project tsconfig.node.json && node scripts/make-executable.cjs",
|
|
38
|
+
"prepublishOnly": "npm run build",
|
|
39
|
+
"dev": "concurrently -n WEB,NODE -c \"blue,yellow\" \"npm:dev:web\" \"npm:dev:node\"",
|
|
40
|
+
"dev:web": "webpack --config webpack.config.js --mode development --watch",
|
|
41
|
+
"dev:node": "tsc --project tsconfig.node.json --watch --preserveWatchOutput"
|
|
43
42
|
},
|
|
44
43
|
"peerDependencies": {
|
|
45
44
|
"react": ">=16.8.0",
|
|
@@ -47,13 +46,15 @@
|
|
|
47
46
|
},
|
|
48
47
|
"dependencies": {
|
|
49
48
|
"@fal-ai/client": "^1.9.4",
|
|
49
|
+
"@ffmpeg/ffmpeg": "^0.12.15",
|
|
50
|
+
"@ffmpeg/util": "^0.12.2",
|
|
50
51
|
"chalk": "^4.1.2",
|
|
51
52
|
"commander": "^11.1.0",
|
|
52
53
|
"dotenv": "^17.3.1",
|
|
53
54
|
"ffmpeg-static": "^5.3.0",
|
|
54
|
-
"fluent-ffmpeg": "^2.1.3",
|
|
55
55
|
"fs-extra": "^11.3.4",
|
|
56
|
-
"
|
|
56
|
+
"jszip": "^3.10.1",
|
|
57
|
+
"motion": "^12.36.0",
|
|
57
58
|
"sharp": "^0.34.5"
|
|
58
59
|
},
|
|
59
60
|
"repository": {
|