@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.
- package/README.md +70 -0
- package/dist/module.d.mts +5 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +32 -0
- package/dist/runtime/components/canvas3-image.vue +71 -0
- package/dist/runtime/components/canvas3-image.vue.d.ts +5 -0
- package/dist/runtime/components/canvas3-text.vue +95 -0
- package/dist/runtime/components/canvas3-text.vue.d.ts +11 -0
- package/dist/runtime/constants/default-canvas3-options.d.ts +40 -0
- package/dist/runtime/constants/default-canvas3-options.js +40 -0
- package/dist/runtime/constants/shaders/TextBlurFragment.glsl +126 -0
- package/dist/runtime/constants/shaders/TextBlurVertex.glsl +10 -0
- package/dist/runtime/constants/shaders/projectBlurFragment.glsl +143 -0
- package/dist/runtime/constants/shaders/projectBlurVertex.glsl +11 -0
- package/dist/runtime/constants/shaders/scrollFragment.glsl +7 -0
- package/dist/runtime/constants/shaders/scrollVertex.glsl +8 -0
- package/dist/runtime/directives/action-on-scroll.d.ts +2 -0
- package/dist/runtime/directives/action-on-scroll.js +42 -0
- package/dist/runtime/directives/canvas3-image-directive.client.d.ts +6 -0
- package/dist/runtime/directives/canvas3-image-directive.client.js +104 -0
- package/dist/runtime/directives/set-data-attributes.d.ts +3 -0
- package/dist/runtime/directives/set-data-attributes.js +34 -0
- package/dist/runtime/layouts/canvas3-layout.vue +44 -0
- package/dist/runtime/layouts/canvas3-layout.vue.d.ts +11 -0
- package/dist/runtime/plugin.d.ts +2 -0
- package/dist/runtime/plugin.js +3 -0
- package/dist/runtime/plugins/directives.d.ts +2 -0
- package/dist/runtime/plugins/directives.js +9 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/runtime/types/shaders.d.ts +4 -0
- package/dist/runtime/types/three-msdf-text-utils.d.ts +36 -0
- package/dist/runtime/types/types.d.ts +71 -0
- package/dist/runtime/types/types.js +0 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/TextLayout.d.ts +1 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/TextLayout.js +0 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/createIndices.d.ts +1 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/createIndices.js +0 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/index.d.ts +1 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/index.js +0 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/utils.d.ts +1 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/utils.js +0 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/vertices.d.ts +1 -0
- package/dist/runtime/utils/MSDFFont/MSDFTextGeometry/vertices.js +0 -0
- package/dist/runtime/utils/canvas3/canvas3.d.ts +65 -0
- package/dist/runtime/utils/canvas3/canvas3.js +512 -0
- package/dist/runtime/utils/canvas3/helpers.d.ts +12 -0
- package/dist/runtime/utils/canvas3/helpers.js +96 -0
- package/dist/runtime/utils/canvas3/scroll.d.ts +37 -0
- package/dist/runtime/utils/canvas3/scroll.js +191 -0
- package/dist/runtime/utils/exports.d.ts +10 -0
- package/dist/runtime/utils/exports.js +14 -0
- package/dist/types.d.mts +7 -0
- 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
|
+
}
|