Home > OS >  Curtainjs doesn't load when changing page
Curtainjs doesn't load when changing page

Time:06-14

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.

  • Related