I'm developing a site using barba.js. In this page, "slider", I have a three.js scene. If I open up the page everything is fine. If I navigate from slider to home and then come back to slider page, I got this error:
Uncaught (in promise) TypeError: _this.settings is not a function
The script that controls the slider page is Sketch.js.
import * as THREE from 'three';
import TextTexture from 'three.texttexture';
import TextSprite from '@seregpie/three.text-sprite';
import gsap from 'gsap';
import { Power2 } from 'gsap';
import layout from './Layout'
import { TimelineMax } from "gsap/all";
export default class Sketch {
constructor() {
const container = document.querySelector('#sliderhome');
if (container === null || container === undefined) return
this.container = container
const sliderContainer = document.querySelector("#sliderhome");
console.log("init")
if (sliderContainer) {
this.initSlider();
}
}
initSlider() {
this.scene = new THREE.Scene();
this.vertex = `varying vec2 vUv;void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );}`;
this.fragment = `
uniform float time;
uniform float progress;
uniform float intensity;
uniform float width;
uniform float scaleX;
uniform float scaleY;
uniform float transition;
uniform float radius;
uniform float swipe;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform sampler2D displacement;
uniform vec4 resolution;
varying vec2 vUv;
mat2 getRotM(float angle) {
float s = sin(angle);
float c = cos(angle);
return mat2(c, -s, s, c);
}
const float PI = 3.1415;
const float angle1 = PI *0.25;
const float angle2 = -PI *0.75;
void main() {
vec2 newUV = (vUv - vec2(0.5))*resolution.zw + vec2(0.5);
vec4 disp = texture2D(displacement, newUV);
vec2 dispVec = vec2(disp.r, disp.g);
vec2 distortedPosition1 = newUV + getRotM(angle1) * dispVec * intensity * progress;
vec4 t1 = texture2D(texture1, distortedPosition1);
vec2 distortedPosition2 = newUV + getRotM(angle2) * dispVec * intensity * (1.0 - progress);
vec4 t2 = texture2D(texture2, distortedPosition2);
gl_FragColor = mix(t1, t2, progress);
}
`;
this.uniforms = {
intensity: {value: 1, type:'f', min:0., max:3}
}
this.renderer = new THREE.WebGLRenderer();
this.width = window.innerWidth;
this.height = window.innerHeight;
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(this.width, this.height);
this.renderer.setClearColor(0xeeeeee, 1);
this.duration = 1;
this.easing = 'easeInOut'
this.clicker = document.getElementById("nextSlide");
this.clickNavigate = document.querySelectorAll(".c-navigator_item");
this.container = document.getElementById("slider");
this.images = JSON.parse(this.container.getAttribute('data-images'));
this.titles = JSON.parse(this.container.getAttribute('data-titles'));
this.subtitles = JSON.parse(this.container.getAttribute('data-subtitles'));
this.chinesetitles = JSON.parse(this.container.getAttribute('data-chinese'));
this.navigators = JSON.parse(this.container.getAttribute('data-id'));
this.colors = JSON.parse(this.container.getAttribute('data-colors'));
this.urls = JSON.parse(this.container.getAttribute('data-url'));
this.width = this.container.offsetWidth;
this.height = this.container.offsetHeight;
this.container.appendChild(this.renderer.domElement);
/* Define a global index */
this.index = 0;
this.camera = new THREE.PerspectiveCamera(
70,
window.innerWidth / window.innerHeight,
0.001,
1000
);
this.camera.position.set(0, 0, 2);
this.time = 0;
this.current = 0;
this.textures = [];
this.title = [];
this.subtitle = [];
this.chinesetitle = [];
this.navigatorIndex = [];
this.color = [];
this.url = [];
this.paused = true;
this.initiate( ()=> {
this.setupResize();
this.settings();
this.addObjects();
this.resize();
this.clickEvent();
this.play();
})
}
initiate(cb){
const promises = [];
const titlesObject = [];
const subtitlesObject = [];
const chineseObject = [];
const navigatorIndexes = [];
const colorObject = [];
const urlObject = [];
let that = this;
this.images.forEach((url,i)=>{
let promise = new Promise(resolve => {
that.textures[i] = new THREE.TextureLoader().load( url, resolve );
});
promises.push(promise);
})
this.titles.forEach((el,j)=>{
let promiseS = new Promise(resolve => {
that.title[j] = el;
});
titlesObject.push(promiseS);
})
this.subtitles.forEach((subel,z)=>{
let promiseZ = new Promise(resolve => {
that.subtitle[z] = subel;
});
subtitlesObject.push(promiseZ);
})
this.chinesetitles.forEach((ch,y)=>{
let promiseY = new Promise(resolve => {
that.chinesetitle[y] = ch;
});
chineseObject.push(promiseY);
})
this.colors.forEach((co,r)=>{
let promiseR = new Promise(resolve => {
that.color[r] = co;
});
colorObject.push(promiseR);
})
this.urls.forEach((ur,o)=>{
let promiseO = new Promise(resolve => {
that.url[o] = ur;
});
urlObject.push(promiseO);
})
Promise.all(promises).then(() => {
cb();
console.log(cb)
});
}
clickEvent(){
this.clicker.addEventListener('click',()=>{
this.next();
})
let that = this;
Array.from(this.clickNavigate).forEach(link => {
link.addEventListener('click', function(e) {
that.navigate(e);
});
});
}
settings() {
let that = this;
//if(this.debug) this.gui = new dat.GUI();
this.settings = {progress:0.5};
// if(this.debug) this.gui.add(this.settings, "progress", 0, 1, 0.01);
Object.keys(this.uniforms).forEach((item)=> {
this.settings[item] = this.uniforms[item].value;
//if(this.debug) this.gui.add(this.settings, item, this.uniforms[item].min, this.uniforms[item].max, 0.01);
})
}
setupResize() {
window.addEventListener("resize", this.resize.bind(this));
}
resize() {
this.width = this.container.offsetWidth;
this.height = this.container.offsetHeight;
this.renderer.setSize(this.width, this.height);
this.camera.aspect = this.width / this.height;
// image cover
this.imageAspect = this.textures[0].image.height/this.textures[0].image.width;
let a1; let a2;
if(this.height/this.width>this.imageAspect) {
a1 = (this.width/this.height) * this.imageAspect ;
a2 = 1;
} else{
a1 = 1;
a2 = (this.height/this.width) / this.imageAspect;
}
this.material.uniforms.resolution.value.x = this.width;
this.material.uniforms.resolution.value.y = this.height;
this.material.uniforms.resolution.value.z = a1;
this.material.uniforms.resolution.value.w = a2;
const dist = this.camera.position.z;
const height = 1;
this.camera.fov = 2*(180/Math.PI)*Math.atan(height/(2*dist));
this.plane.scale.x = this.camera.aspect;
this.plane.scale.y = 1;
this.camera.updateProjectionMatrix();
}
addObjects() {
let that = this;
this.material = new THREE.ShaderMaterial({
extensions: {
derivatives: "#extension GL_OES_standard_derivatives : enable"
},
side: THREE.DoubleSide,
uniforms: {
time: { type: "f", value: 0 },
progress: { type: "f", value: 0 },
border: { type: "f", value: 0 },
intensity: { type: "f", value: 0 },
scaleX: { type: "f", value: 40 },
scaleY: { type: "f", value: 40 },
transition: { type: "f", value: 40 },
swipe: { type: "f", value: 0 },
width: { type: "f", value: 0 },
radius: { type: "f", value: 0 },
texture1: { type: "f", value: this.textures[0] },
texture2: { type: "f", value: this.textures[1] },
displacement: { type: "f", value: new THREE.TextureLoader().load('https://www.gianlucarinaldidev.it/jijide/wp-content/uploads/2021/06/disp1.jpg') },
resolution: { type: "v4", value: new THREE.Vector4() },
},
// wireframe: true,
vertexShader: this.vertex,
fragmentShader: this.fragment
});
this.geometry = new THREE.PlaneGeometry(1, 1, 2, 2);
this.plane = new THREE.Mesh(this.geometry, this.material);
this.scene.add(this.plane);
let text2 = document.getElementById("titleContainer");
text2.innerHTML = this.title[0];
let subtext2 = document.getElementById("subtitleContainer");
subtext2.innerHTML = this.subtitle[0];
let chinese2 = document.getElementById("chineseContainer");
chinese2.innerHTML = this.chinesetitle[0];
let nextSlide = document.getElementById("nextSlide");
nextSlide.innerHTML = this.title[1];
let color = document.getElementById("colorContainer");
color.innerHTML = this.color[0];
let url = document.getElementById("productUrl");
url.href= this.url[0];
this.theNextTitle = this.title[1];
this.theNextTexture = this.textures[1];
this.theNextSubtitle = this.subtitle[1];
this.theNextChinese = this.chinesetitle[1];
this.theNextUrl = this.url[1];
}
stop() {
this.paused = true;
}
play() {
this.paused = false;
this.render();
}
next(){
this.current = this.index;
/*if (this.index == 0) {
this.current = -1;
}*/
console.log("After clicking Next, Current is now " + this.current)
//this.current = this.sync();
if(this.isRunning) return;
this.isRunning = true;
let len = this.textures.length;
let nextTexture =this.textures[(this.current +1)%len];
this.material.uniforms.texture2.value = nextTexture;
this.theNextTexture = this.textures[(this.current + 1)];
this.material.uniforms.texture2.value = this.theNextTexture;
let tl = new TimelineMax();
/* Titles */
let text2 = document.getElementById("titleContainer");
let textLenght = this.title.length;
let nextTitle = this.title[(this.current +1)%textLenght];
let nextNextTitle = this.title[(this.current +2)%textLenght];
if (this.index == 0) {
nextNextTitle = this.title[1];
}
//text2.innerHTML = nextTitle;
this.theNextTitle = this.title[(this.current + 1)];
text2.innerHTML = this.theNextTitle;
/* Subtitles */
let subtext2 = document.getElementById("subtitleContainer");
let subtextLenght = this.subtitle.length;
let nextSubtitle = this.subtitle[(this.current +1)%subtextLenght];
//subtext2.innerHTML = nextSubtitle;
this.theNextSubtitle = this.subtitle[(this.current + 1)];
subtext2.innerHTML = this.theNextSubtitle;
/* Chinese */
let chinese2 = document.getElementById("chineseContainer");
let chinesetextLenght = this.chinesetitle.length;
let nextChinese = this.chinesetitle[(this.current +1)%chinesetextLenght];
//chinese2.innerHTML = nextChinese;
/* URL */
let url2 = document.getElementById("productUrl");
let urltextLenght = this.url.length;
let nextUrl = this.url[(this.current +1)%urltextLenght];
this.theNextUrl = this.url[(this.current + 1)];
url2.href = this.theNextUrl;
let nextSlide = document.getElementById("nextSlide");
nextSlide.innerHTML = nextNextTitle;
tl.to(this.material.uniforms.progress,this.duration,{
value:1,
ease: Power2[this.easing],
onComplete:()=>{
console.log('FINISH');
this.current = (this.current +1)%len;
this.material.uniforms.texture1.value = this.theNextTexture;
this.material.uniforms.progress.value = 0;
this.isRunning = false;
text2.innerHTML = this.theNextTitle
subtext2.innerHTML = this.theNextSubtitle;
chinese2.innerHTML = this.theNextChinese;
url2.href = this.theNextUrl;
nextSlide.innerHTML = nextNextTitle;
this.index = (this.index +1);
if (this.index > 7) {
this.index = 0;
}
console.log("Index is" + this.index)
/*if (this.current == 0) {
this.material.uniforms.texture1.value = this.textures[0];
text2.innerHTML = this.title[0];
subtext2.innerHTML = this.subtitle[0];
chinese2.innerHTML = this.chinesetitle[0];
}*/
}})
}
navigate(e) {
let el__id = e.target.dataset.id;
this.index = el__id;
this.current = el__id;
if(this.isRunning) return;
this.isRunning = true;
this.material.uniforms.texture2.value = this.textures[el__id];
let tl = new TimelineMax();
let text2 = document.getElementById("titleContainer");
text2.innerHTML = this.title[el__id];
let subtext2 = document.getElementById("subtitleContainer");
subtext2.innerHTML = this.subtitle[el__id];
let chinese2 = document.getElementById("chineseContainer");
chinese2.innerHTML = this.chinesetitle[el__id];
let color = document.getElementById("colorContainer");
color.innerHTML = this.color[el__id];
let url = document.getElementById("productUrl");
url.href = this.url[el__id];
let nextSlideNavigate = document.getElementById("nextSlide");
let textLenght = this.title.length;
//this.title[(this.current +1)%textLenght];
let len = this.textures.length;
this.index++;
if (this.index > 7) {
this.index = 0;
}
if (this.index == 0) {
let nextNextNavigateTitle = this.title[0];
nextSlideNavigate.innerHTML = nextNextNavigateTitle;
}
if (this.index == 1) {
let nextNextNavigateTitle = this.title[1];
nextSlideNavigate.innerHTML = nextNextNavigateTitle;
}
if (this.index == 2) {
let nextNextNavigateTitle = this.title[2];
nextSlideNavigate.innerHTML = nextNextNavigateTitle;
}
if (this.index == 3) {
let nextNextNavigateTitle = this.title[3];
nextSlideNavigate.innerHTML = nextNextNavigateTitle;
}
if (this.index == 4) {
let nextNextNavigateTitle = this.title[4];
nextSlideNavigate.innerHTML = nextNextNavigateTitle;
}
if (this.index == 5) {
let nextNextNavigateTitle = this.title[5];
nextSlideNavigate.innerHTML = nextNextNavigateTitle;
}
if (this.index == 6) {
let nextNextNavigateTitle = this.title[6];
nextSlideNavigate.innerHTML = nextNextNavigateTitle;
}
if (this.index == 7) {
let nextNextNavigateTitle = this.title[7];
nextSlideNavigate.innerHTML = nextNextNavigateTitle;
}
//let nextNextNavigateTitle = this.title[this.index];
//this.index = el__id;
console.log("In Navigate, Current is now " + this.current)
//nextSlide.innerHTML = this.title[el__id + 1];
tl.to(this.material.uniforms.progress,this.duration,{
value:1,
ease: Power2[this.easing],
onComplete:()=>{
//this.current = this.current;
//this.current = el__id;
this.material.uniforms.texture1.value = this.textures[el__id];
this.material.uniforms.progress.value = 0;
this.isRunning = false;
}})
}
sync() {
return this.index;
}
render() {
if (this.paused) return;
this.time += 0.05;
this.material.uniforms.time.value = this.time;
// this.material.uniforms.progress.value = this.settings.progress;
Object.keys(this.uniforms).forEach((item)=> {
this.material.uniforms[item].value = this.settings[item];
});
requestAnimationFrame(this.render.bind(this));
this.renderer.render(this.scene, this.camera);
}
destroy() {
console.log("destroyed")
//cancelAnimationFrame(this.slider.id);// Stop the animation
//this.renderer.domElement.addEventListener('dblclick', null, false); //remove listener to render
this.renderer = null;
this.scene = null;
this.projector = null;
this.camera = null;
this.controls = null;
this.container = null;
this.images = null;
this.titles = null;
this.subtitles = null;
this.chinesetitles = null;
this.navigators = null;
this.colors = null;
this.urls = null;
this.uniforms = null;
//empty(this.modelContainer);
}
reinit(newContainer = document) {
const container = newContainer.querySelector('#sliderhome')
if (container === null || container === undefined) return
this.container = container
this.initSlider();
}
}
The error is this:
Uncaught (in promise) TypeError: _this.settings is not a function
at eval (Sketch.js?c65f:127)
at eval (Sketch.js?c65f:193)
eval @ Sketch.js?c65f:127
eval @ Sketch.js?c65f:193
Promise.then (async)
initiate @ Sketch.js?c65f:192
initSlider @ Sketch.js?c65f:125
reinit @ Sketch.js?c65f:574
eval @ Router.js?7be6:168
eval @ barba.umd.js?baf6:1
eval @ barba.umd.js?baf6:1
eval @ barba.umd.js?baf6:1
Promise.then (async)
eval @ barba.umd.js?baf6:1
r.do @ barba.umd.js?baf6:1
r.j @ barba.umd.js?baf6:1
eval @ barba.umd.js?baf6:1
Promise.then (async)
eval @ barba.umd.js?baf6:1
eval @ barba.umd.js?baf6:1
s @ barba.umd.js?baf6:1
r @ barba.umd.js?baf6:1
Promise.then (async)
eval @ barba.umd.js?baf6:1
eval @ barba.umd.js?baf6:1
Promise.then (async)
t @ barba.umd.js?baf6:1
eval @ barba.umd.js?baf6:1
s @ barba.umd.js?baf6:1
r.doPage @ barba.umd.js?baf6:1
eval @ barba.umd.js?baf6:1
s @ barba.umd.js?baf6:1
eval @ barba.umd.js?baf6:1
Promise.then (async)
i @ barba.umd.js?baf6:1
e.page @ barba.umd.js?baf6:1
e.go @ barba.umd.js?baf6:1
e.U @ barba.umd.js?baf6:1
// In initSlider()
this.initiate( ()=> {
this.setupResize();
this.settings();
this.addObjects();
this.resize();
this.clickEvent();
this.play();
})
and line 193:
Promise.all(promises).then(() => {
cb();
});
Here I do have the Router.js script that controls the routing in Barba.js:
import barba from '@barba/core'
import barbaPrefetch from '@barba/prefetch'
import gsap from 'gsap'
import LocomotiveScroll from 'locomotive-scroll';
import iman from './InstanceManager'
import cursor from "./Cursor"
export default class Router {
constructor() {
this.cpLoader = document.querySelector('.js-cp-loader')
this.cpLoaderBg = document.querySelector('.js-cp-loader-bg')
const product = document.getElementById("product");
if (product) {
this.initScroll();
}
this.init()
}
goTo (url) {
barba.go(url)
}
prefetch (url) {
barba.prefetch(url)
}
initScroll() {
// Map number x from range [a, b] to [c, d]
const map = (x, a, b, c, d) => (x - a) * (d - c) / (b - a) + c;
// Linear interpolation
const lerp = (a, b, n) => (1 - n) * a + n * b;
const clamp = (num, min, max) => num <= min ? min : num >= max ? max : num;
const randomNumber = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
this.scroll = new LocomotiveScroll({
el: document.querySelector('[data-scroll-container]'),
smooth: true,
direction: 'horizontal',
lerp: 0.05
});
this.scroll.on('scroll', (obj) => {
for (const key of Object.keys(obj.currentElements)) {
if ( obj.currentElements[key].el.classList.contains('gallery__item-imginner') ) {
let progress = obj.currentElements[key].progress;
const saturateVal = progress < 0.5 ? clamp(map(progress,0,0.5,0,1),0,1) : clamp(map(progress,0.5,1,1,0),0,1);
const brightnessVal = progress < 0.5 ? clamp(map(progress,0,0.5,0,1),0,1) : clamp(map(progress,0.5,1,1,0),0,1);
obj.currentElements[key].el.style.filter = `opacity(${brightnessVal})`
}
}
});
this.scroll.update();
}
update() {
this.scroll.update();
}
destroy() {
this.scroll.destroy();
}
defaultLeave = ({ current, next }, context) => {
try {
const done = context.async()
gsap.set(this.cpLoader, {
zIndex: 1000,
visibility: 'visible'
})
gsap.to(this.cpLoaderBg, {
duration: 0.6,
opacity: 1,
onComplete: () => {
done()
}
})
} catch (err) {
console.warn('default leave error', err)
}
}
defaultEnter({ current, next }, context) {
try {
const done = context.async()
done()
gsap.to(this.cpLoaderBg, {
duration: 0.6,
opacity: 0,
onComplete: () => {
gsap.set(this.cpLoader, {
zIndex: -1,
visibility: 'hidden'
})
}
})
} catch (err) {
console.warn('default enter error', err)
}
}
init() {
barba.use(barbaPrefetch)
barba.hooks.after(({ current, next }) => {
const product = document.getElementById("product");
if (product) {
this.initScroll();
}
});
barba.hooks.afterLeave(( { current, next } ) => {
cursor.destroy()
iman.map('destroy')
})
// update the scroll after entering a page
barba.hooks.after(() => {
scroll.update();
});
barba.hooks.beforeEnter(({ current, next }) => {
try {
if (current.container) {
window.scrollTo(0, 0)
const slider = iman.get('sketch')
if (slider) {
slider.reinit(next.container)
}
setTimeout(() => {
cursor.reinit(next.container)
}, 400)
}
} catch (err) {
console.warn('beforeEnter err',err)
}
})
const self = this
this.instance = barba.init({
debug: false,
timeout: 11000,
transitions: [
{
name: 'default',
leave({ current, next }) {
try {
self.defaultLeave({ current, next }, this)
} catch (err) {
console.warn('default LEAVE error', err)
}
},
enter({ current, next }) {
try {
self.defaultEnter({ current, next }, this)
} catch (err) {
console.warn('default ENTER error', err)
}
},
}
],
requestError: (trigger, action, url, response) => {
// console.log('requestError:trigger', trigger)
// console.log('requestError:action', action)
// console.log('requestError:url', url)
// console.log('requestError:response', response)
// go to a custom 404 page if the user click on a link that return a 404 response status
if (action === 'click' && response.status && response.status === 404) {
// window.location.replace(`${mainUrl}/404/`)
}
// prevent Barba from redirecting the user to the requested URL
// this is equivalent to e.preventDefault()
return false
},
})
}
}
And here the Application.js script:
import Router from './Router'
import * as THREE from './three.js';
import TextTexture from 'three.texttexture';
import TextSprite from '@seregpie/three.text-sprite';
import Sketch from './Sketch';
import layout from './Layout'
import iman from './InstanceManager'
import cursor from './Cursor'
import initiaLoader from './InitialLoader'
import { debounce } from "../util"
class Application {
onLoad() {
window.addEventListener('load', () => {
layout.init()
iman.add('layout', layout, true)
cursor.init()
cursor.activateMovement()
initiaLoader.init()
iman.add('sketch', new Sketch())
iman.add('scrollAnimator', new ScrollAnimator())
})
}
onContentLoaded() {
document.addEventListener('DOMContentLoaded', () => {
document.documentElement.className = 'js'
iman.add('router', new Router(), true)
})
}
onResize() {
window.addEventListener('resize', debounce(() => {
layout.resize()
iman.map('resize')
}, 100))
}
init() {
this.onContentLoaded()
this.onLoad()
this.onResize()
}
}
const application = new Application()
export default application
Thanks in advance to everyone.
Your problem appears to be with the following class method...
settings() {
let that = this;
//if(this.debug) this.gui = new dat.GUI();
this.settings = {progress:0.5};
// if(this.debug) this.gui.add(this.settings, "progress", 0, 1, 0.01);
Object.keys(this.uniforms).forEach((item)=> {
this.settings[item] = this.uniforms[item].value;
//if(this.debug) this.gui.add(this.settings, item, this.uniforms[item].min, this.uniforms[item].max, 0.01);
})
}
...where settings
is initially defined as a method, but then within the execution of this.settings()
, it is resetting this.settings
to an object.
Thus, the next time this.settings()
is attempted to be executed, it fails.
Here's a minimal reproducible representation of the issue.
class Test {
constructor() {
this.v = 42;
}
settings() {
console.log( 'In settings function' );
this.settings = {a:1};
}
}
t = new Test();
t.settings(); // Succeeds, but resets settings method to an object.
t.settings(); // Fails, because this.settings is no longer a function.