Why the Slider au scroll and its distortion effect created with Three.js and Parcel.js doesn't appear any more?

95 Views Asked by At

I have 2 pages and page 2 contains a horizontal scrolling slider with a distortion effect created with Three.js and Parcel.js (+ cursor effect with gsap on those 2 pages). Separately, 2 pages work very well using **parcel *.html to show pages on the browser.

Problem: After adding a page transition effect with barbar.js and gsap, every time I go to the page 2 from page 1, the page transition works perfectly, but neither the slider nor the distortion effect with Three.js appear. I have to refresh page 2 to see the slider with the distortion effect.

In my project folder, I have 2 HTML files and 3 JavaScript files (app.js for the page transition, cursor.js for the cursor effect & page2.js for the scrolling slider with distortion effect). I don't know where is the problem. Maybe I need some codes to connect these JS files? Or is it the problem related to Parcel.js?

I paste my code below. Could you help me with that? Thank you in advance...

Info: I've installed three.js, parcel.js, and gsap with npm.

Page1.html:

<body data-barba="wrapper">
    <div class="cont-bandes">
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
    </div>
    <div class="wipe-transition"></div>

    <div class="cursor">
        <div class="cursor__ball cursor__ball--big">
            <svg height="30" width="30">
                <circle cx="15" cy="15" r="12" stroke-width="0"></circle>
            </svg>
        </div>

        <div class="cursor__ball cursor__ball--small">
            <svg height="10" width="10">
                <circle cx="5" cy="5" r="4" stroke-width="0"></circle>
            </svg>
        </div>
    </div>

    <main data-barba="container" data-barba-namespace="home">

        <h1 class="homeTitle">Japan</h1>

    </main>

    <footer>

        <nav class="pagesNav">
            <ul class="pagesNav__items">
                <li>
                    <a href="./index.html" class="hoverable">
                        <span class="letter">H</span>
                        <span class="letter">o</span>
                        <span class="letter">m</span>
                        <span class="letter">e</span>
                    </a>
                </li>
                <li>
                    <a href="./osaka.html" class="hoverable">
                        <span class="letter">O</span>
                        <span class="letter">s</span>
                        <span class="letter">a</span>
                        <span class="letter">k</span>
                        <span class="letter">a</span>
                    </a>
                </li>
                <li>
                    <a href="#" class="hoverable">
                        <span class="letter">T</span>
                        <span class="letter">o</span>
                        <span class="letter">k</span>
                        <span class="letter">y</span>
                        <span class="letter">o</span>
                    </a>
                </li>
                <li>
                    <a href="#" class="hoverable">
                        <span class="letter">K</span>
                        <span class="letter">y</span>
                        <span class="letter">o</span>
                        <span class="letter">t</span>
                        <span class="letter">o</span>
                    </a>
                </li>
            </ul>
        </nav>

    </footer>

    <script src="/scripts/cursor.js"></script>
    <script src="https://unpkg.com/@barba/core"></script>
    <script src="/scripts/app.js"></script>


</body>

Page2.html:

<body class="osaka-body" data-barba="wrapper">

    <div class="transition">
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
        <div class="bande"></div>
    </div>

    <div class="cursor">
        <div class="cursor__ball cursor__ball--big">
            <svg height="30" width="30">
                <circle cx="15" cy="15" r="12" stroke-width="0"></circle>
            </svg>
        </div>

        <div class="cursor__ball cursor__ball--small">
            <svg height="10" width="10">
                <circle cx="5" cy="5" r="4" stroke-width="0"></circle>
            </svg>
        </div>
    </div>

    <main data-barba="container" data-barba-namespace="home">
        <header>
            <h1 class="osakaTitle">大阪</span></h1>
        </header>

        <div class="slider">
            <div class="slider-inner">
                <div class="item hoverable">
                    <img src="images/osaka/1.jpg" alt="">
                </div>
                <div class="item hoverable">
                    <img src="images/osaka/2.jpg" alt="">
                </div>
                <div class="item hoverable">
                    <img src="images/osaka/3.jpg" alt="">
                </div>
                <div class="item hoverable">
                    <img src="images/osaka/4.jpg" alt="">
                </div>
                <div class="item hoverable">
                    <img src="images/osaka/5.jpg" alt="">
                </div>
                <div class="item hoverable">
                    <img src="images/osaka/6.jpg" alt="">
                </div>
            </div>
        </div>

    </main>

    <footer>

        <nav class="pagesNav">
            <ul class="pagesNav__items">
                <li>
                    <a href="index.html" class="hoverable">
                        <span class="letter">H</span>
                        <span class="letter">o</span>
                        <span class="letter">m</span>
                        <span class="letter">e</span>
                    </a>
                </li>
                <li>
                    <a href="osaka.html" class="hoverable">
                        <span class="letter">O</span>
                        <span class="letter">s</span>
                        <span class="letter">a</span>
                        <span class="letter">k</span>
                        <span class="letter">a</span>
                    </a>
                </li>
                <li>
                    <a href="#" class="hoverable">
                        <span class="letter">T</span>
                        <span class="letter">o</span>
                        <span class="letter">k</span>
                        <span class="letter">y</span>
                        <span class="letter">o</span>
                    </a>
                </li>
                <li>
                    <a href="#" class="hoverable">
                        <span class="letter">K</span>
                        <span class="letter">y</span>
                        <span class="letter">o</span>
                        <span class="letter">t</span>
                        <span class="letter">o</span>
                    </a>
                </li>
            </ul>
        </nav>

    </footer>

    <script src="scripts/slider.js"></script>
    <script src="scripts/cursor.js"></script>
    <script src="https://unpkg.com/@barba/core"></script>
    <script src="/scripts/app.js"></script>

</body>

app.js:

import gsap from "gsap";

const wipe = document.querySelector('.wipe-transition');
const allBandes = document.querySelectorAll('.bande');
const TLAnim = gsap.timeline();

function delay(n) {
    return new Promise((done) => {
        setTimeout(() => {
            done();
        }, n)
    })
}

barba.init({

    sync: true,

    transitions: [
        {
            async leave() {

                const done = this.async();

                TLAnim
                    .to(allBandes, { height: '100%', stagger: 0.05 })


                await delay(1500);
                done();

            },
            enter() {


                TLAnim
                    .to(allBandes, { height: '0%', stagger: 0.05 })

            }
        }
    ]

})

slider.js:

import * as THREE from 'three';
import vertexShader from './shaders/vertexShader.glsl';
import fragmentShader from './shaders/fragmentShader.glsl';


const images = [...document.querySelectorAll('img')];
const slider = document.querySelector('.slider');
let sliderWidth;
let imageWidth;
let current = 0;
let target = 0;
const ease = .05;


function lerp(start, end, t) {
    return start * (1 - t) + end * t;
}

function setTransform(el, transform) {
    el.style.transform = transform;
}

function init() {
    sliderWidth = slider.getBoundingClientRect().width;
    imageWidth = sliderWidth / images.length;
    document.body.style.height = `${sliderWidth - (window.innerWidth - window.innerHeight)}px`;
}


function animate() {
    current = parseFloat(lerp(current, target, ease)).toFixed(2);
    target = window.scrollY;
    slider.style.transform = `translate3d(${-current}px, 0, 0)`
}

class EffectCanvas {
    constructor() {
        this.container = document.querySelector('main');
        this.width = this.container.offsetWidth;
        this.height = this.container.offsetHeight;

        this.images = [...document.querySelectorAll('img')];


        this.meshItems = []; // Used to store all meshes we will be creating.
        this.setupCamera();
        this.createMeshItems();
        this.render();
    }

    // Getter function used to get screen dimensions used for the camera and mesh materials
    get viewport() {
        let width = window.innerWidth;
        let height = window.innerHeight;
        let aspectRatio = width / height;
        return {
            width,
            height,
            aspectRatio
        };
    }

    setupCamera() {
        window.addEventListener('resize', this.resize.bind(this));

        // Create new scene
        this.scene = new THREE.Scene();

        // Initialize perspective camera
        let perspective = 1000;
        const fov = (180 * (2 * Math.atan(window.innerHeight / 2 / perspective))) / Math.PI; 
        this.camera = new THREE.PerspectiveCamera(fov, this.viewport.aspectRatio, 1, 1000);
        this.camera.position.set(0, 0, perspective) 

        //renderer
        this.renderer = new THREE.WebGLRenderer({
            antialias: true,
            alpha: true
        });
        this.renderer.setSize(this.viewport.width, this.viewport.height); 
        this.renderer.setPixelRatio(window.devicePixelRatio); 
        this.container.appendChild(this.renderer.domElement); 
    }

    resize() {
        init();
        this.camera.aspect = this.viewport.aspectRatio; 
        this.camera.updateProjectionMatrix(); 
        this.renderer.setSize(this.viewport.width, this.viewport.height);
    }

    createMeshItems() {
        this.images.forEach(image => {
            let meshItem = new MeshItem(image, this.scene);
            this.meshItems.push(meshItem);
        });
    }

    render() {
        animate();
        for (let i = 0; i < this.meshItems.length; i++) {
            this.meshItems[i].render();
        }
        this.renderer.render(this.scene, this.camera);
        requestAnimationFrame(this.render.bind(this));
    }

}

class MeshItem {
    constructor(element, scene) {
        this.element = element;
        this.scene = scene;
        this.offset = new THREE.Vector2(0, 0);
        this.sizes = new THREE.Vector2(0, 0);
        this.createMesh();

        console.log(this.element);
    }



    getDimensions() {
        const { width, height, top, left } = this.element.getBoundingClientRect();
        this.sizes.set(width, height);
        this.container = document.querySelector('main');
        this.width = this.container.offsetWidth;
        this.height = this.container.offsetHeight;
        this.offset.set(left - this.width / 2 + width / 2., -top + this.height / 2 - height / 2.); 
    }

    createMesh() {
        this.geometry = new THREE.PlaneGeometry(1, 1, 30, 30); 

        this.imageTexture = new THREE.TextureLoader().load(this.element.src);
        console.log(this.imageTexture);

        this.uniforms = {
            uTexture: { value: this.imageTexture },
            uOffset: { value: new THREE.Vector2(0, 0) },
            uAlpha: { value: 1.0 } 
        }
        this.material = new THREE.ShaderMaterial({
            uniforms: this.uniforms,
            vertexShader: vertexShader,
            fragmentShader: fragmentShader
        });
        this.mesh = new THREE.Mesh(this.geometry, this.material);
        this.getDimensions();
        this.mesh.position.set(this.offset.x, this.offset.y, 0);
        this.mesh.scale.set(this.sizes.x, this.sizes.y, 1);

        this.scene.add(this.mesh);
    }

    render() {
        this.getDimensions();
        this.mesh.position.set(this.offset.x, this.offset.y, 0);
        this.mesh.scale.set(this.sizes.x, this.sizes.y, 0);
        this.uniforms.uOffset.value.set(-(target - current) * 0.0002, 0.0); 
    }
}

init();
new EffectCanvas();

Thank you very much!

1

There are 1 best solutions below

0
Brunno Breda On

I have this same issue with barba.js and gsap animations, only worked in the first page, after transition every other animation in my main.js doens't worked.

In the documentation I found a solution to make it work called view

Barba.js documentation - view

Exemple:

barba.init({
  views: [{
    namespace: 'page1',
    beforeLeave(data) {
      // do something before leaving the current `page1` namespace
      // it's not what you looking for, but it is good to know
    }
  }, {
    namespace: 'page2',
    beforeEnter(data) {
      // (here it's what you want) do something before entering the `page2` namespace
      // your animation here
      function animate() {
        requestAnimationFrame(animate);

        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;

        renderer.render(scene, camera);
      }

      animate();
    }
  }]
});

The thing is, javascript need to run in every page to work, since barba.js only refresh the content, you need to run your javascript again. With view you can run your javascript properly in every different page, and even distribute it properly between pages.