@canvas3/nuxt 0.0.1

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.
Files changed (53) hide show
  1. package/README.md +70 -0
  2. package/dist/module.d.mts +5 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +32 -0
  5. package/dist/runtime/components/canvas3-image.vue +71 -0
  6. package/dist/runtime/components/canvas3-image.vue.d.ts +5 -0
  7. package/dist/runtime/components/canvas3-text.vue +95 -0
  8. package/dist/runtime/components/canvas3-text.vue.d.ts +11 -0
  9. package/dist/runtime/constants/default-canvas3-options.d.ts +40 -0
  10. package/dist/runtime/constants/default-canvas3-options.js +40 -0
  11. package/dist/runtime/constants/shaders/TextBlurFragment.glsl +126 -0
  12. package/dist/runtime/constants/shaders/TextBlurVertex.glsl +10 -0
  13. package/dist/runtime/constants/shaders/projectBlurFragment.glsl +143 -0
  14. package/dist/runtime/constants/shaders/projectBlurVertex.glsl +11 -0
  15. package/dist/runtime/constants/shaders/scrollFragment.glsl +7 -0
  16. package/dist/runtime/constants/shaders/scrollVertex.glsl +8 -0
  17. package/dist/runtime/directives/action-on-scroll.d.ts +2 -0
  18. package/dist/runtime/directives/action-on-scroll.js +42 -0
  19. package/dist/runtime/directives/canvas3-image-directive.client.d.ts +6 -0
  20. package/dist/runtime/directives/canvas3-image-directive.client.js +104 -0
  21. package/dist/runtime/directives/set-data-attributes.d.ts +3 -0
  22. package/dist/runtime/directives/set-data-attributes.js +34 -0
  23. package/dist/runtime/layouts/canvas3-layout.vue +44 -0
  24. package/dist/runtime/layouts/canvas3-layout.vue.d.ts +11 -0
  25. package/dist/runtime/plugin.d.ts +2 -0
  26. package/dist/runtime/plugin.js +3 -0
  27. package/dist/runtime/plugins/directives.d.ts +2 -0
  28. package/dist/runtime/plugins/directives.js +9 -0
  29. package/dist/runtime/server/tsconfig.json +3 -0
  30. package/dist/runtime/types/shaders.d.ts +4 -0
  31. package/dist/runtime/types/three-msdf-text-utils.d.ts +36 -0
  32. package/dist/runtime/types/types.d.ts +71 -0
  33. package/dist/runtime/types/types.js +0 -0
  34. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/TextLayout.d.ts +1 -0
  35. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/TextLayout.js +0 -0
  36. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/createIndices.d.ts +1 -0
  37. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/createIndices.js +0 -0
  38. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/index.d.ts +1 -0
  39. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/index.js +0 -0
  40. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/utils.d.ts +1 -0
  41. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/utils.js +0 -0
  42. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/vertices.d.ts +1 -0
  43. package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/vertices.js +0 -0
  44. package/dist/runtime/utils/canvas3/canvas3.d.ts +65 -0
  45. package/dist/runtime/utils/canvas3/canvas3.js +512 -0
  46. package/dist/runtime/utils/canvas3/helpers.d.ts +12 -0
  47. package/dist/runtime/utils/canvas3/helpers.js +96 -0
  48. package/dist/runtime/utils/canvas3/scroll.d.ts +37 -0
  49. package/dist/runtime/utils/canvas3/scroll.js +191 -0
  50. package/dist/runtime/utils/exports.d.ts +10 -0
  51. package/dist/runtime/utils/exports.js +14 -0
  52. package/dist/types.d.mts +7 -0
  53. package/package.json +64 -0
@@ -0,0 +1,512 @@
1
+ import * as THREE from "three";
2
+ import { ref } from "vue";
3
+ import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
4
+ import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
5
+ import { ShaderPass } from "three/addons/postprocessing/ShaderPass.js";
6
+ import { gsap } from "gsap";
7
+ import {
8
+ generateBindingLogic,
9
+ getMSDFFontMeshScales,
10
+ heightPositionCoef,
11
+ loadTexture
12
+ } from "./helpers.js";
13
+ import Scroll from "#canvas3-nuxt/utils/canvas3/scroll";
14
+ import { defaultCanvas3Options } from "#canvas3-nuxt/constants/default-canvas3-options";
15
+ const canvasInitiated = ref(false);
16
+ class Canvas3Class {
17
+ canvasInitiated = canvasInitiated;
18
+ animateImageMesh = false;
19
+ canvasContainer = null;
20
+ scrollableContent = null;
21
+ time = 0;
22
+ scene = new THREE.Scene();
23
+ MSDFTextGeometryAtlas;
24
+ MSDFTextGeometryFont;
25
+ materials = [];
26
+ imageStore = [];
27
+ textStore = [];
28
+ activateMeshUniformMap = /* @__PURE__ */ new Map();
29
+ scroll = null;
30
+ currentScroll = 0;
31
+ options = defaultCanvas3Options;
32
+ width = 0;
33
+ height = 0;
34
+ camera = null;
35
+ composer = null;
36
+ renderPass = null;
37
+ renderer;
38
+ myEffect;
39
+ customPass = null;
40
+ animations = /* @__PURE__ */ new Map();
41
+ mouse = { x: 0, y: 0, movementX: 0, movementY: 0, xPrev: 0, yPrev: 0 };
42
+ // triggerSectionPositions= {};
43
+ // constructor({ canvasOptions }) {
44
+ // this.options = canvasOptions;
45
+ // }
46
+ async init(canvasElement, scrollableContent, canvas3Options) {
47
+ this.options = canvas3Options;
48
+ this.scene = new THREE.Scene();
49
+ this.canvasContainer = canvasElement;
50
+ this.scrollableContent = scrollableContent;
51
+ this.scroll = new Scroll({
52
+ dom: this.scrollableContent,
53
+ activateMeshCallback: this.activateMesh.bind(this)
54
+ });
55
+ this.setCanvasAndCamera();
56
+ this.setSize();
57
+ this.composerPass();
58
+ this.render();
59
+ window.addEventListener("mousemove", (event) => {
60
+ this.mouse.x = event.clientX / this.width;
61
+ this.mouse.y = event.clientY / this.height;
62
+ setTimeout(() => {
63
+ this.mouse.movementX = event.clientX / this.width;
64
+ this.mouse.movementY = event.clientY / this.height;
65
+ }, 70);
66
+ if (this.mouse.movementX === 0 && this.mouse.movementY === 0) {
67
+ this.mouse.movementX = this.mouse.x;
68
+ this.mouse.movementY = this.mouse.y;
69
+ }
70
+ });
71
+ this.canvasInitiated.value = true;
72
+ }
73
+ setCanvasAndCamera() {
74
+ this.width = this.canvasContainer?.offsetWidth ?? 0;
75
+ this.height = this.canvasContainer?.offsetHeight ?? 0;
76
+ this.camera = new THREE.PerspectiveCamera(
77
+ 70,
78
+ this.width / this.height,
79
+ 100,
80
+ 2e3
81
+ );
82
+ this.camera.position.z = 600;
83
+ this.camera.fov = 2 * Math.atan(this.height / 2 / 600) * (180 / Math.PI);
84
+ this.renderer = new THREE.WebGLRenderer({
85
+ alpha: true
86
+ });
87
+ this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
88
+ this.canvasContainer?.appendChild(this.renderer.domElement);
89
+ }
90
+ // async loadFontMSDF() {
91
+ // const loadFontAtlas = (path: string) => {
92
+ // return new Promise((resolve) => {
93
+ // const loader = new THREE.TextureLoader()
94
+ // loader.load(path, resolve)
95
+ // })
96
+ // }
97
+ // const loadFont = (path: string) => {
98
+ // return new Promise((resolve) => {
99
+ // const loader = new FontLoader()
100
+ // loader.load(path, resolve)
101
+ // })
102
+ // }
103
+ // await Promise.all([
104
+ // loadFontAtlas(this.options.font.atlas),
105
+ // loadFont(this.options.font.fnt),
106
+ // ]).then(([atlas, font]) => {
107
+ // if (atlas)
108
+ // this.MSDFTextGeometryAtlas = atlas
109
+ // if (font)
110
+ // this.MSDFTextGeometryFont = font
111
+ // })
112
+ // }
113
+ resizeOnChange() {
114
+ this.setSize();
115
+ this.resizeImageStore();
116
+ this.resizeTextStore();
117
+ this.scroll?.resizeMobileBreakEvents();
118
+ this.scroll?.setSize();
119
+ }
120
+ resizeImageStore() {
121
+ for (const item of this.imageStore) {
122
+ if (item) {
123
+ const bounds = item.htmlEl.getBoundingClientRect();
124
+ if (!bounds) return;
125
+ item.mesh.scale.set(bounds.width, bounds.height, 1);
126
+ item.mesh.material.uniforms.uMeshSize.value.set(
127
+ bounds.width ?? 0,
128
+ bounds.height ?? 0
129
+ );
130
+ item.width = bounds.width;
131
+ item.height = bounds.height;
132
+ }
133
+ }
134
+ this.setImageMeshPositions();
135
+ }
136
+ resizeTextStore() {
137
+ for (const item of this.textStore) {
138
+ const bounds = item.htmlEl.getBoundingClientRect();
139
+ const { scaleX, scaleY } = getMSDFFontMeshScales(
140
+ bounds.width,
141
+ item.mesh.geometry?._layout?._width
142
+ );
143
+ item.mesh.scale.set(scaleX, scaleY, 1);
144
+ item.width = bounds.width;
145
+ item.height = bounds.height * heightPositionCoef;
146
+ }
147
+ this.setTextMeshPositions();
148
+ }
149
+ setImageMeshPositions() {
150
+ if (this.imageStore.length === 0) return;
151
+ for (const item of this.imageStore) {
152
+ item.mesh.position.x = item.htmlEl.getBoundingClientRect().left - this.width / 2 + item.width / 2;
153
+ item.mesh.position.y = -item.htmlEl.getBoundingClientRect().top + this.height / 2 - item.height / 2;
154
+ }
155
+ }
156
+ setTextMeshPositions() {
157
+ if (this.textStore.length === 0) return;
158
+ for (const item of this.textStore) {
159
+ item.mesh.position.x = item.htmlEl.getBoundingClientRect().left - this.width / 2;
160
+ item.mesh.position.y = -item.htmlEl.getBoundingClientRect().top + this.height / 2 - item.height / 2;
161
+ }
162
+ }
163
+ meshUniformsUpdate(id, uniforms) {
164
+ const mesh = this.scene.getObjectByName(id);
165
+ if (!mesh) return;
166
+ for (const uniKey in uniforms) {
167
+ if (uniforms[uniKey]) {
168
+ if (!mesh.material.uniforms[uniKey])
169
+ mesh.material.uniforms[uniKey] = {
170
+ value: uniforms[uniKey].value,
171
+ duration: 0
172
+ };
173
+ gsap.to(mesh.material.uniforms[uniKey], {
174
+ duration: uniforms[uniKey].duration,
175
+ value: uniforms[uniKey].value,
176
+ ease: uniforms[uniKey].ease ?? "power1.inOut"
177
+ });
178
+ }
179
+ }
180
+ }
181
+ activateMesh(meshId, isActive) {
182
+ const mesh = this.scene.getObjectByName(meshId);
183
+ if (!mesh) {
184
+ console.error("no Mesh found with ID: " + meshId);
185
+ return;
186
+ }
187
+ if (mesh.material.uniforms.uAniInImage && this.options.activateMeshOptions.image.uAniInImage) {
188
+ gsap.to(mesh.material.uniforms.uAniInImage, {
189
+ duration: this.options.activateMeshOptions.image.uAniInImage.duration,
190
+ value: isActive ? 1 : 0,
191
+ ease: this.options.activateMeshOptions.image.uAniInImage.ease
192
+ });
193
+ }
194
+ if (mesh.material.uniforms.uAniInText && this.options.activateMeshOptions.text.uAniInText) {
195
+ gsap.to(mesh.material.uniforms.uAniInText, {
196
+ duration: this.options.activateMeshOptions.text.uAniInText.duration,
197
+ value: isActive ? 1 : 0,
198
+ ease: this.options.activateMeshOptions.text.uAniInText.ease
199
+ });
200
+ }
201
+ const activateMeshUniforms = this.activateMeshUniformMap.get(meshId) ?? null;
202
+ if (activateMeshUniforms) {
203
+ for (const [key, value] of Object.entries(activateMeshUniforms)) {
204
+ if (mesh.material.uniforms[key]) {
205
+ gsap.to(mesh.material.uniforms[key], {
206
+ duration: value.duration,
207
+ value: isActive ? 1 : 0,
208
+ ease: value.ease ?? "power1.inOut"
209
+ });
210
+ }
211
+ }
212
+ }
213
+ }
214
+ // setFixedScrollToElement(elNode, margin = 0) {
215
+ // this.scroll.fixScrollTo = { htmlRef: elNode ?? null, margin: margin };
216
+ // }
217
+ addOnScrollActivateElement(binding) {
218
+ const newBinding = generateBindingLogic(binding);
219
+ this.scroll?.DOM.onScrollActivateElements.push(newBinding);
220
+ }
221
+ updateOnScrollActiveElement(updatedBinding) {
222
+ if (!this.scroll) return;
223
+ for (const [
224
+ index,
225
+ item
226
+ ] of this.scroll.DOM.onScrollActivateElements.entries()) {
227
+ if (item.elNode.dataset.scrollActivateId === updatedBinding.elNode.dataset.scrollActivateId) {
228
+ this.scroll.DOM.onScrollActivateElements[index] = generateBindingLogic(updatedBinding);
229
+ }
230
+ }
231
+ }
232
+ removeScrollActiveElement(elNode) {
233
+ if (!elNode || !this.scroll?.DOM.onScrollActivateElements || this.scroll?.DOM.onScrollActivateElements.length === 0)
234
+ return;
235
+ for (let i = 0; i < this.scroll?.DOM.onScrollActivateElements.length; i++) {
236
+ if (this.scroll.DOM.onScrollActivateElements[i]?.elNode.isEqualNode(elNode)) {
237
+ this.scroll.DOM.onScrollActivateElements.splice(i, 1);
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ removeMesh(id) {
243
+ const toRemove = this.scene.getObjectByName(id);
244
+ if (!toRemove) return;
245
+ this.scene.remove(toRemove);
246
+ toRemove.geometry.dispose();
247
+ toRemove.material.dispose();
248
+ for (const [index, item] of this.imageStore.entries()) {
249
+ if (item.name === id) {
250
+ this.imageStore.splice(index, 1);
251
+ this.materials.splice(index, 1);
252
+ break;
253
+ }
254
+ }
255
+ }
256
+ addAnimationToRender(callBackName, callBackFunction) {
257
+ this.animations.set(callBackName, callBackFunction);
258
+ }
259
+ // addTextAsMSDF(shaderName: string, meshId: string, htmlEl: HTMLElement, textContent: string, theme: string, meshUniforms: MeshMaterialUniform, activateMeshUniforms: MeshMaterialUniform) {
260
+ // this.activateMeshUniformMap.set(meshId, activateMeshUniforms)
261
+ //
262
+ // let vertexShader = this.options.shaders.default.textVertex
263
+ // let fragmentShader = this.options.shaders.default.textFragment
264
+ // if (shaderName && this.options.shaders[shaderName]) {
265
+ // vertexShader = this.options.shaders[shaderName].vertexShader
266
+ // fragmentShader = this.options.shaders[shaderName].fragmentShader
267
+ // }
268
+ //
269
+ // const bounds = htmlEl.getBoundingClientRect()
270
+ // const position = { top: bounds.top, left: bounds.left }
271
+ // position.top += this.currentScroll
272
+ //
273
+ // //* ****************************
274
+ // // MSDF
275
+ // //* ****************************
276
+ //
277
+ // const geometry = new MSDFTextGeometry({
278
+ // text: textContent.trim(),
279
+ // font: this.MSDFTextGeometryFont.data,
280
+ // })
281
+ //
282
+ // const material = new THREE.ShaderMaterial({
283
+ // side: THREE.DoubleSide,
284
+ // transparent: true,
285
+ // defines: {
286
+ // IS_SMALL: false, // false,
287
+ // },
288
+ // uniforms: {
289
+ // uDevicePixelRatio: { value: window.devicePixelRatio },
290
+ // uColor: {
291
+ // // TODO: colors dynamic from component
292
+ // value: new THREE.Vector4(
293
+ // theme === 'dark' ? 27 / 255 : 191 / 255, // R
294
+ // theme === 'dark' ? 24 / 255 : 192 / 255, // G
295
+ // theme === 'dark' ? 24 / 255 : 178 / 255, // B
296
+ // ),
297
+ // },
298
+ // uViewport: {
299
+ // value: new THREE.Vector2(this.width, this.height),
300
+ // },
301
+ // // Common
302
+ // uMouse: { value: new THREE.Vector2(0, 0) },
303
+ // uMouseMovement: { value: new THREE.Vector2(0, 0) },
304
+ // uFooterGameBall: { value: new THREE.Vector2(0, 0) },
305
+ // uMap: { value: null },
306
+ // // Rendering
307
+ // uThreshold: { value: 0.05 },
308
+ // uAlphaTest: { value: 0.01 },
309
+ // uStrokeOutsetWidth: { value: 0.0 },
310
+ // uStrokeInsetWidth: { value: 0.3 }, // 0.3
311
+ // // new generic
312
+ // uTime: { value: 0 },
313
+ // uMeshSize: { value: new THREE.Vector2(bounds.width, bounds.height) },
314
+ // ...this.options.activateMeshOptions.text,
315
+ // ...activateMeshUniforms,
316
+ // ...meshUniforms,
317
+ // },
318
+ // vertexShader: vertexShader,
319
+ // fragmentShader: fragmentShader,
320
+ // })
321
+ //
322
+ // if (material.uniforms.uMap)
323
+ // material.uniforms.uMap.value = this.MSDFTextGeometryAtlas
324
+ //
325
+ // this.materials.push(material)
326
+ //
327
+ // const mesh = new THREE.Mesh(geometry, material)
328
+ // mesh.name = meshId
329
+ // mesh.renderOrder = 1
330
+ //
331
+ // const { scaleX, scaleY } = getMSDFFontMeshScales(
332
+ // bounds.width,
333
+ // mesh.geometry?._layout?._width,
334
+ // )
335
+ //
336
+ // mesh.scale.set(scaleX, scaleY, 1)
337
+ //
338
+ // this.scene.add(mesh)
339
+ //
340
+ // const newMesh: MeshType = {
341
+ // name: meshId,
342
+ // htmlEl: htmlEl,
343
+ // mesh: mesh,
344
+ // top: position.top,
345
+ // left: position.left,
346
+ // width: bounds.width,
347
+ // height: bounds.height * heightPositionCoef,
348
+ // }
349
+ //
350
+ // this.textStore.push(newMesh)
351
+ //
352
+ // this.setTextMeshPositions()
353
+ //
354
+ // if (htmlEl.dataset.activeScroll === 'true') {
355
+ // this.activateMesh(meshId, true)
356
+ // }
357
+ // }
358
+ async addImageAsMesh(imgHtmlEl, shaderName, meshId, meshUniforms, activateMeshUniforms) {
359
+ this.activateMeshUniformMap.set(meshId, activateMeshUniforms);
360
+ let vertexShader = this.options.shaders.default.vertexShader;
361
+ let fragmentShader = this.options.shaders.default.fragmentShader;
362
+ if (shaderName && this.options.shaders[shaderName]) {
363
+ vertexShader = this.options.shaders[shaderName].vertexShader;
364
+ fragmentShader = this.options.shaders[shaderName].fragmentShader;
365
+ }
366
+ const bounds = imgHtmlEl.getBoundingClientRect();
367
+ const position = { top: bounds.top, left: bounds.left };
368
+ position.top += this.currentScroll;
369
+ const geometry = new THREE.PlaneGeometry(1, 1);
370
+ const texture = await loadTexture(imgHtmlEl.src);
371
+ texture.needsUpdate = true;
372
+ const material = new THREE.ShaderMaterial({
373
+ uniforms: {
374
+ uDevicePixelRatio: { value: window.devicePixelRatio },
375
+ uTime: { value: 0 },
376
+ uImage: { value: texture },
377
+ vectorVNoise: { value: new THREE.Vector2(1.5, 1.5) },
378
+ // 1.5
379
+ uMouse: { value: new THREE.Vector2(0, 0) },
380
+ uMouseMovement: { value: new THREE.Vector2(0, 0) },
381
+ uMeshSize: { value: new THREE.Vector2(bounds.width, bounds.height) },
382
+ uTextureSize: {
383
+ value: new THREE.Vector2(
384
+ texture.image.naturalWidth,
385
+ texture.image.naturalHeight
386
+ )
387
+ },
388
+ uViewport: {
389
+ value: new THREE.Vector2(this.width, this.height)
390
+ },
391
+ ...this.options.activateMeshOptions.image,
392
+ ...activateMeshUniforms,
393
+ ...meshUniforms
394
+ },
395
+ fragmentShader,
396
+ vertexShader,
397
+ transparent: true,
398
+ name: meshId
399
+ // wireframe: true,
400
+ });
401
+ this.materials.push(material);
402
+ const mesh = new THREE.Mesh(geometry, material);
403
+ mesh.name = meshId;
404
+ const neutralZScale = 1;
405
+ mesh.scale.set(bounds.width, bounds.height, neutralZScale);
406
+ this.scene.add(mesh);
407
+ const newMesh = {
408
+ name: meshId,
409
+ htmlEl: imgHtmlEl,
410
+ mesh,
411
+ top: position.top,
412
+ left: position.left,
413
+ width: bounds.width,
414
+ height: bounds.height
415
+ };
416
+ this.imageStore.push(newMesh);
417
+ this.setImageMeshPositions();
418
+ }
419
+ composerPass() {
420
+ if (!this.options.shaders.scroll) return;
421
+ this.composer = new EffectComposer(this.renderer);
422
+ if (this.camera)
423
+ this.renderPass = new RenderPass(this.scene, this.camera);
424
+ if (this.renderPass)
425
+ this.composer.addPass(this.renderPass);
426
+ this.myEffect = {
427
+ uniforms: {
428
+ tDiffuse: { value: null },
429
+ scrollSpeed: { value: null }
430
+ },
431
+ vertexShader: this.options.shaders.scroll.vertexShader,
432
+ fragmentShader: this.options.shaders.scroll.fragmentShader
433
+ };
434
+ this.customPass = new ShaderPass(this.myEffect);
435
+ this.customPass.renderToScreen = true;
436
+ this.composer.addPass(this.customPass);
437
+ }
438
+ setSize() {
439
+ this.width = this.canvasContainer?.offsetWidth ?? 0;
440
+ this.height = this.canvasContainer?.offsetHeight ?? 0;
441
+ if (this.camera) {
442
+ this.camera.aspect = this.width / this.height;
443
+ this.camera.updateProjectionMatrix();
444
+ }
445
+ this.renderer.setSize(this.width, this.height);
446
+ this.renderer.setPixelRatio(window.devicePixelRatio);
447
+ this.renderer.render(this.scene, this.camera);
448
+ }
449
+ scrollToTop(delay = 0) {
450
+ setTimeout(() => {
451
+ this.scroll?.render(0, false);
452
+ }, delay);
453
+ }
454
+ scrollTo(position, delay = 0) {
455
+ setTimeout(() => {
456
+ this.scroll?.render(position, true);
457
+ }, delay);
458
+ }
459
+ scrollToElBySelector(elQuerySelector, delay = 0) {
460
+ const element = document.querySelector(elQuerySelector);
461
+ if (!element) return;
462
+ const position = element.offsetTop;
463
+ setTimeout(() => {
464
+ this.scroll?.render(position, true);
465
+ }, delay * 1e3);
466
+ }
467
+ setMeshPositionsUpdate(state) {
468
+ this.animateImageMesh = state;
469
+ }
470
+ render() {
471
+ this.time += 0.05;
472
+ this.scroll?.render(null, null);
473
+ if (this.scroll?.scrollInProgress) {
474
+ if (this.customPass && this.customPass.uniforms.scrollSpeed)
475
+ this.customPass.uniforms.scrollSpeed.value = this.scroll?.speedTarget;
476
+ this.setImageMeshPositions();
477
+ this.setTextMeshPositions();
478
+ }
479
+ if (this.animateImageMesh) {
480
+ this.resizeImageStore();
481
+ this.resizeTextStore();
482
+ }
483
+ for (const material of this.materials) {
484
+ if (material.uniforms.uTime)
485
+ material.uniforms.uTime.value = this.time;
486
+ if (material.uniforms.uMouse)
487
+ material.uniforms.uMouse.value = new THREE.Vector2(
488
+ this.mouse.x,
489
+ this.mouse.y
490
+ );
491
+ if (material.uniforms.uMouseMovement)
492
+ material.uniforms.uMouseMovement.value = new THREE.Vector2(
493
+ this.mouse.movementX,
494
+ this.mouse.movementY
495
+ );
496
+ }
497
+ if (this.composer)
498
+ this.composer.render();
499
+ for (const [_key, callback] of this.animations.entries()) {
500
+ if (callback) {
501
+ callback();
502
+ }
503
+ }
504
+ try {
505
+ requestAnimationFrame(this.render.bind(this));
506
+ } catch (e) {
507
+ console.error(e);
508
+ setImmediate(this.render.bind(this));
509
+ }
510
+ }
511
+ }
512
+ export const Canvas3 = new Canvas3Class();
@@ -0,0 +1,12 @@
1
+ import type { ScrollActionBinding, ScrollActionType } from '#canvas3-nuxt/types/types';
2
+ export declare function generateBindingLogic(newBindingData: ScrollActionBinding): ScrollActionType;
3
+ export declare function findMeshIDs(elParent: HTMLElement): false | string[];
4
+ export declare function loadTexture(src: string): Promise<unknown>;
5
+ export declare const heightPositionCoef = 1.38;
6
+ export declare function getMSDFFontMeshScales(boundsWidth: number, geometryWidth: number): {
7
+ scaleX: number;
8
+ scaleY: number;
9
+ };
10
+ export declare function setScrollActiveElements(elNode: HTMLElement, meshIds: string[], state: string): void;
11
+ export declare function elementNearViewport(bounds: DOMRect, margin?: number): boolean;
12
+ export declare const lerp: (a: number, b: number, n: number) => number;
@@ -0,0 +1,96 @@
1
+ import * as THREE from "three";
2
+ import { gsap } from "gsap";
3
+ export function generateBindingLogic(newBindingData) {
4
+ const binding = newBindingData;
5
+ binding.elNode.dataset.activeScroll = newBindingData?.elNode?.dataset?.activeScroll ?? "false";
6
+ binding.containedMeshIds = [];
7
+ binding.bounds = binding.elNode.getBoundingClientRect();
8
+ if (!binding.options.trackOnly) {
9
+ const foundMeshIds = findMeshIDs(binding.elNode);
10
+ if (foundMeshIds)
11
+ binding.containedMeshIds = foundMeshIds;
12
+ binding.elNode.classList.add("show-on-scroll");
13
+ }
14
+ if (binding.options.fixToParent) {
15
+ binding.containerId = binding.options.fixToParent.containerId;
16
+ binding.fixPosition = 0;
17
+ if (binding.options.fixToParent && binding.options.fixToParent.fixPosition) {
18
+ binding.fixPosition = binding.options.fixToParent.fixPosition;
19
+ }
20
+ binding.options.scrollSpeed = { value: 1 };
21
+ const containerEl = document.getElementById(binding.containerId ?? "");
22
+ if (containerEl)
23
+ binding.containerEl = containerEl;
24
+ if (binding.elNode.children[0]) {
25
+ binding.childEl = binding.elNode.children[0];
26
+ binding.childElBounds = binding.childEl?.getBoundingClientRect();
27
+ }
28
+ if (binding.containerEl)
29
+ binding.containerBottom = binding.containerEl.getBoundingClientRect().bottom;
30
+ binding.margin = 0;
31
+ }
32
+ if (newBindingData.options.scrollSpeedSetTo?.value) {
33
+ const dataSetScrollSpeed = Number(binding.elNode.dataset.scrollSpeed);
34
+ binding.options.scrollSpeed = {
35
+ value: dataSetScrollSpeed
36
+ };
37
+ gsap.to(binding.options.scrollSpeed, {
38
+ duration: newBindingData.options.scrollSpeedSetTo.duration,
39
+ value: newBindingData.options.scrollSpeedSetTo.value,
40
+ onComplete: () => {
41
+ binding.elNode.dataset.scrollSpeed = newBindingData.options.scrollSpeedSetTo.value;
42
+ }
43
+ });
44
+ }
45
+ return binding;
46
+ }
47
+ export function findMeshIDs(elParent) {
48
+ const meshIds = [];
49
+ if (elParent.dataset.meshId) {
50
+ meshIds.push(elParent.dataset.meshId);
51
+ return meshIds;
52
+ }
53
+ const elementsWithMesh = elParent.querySelectorAll("[data-mesh-id]");
54
+ if (!elementsWithMesh || elementsWithMesh.length === 0) return false;
55
+ for (const el of elementsWithMesh) {
56
+ if (el instanceof HTMLElement && el.dataset.meshId) {
57
+ meshIds.push(el.dataset.meshId);
58
+ }
59
+ }
60
+ return meshIds;
61
+ }
62
+ export function loadTexture(src) {
63
+ return new Promise((resolve, reject) => {
64
+ const loader = new THREE.TextureLoader();
65
+ loader.load(
66
+ src,
67
+ (texture) => resolve(texture),
68
+ // On success
69
+ void 0,
70
+ // On progress (optional)
71
+ (err) => reject(err)
72
+ // On error
73
+ );
74
+ });
75
+ }
76
+ const heightSizeCoef = 1;
77
+ export const heightPositionCoef = 1.38;
78
+ export function getMSDFFontMeshScales(boundsWidth, geometryWidth) {
79
+ const scaleX = boundsWidth / geometryWidth * heightSizeCoef;
80
+ const scaleY = -1 * scaleX * heightSizeCoef;
81
+ return { scaleX, scaleY };
82
+ }
83
+ export function setScrollActiveElements(elNode, meshIds, state) {
84
+ if (!meshIds || meshIds.length === 0) return;
85
+ for (const id of meshIds) {
86
+ const el = elNode.querySelector(`[data-mesh-id="${id}"]`);
87
+ if (el instanceof HTMLElement) {
88
+ el.dataset.activeScroll = state;
89
+ }
90
+ }
91
+ }
92
+ export function elementNearViewport(bounds, margin = 200) {
93
+ const windowHeight = window.innerHeight;
94
+ return bounds.top >= -margin && bounds.top <= windowHeight || bounds.bottom >= -margin && bounds.bottom <= windowHeight || bounds.top <= -margin && bounds.bottom >= windowHeight;
95
+ }
96
+ export const lerp = (a, b, n) => (1 - n) * a + n * b;
@@ -0,0 +1,37 @@
1
+ import type { ScrollActionType } from '#canvas3-nuxt/types/types';
2
+ export default class Scroll {
3
+ activateMeshCallback: (meshId: string, isActive: boolean) => void;
4
+ fixScrollTo: {
5
+ htmlRef: null | HTMLElement;
6
+ margin: number;
7
+ };
8
+ scrollToRender: number;
9
+ scrollRendered: number;
10
+ current: number;
11
+ scrollInProgress: boolean;
12
+ ease: number;
13
+ speed: number;
14
+ speedTarget: number;
15
+ scrollSpeedBottomMargin: number;
16
+ DOM: {
17
+ scrollable: HTMLElement;
18
+ onScrollActivateElements: ScrollActionType[];
19
+ };
20
+ constructor(options: {
21
+ dom: HTMLElement;
22
+ activateMeshCallback: (meshId: string, isActive: boolean) => void;
23
+ });
24
+ init(): void;
25
+ getScroll(): void;
26
+ resizeMobileBreakEvents(): void;
27
+ initEvents(): void;
28
+ setSize(): void;
29
+ setElementActive(item: ScrollActionType, isActive: boolean): void;
30
+ setElementsScrollPositions(): void;
31
+ setScrollPosition(): void;
32
+ scrollRenderToFluid(scrollTo: number): void;
33
+ scrollRenderTo(scrollTo: number): void;
34
+ calculateScrollSpeed(): void;
35
+ move(): void;
36
+ render(scrollTo: number | null, fluid: boolean | null): void;
37
+ }