So I'm using curtainjs on my website. I have a landing page that redirect to my about page, but when I go to my about page from the landing page, the curtainjs doesn't work. But as soon as I reload the page everything works fine.
Here's how I implemented curtainjs :
mounted(){
window.addEventListener("load", this.curtainjs);
}
methods: {
curtainjs() {
const curtains = new Curtains({
container: "canvas",
pixelRatio: Math.min(1.5, window.devicePixelRatio),
});
curtains
.onRender(() => {
// update our planes deformation
// increase/decrease the effect
planesDeformations = curtains.lerp(planesDeformations, 0, 0.075);
})
.onScroll(() => {
// get scroll deltas to apply the effect on scroll
const delta = curtains.getScrollDeltas();
// invert value for the effect
delta.y = -delta.y;
// threshold
if (delta.y > 60) {
delta.y = 60;
} else if (delta.y < -60) {
delta.y = -60;
}
if (Math.abs(delta.y) > Math.abs(planesDeformations)) {
planesDeformations = curtains.lerp(
planesDeformations,
delta.y,
0.1
);
}
})
.onError(() => {
// we will add a class to the document body to display original images
document.body.classList.add("no-curtains", "planes-loaded");
})
.onContextLost(() => {
// on context lost, try to restore the context
curtains.restoreContext();
});
const planes = [];
let planesDeformations = 0;
// get our planes elements
let planeElements = document.getElementsByClassName("plane");
const vs = `
precision mediump float;
// default mandatory variables
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 planeTextureMatrix;
// custom variables
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
uniform float uPlaneDeformation;
void main() {
vec3 vertexPosition = aVertexPosition;
// cool effect on scroll
vertexPosition.y = sin(((vertexPosition.x 1.0) / 2.0) * 3.141592) * (sin(uPlaneDeformation / 90.0));
gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);
// varyings
vVertexPosition = vertexPosition;
vTextureCoord = (planeTextureMatrix * vec4(aTextureCoord, 0.0, 1.0)).xy;
}
`;
const fs = `
precision mediump float;
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
uniform sampler2D planeTexture;
void main() {
// just display our texture
gl_FragColor = texture2D(planeTexture, vTextureCoord);
}
`;
// all planes will have the same parameters
const params = {
vertexShader: vs,
fragmentShader: fs,
widthSegments: 10,
heightSegments: 10,
fov: 45,
drawCheckMargins: {
top: 0,
right: 0,
bottom: 0,
left: 0,
},
uniforms: {
planeDeformation: {
name: "uPlaneDeformation",
type: "1f",
value: 0,
},
},
};
// add our planes and handle them
for (let i = 0; i < planeElements.length; i ) {
planes.push(new Plane(curtains, planeElements[i], params));
handlePlanes(i);
}
// handle all the planes
function handlePlanes(index) {
const plane = planes[index];
// check if our plane is defined and use it
plane
.onReady(() => {
// once everything is ready, display everything
if (index === planes.length - 1) {
document.body.classList.add("planes-loaded");
document.querySelectorAll(".picture").forEach((img) => {
img.style.display = "none";
});
}
})
.onRender(() => {
// update the uniform
plane.uniforms.planeDeformation.value = planesDeformations;
});
}
},
}
I don't know where it coould come from because there is no other place to put the function call than the mounted part.
CodePudding user response:
This happens because the window load
event has already been fired when you navigate from your landing page to the about page.
You could directly call this.curtainsjs()
inside your mounted
hook and this should work.
However this pattern will lead to memory leaks every time you'll navigate away from the about page since the curtains
instance won't be correctly disposed.
There's at least a couple solutions to avoid this:
Solution 1 - quick and dirty
Assign your curtains
instance to a private component variable and dispose the instance before unmounting the component.
This should work but it's far from being perfect in terms of performances and ressources, as you'll recreate a new webgl
context each time you'll navigate to the about page.
mounted(){
this.curtainjs();
},
beforeUnmount() {
if(this.$curtains) {
// dispose curtains instance
this.$curtains.dispose();
}
},
methods: {
curtainjs() {
this.$curtains = new Curtains({
container: "canvas",
pixelRatio: Math.min(1.5, window.devicePixelRatio),
});
this.$curtains
.onRender(() => {
// update our planes deformation
// increase/decrease the effect
planesDeformations = this.$curtains.lerp(planesDeformations, 0, 0.075);
})
.onScroll(() => {
// get scroll deltas to apply the effect on scroll
const delta = this.$curtains.getScrollDeltas();
// invert value for the effect
delta.y = -delta.y;
// threshold
if (delta.y > 60) {
delta.y = 60;
} else if (delta.y < -60) {
delta.y = -60;
}
if (Math.abs(delta.y) > Math.abs(planesDeformations)) {
planesDeformations = curtains.lerp(
planesDeformations,
delta.y,
0.1
);
}
})
.onError(() => {
// we will add a class to the document body to display original images
document.body.classList.add("no-curtains", "planes-loaded");
})
.onContextLost(() => {
// on context lost, try to restore the context
this.$curtains.restoreContext();
});
const planes = [];
let planesDeformations = 0;
// get our planes elements
let planeElements = document.getElementsByClassName("plane");
const vs = `
precision mediump float;
// default mandatory variables
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 planeTextureMatrix;
// custom variables
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
uniform float uPlaneDeformation;
void main() {
vec3 vertexPosition = aVertexPosition;
// cool effect on scroll
vertexPosition.y = sin(((vertexPosition.x 1.0) / 2.0) * 3.141592) * (sin(uPlaneDeformation / 90.0));
gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);
// varyings
vVertexPosition = vertexPosition;
vTextureCoord = (planeTextureMatrix * vec4(aTextureCoord, 0.0, 1.0)).xy;
}
`;
const fs = `
precision mediump float;
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
uniform sampler2D planeTexture;
void main() {
// just display our texture
gl_FragColor = texture2D(planeTexture, vTextureCoord);
}
`;
// all planes will have the same parameters
const params = {
vertexShader: vs,
fragmentShader: fs,
widthSegments: 10,
heightSegments: 10,
fov: 45,
drawCheckMargins: {
top: 0,
right: 0,
bottom: 0,
left: 0,
},
uniforms: {
planeDeformation: {
name: "uPlaneDeformation",
type: "1f",
value: 0,
},
},
};
// add our planes and handle them
for (let i = 0; i < planeElements.length; i ) {
planes.push(new Plane(this.$curtains, planeElements[i], params));
handlePlanes(i);
}
// handle all the planes
function handlePlanes(index) {
const plane = planes[index];
// check if our plane is defined and use it
plane
.onReady(() => {
// once everything is ready, display everything
if (index === planes.length - 1) {
document.body.classList.add("planes-loaded");
document.querySelectorAll(".picture").forEach((img) => {
img.style.display = "none";
});
}
})
.onRender(() => {
// update the uniform
plane.uniforms.planeDeformation.value = planesDeformations;
});
}
},
}
Solution 2 - the right way
The correct "Vue" way to handle this would be to create a curtains
plugin and a Curtains
component at the root of your App
where you'll handle the webgl
context creation only once on init.
Then you could create as many Plane
components as needed with their own lifecycle methods (creation and removal using mounted
and beforeUnmount
hooks) using your plugin.
I won't write the whole code here (it shouldn't be that difficult), even tho I realize a Vue
example usecase of the library might be useful. I'll think about it.
Also, looking at your code it seems you've just copied/pasted one of the library example into your curtainsjs
method. There's a lot of room for improvement here: handle errors/events and variables with Vue data
, computed
and methods
properties, import the shaders from external files, etc.