scrollcraft 2.0.2 ā 2.0.5
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 +5 -6
- package/dist/cli/index.js +4 -3
- package/dist/react/index.js +1 -1
- package/package.json +16 -42
package/README.md
CHANGED
|
@@ -37,13 +37,13 @@ const App = () => (
|
|
|
37
37
|
Choose your path based on your role:
|
|
38
38
|
|
|
39
39
|
### š¤ For Humans
|
|
40
|
-
- [**Core Architecture**](docs/architecture.md): Understand the state-snapshot engine.
|
|
41
|
-
- [**Asset Pipeline**](docs/asset-pipeline.md): Learn how to use the CLI and AI tracking.
|
|
42
|
-
- [**React Hooks**](docs/react-integration.md): Build custom interactive components.
|
|
40
|
+
- [**Core Architecture**](https://github.com/aleskozelsky/scrollcraft/blob/main/packages/docs/architecture.md): Understand the state-snapshot engine.
|
|
41
|
+
- [**Asset Pipeline**](https://github.com/aleskozelsky/scrollcraft/blob/main/packages/docs/asset-pipeline.md): Learn how to use the CLI and AI tracking.
|
|
42
|
+
- [**React Hooks**](https://github.com/aleskozelsky/scrollcraft/blob/main/packages/docs/react-integration.md): Build custom interactive components.
|
|
43
43
|
|
|
44
44
|
### š¤ For AI Agents
|
|
45
|
-
- [**AGENTS.md**](AGENTS.md): Technical standard operating procedures for the repository.
|
|
46
|
-
- [**AI Integration Protocol**](docs/ai-integration.md): How to prompt agents to build scenes for you.
|
|
45
|
+
- [**AGENTS.md**](https://github.com/aleskozelsky/scrollcraft/blob/main/AGENTS.md): Technical standard operating procedures for the repository.
|
|
46
|
+
- [**AI Integration Protocol**](https://github.com/aleskozelsky/scrollcraft/blob/main/packages/docs/ai-integration.md): How to prompt agents to build scenes for you.
|
|
47
47
|
|
|
48
48
|
---
|
|
49
49
|
|
|
@@ -55,4 +55,3 @@ Choose your path based on your role:
|
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
58
|
-
*Dedicated to my family ā Svetla, Verca, Natalka, Alex, and Agatka ā and our daughter Agatka, who stays in our hearts.*
|
package/dist/cli/index.js
CHANGED
|
@@ -45,6 +45,7 @@ const child_process_1 = require("child_process");
|
|
|
45
45
|
const ffmpeg_static_1 = __importDefault(require("ffmpeg-static"));
|
|
46
46
|
const fal_service_1 = require("./fal-service");
|
|
47
47
|
const processor_1 = require("./processor");
|
|
48
|
+
const pkg = require('../../package.json');
|
|
48
49
|
/**
|
|
49
50
|
* Robust FFmpeg Detection
|
|
50
51
|
* Prioritizes bundled static binary, then system PATH.
|
|
@@ -65,8 +66,8 @@ function getFFmpegPath() {
|
|
|
65
66
|
const program = new commander_1.Command();
|
|
66
67
|
program
|
|
67
68
|
.name('scft')
|
|
68
|
-
.description('ScrollCraft CLI
|
|
69
|
-
.version(
|
|
69
|
+
.description('ScrollCraft CLI - Immersive Web SDK')
|
|
70
|
+
.version(pkg.version);
|
|
70
71
|
program
|
|
71
72
|
.command('create')
|
|
72
73
|
.description('ONE-STEP: Transform video/images into a responsive ScrollCraft')
|
|
@@ -77,7 +78,7 @@ program
|
|
|
77
78
|
.option('--cloud', 'Use Fal.ai for tracking and refinement', false)
|
|
78
79
|
.option('--depth', 'Generate a 3D depth map for the displacement effect (Requires --cloud)', false)
|
|
79
80
|
.action(async (input, opts) => {
|
|
80
|
-
console.log(chalk_1.default.bold.blue('\nšļø ScrollCraft
|
|
81
|
+
console.log(chalk_1.default.bold.blue('\nšļø ScrollCraft Asset Pipeline\n'));
|
|
81
82
|
// 0. PRE-FLIGHT CHECK
|
|
82
83
|
const ffmpegPath = getFFmpegPath();
|
|
83
84
|
if (!ffmpegPath) {
|
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={326(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},540(t,e,i){t.exports=i(326)},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(540),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 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})());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scrollcraft",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
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",
|
|
@@ -28,41 +28,33 @@
|
|
|
28
28
|
},
|
|
29
29
|
"files": [
|
|
30
30
|
"dist",
|
|
31
|
-
"package.json",
|
|
32
31
|
"README.md"
|
|
33
32
|
],
|
|
34
33
|
"scripts": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"build:js": "webpack --mode production",
|
|
38
|
-
"build:types": "tsc --emitDeclarationOnly",
|
|
34
|
+
"build": "npm run sync-readme && npm run build:js && npm run build:types && npm run build:cli",
|
|
35
|
+
"sync-readme": "node -e \"fs.copyFileSync('../../README.md', './README.md')\"",
|
|
36
|
+
"build:js": "webpack --config webpack.config.js --mode production",
|
|
37
|
+
"build:types": "tsc --project tsconfig.json --emitDeclarationOnly",
|
|
39
38
|
"build:cli": "tsc --project tsconfig.cli.json && node scripts/make-executable.cjs",
|
|
40
39
|
"dev": "concurrently -n JS,CLI,TYP -c \"blue,yellow,magenta\" \"npm:dev:js\" \"npm:dev:cli\" \"npm:dev:types\"",
|
|
41
|
-
"dev:js": "webpack --mode development --watch",
|
|
40
|
+
"dev:js": "webpack --config webpack.config.js --mode development --watch",
|
|
42
41
|
"dev:cli": "tsc --project tsconfig.cli.json --watch --preserveWatchOutput",
|
|
43
|
-
"dev:types": "tsc --emitDeclarationOnly --watch --preserveWatchOutput"
|
|
44
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
42
|
+
"dev:types": "tsc --project tsconfig.json --emitDeclarationOnly --watch --preserveWatchOutput"
|
|
45
43
|
},
|
|
46
44
|
"peerDependencies": {
|
|
47
45
|
"react": ">=16.8.0",
|
|
48
46
|
"react-dom": ">=16.8.0"
|
|
49
47
|
},
|
|
50
|
-
"
|
|
51
|
-
"@
|
|
52
|
-
"@types/node": "^20.0.0",
|
|
53
|
-
"@types/react": "^18.0.0",
|
|
54
|
-
"@types/react-dom": "^18.0.0",
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@fal-ai/client": "^1.9.4",
|
|
55
50
|
"chalk": "^4.1.2",
|
|
56
|
-
"clean-webpack-plugin": "^4.0.0",
|
|
57
51
|
"commander": "^11.1.0",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"webpack": "^5.90.0",
|
|
65
|
-
"webpack-cli": "^5.1.0"
|
|
52
|
+
"dotenv": "^17.3.1",
|
|
53
|
+
"ffmpeg-static": "^5.3.0",
|
|
54
|
+
"fluent-ffmpeg": "^2.1.3",
|
|
55
|
+
"fs-extra": "^11.3.4",
|
|
56
|
+
"motion": "^12.35.1",
|
|
57
|
+
"sharp": "^0.34.5"
|
|
66
58
|
},
|
|
67
59
|
"repository": {
|
|
68
60
|
"type": "git",
|
|
@@ -72,10 +64,7 @@
|
|
|
72
64
|
"scroll",
|
|
73
65
|
"animation",
|
|
74
66
|
"video",
|
|
75
|
-
"scroll",
|
|
76
67
|
"image",
|
|
77
|
-
"sequence",
|
|
78
|
-
"scroll",
|
|
79
68
|
"sequence"
|
|
80
69
|
],
|
|
81
70
|
"author": "Ales Kozelsky",
|
|
@@ -83,20 +72,5 @@
|
|
|
83
72
|
"bugs": {
|
|
84
73
|
"url": "https://github.com/aleskozelsky/scrollcraft/issues"
|
|
85
74
|
},
|
|
86
|
-
"homepage": "https://github.com/aleskozelsky/scrollcraft#readme"
|
|
87
|
-
"dependencies": {
|
|
88
|
-
"@fal-ai/client": "^1.9.4",
|
|
89
|
-
"@types/fs-extra": "^11.0.4",
|
|
90
|
-
"@types/sharp": "^0.31.1",
|
|
91
|
-
"dotenv": "^17.3.1",
|
|
92
|
-
"ffmpeg-static": "^5.3.0",
|
|
93
|
-
"fluent-ffmpeg": "^2.1.3",
|
|
94
|
-
"fs-extra": "^11.3.4",
|
|
95
|
-
"motion": "^12.35.1",
|
|
96
|
-
"sharp": "^0.34.5"
|
|
97
|
-
},
|
|
98
|
-
"directories": {
|
|
99
|
-
"doc": "docs",
|
|
100
|
-
"example": "examples"
|
|
101
|
-
}
|
|
75
|
+
"homepage": "https://github.com/aleskozelsky/scrollcraft#readme"
|
|
102
76
|
}
|