Hello all!
I am trying to update this threejs version r128 to 141 (rest of the animations on page are 0.141) but am struggling to get this code to work. I know for sure there is an issue with how I am importing FontLoader since the change in patch 132–>133 "FontLoader and Font have been removed from core. Both classes are now located in examples/jsm/loaders/FontLoader.js "
But the docs page for FontLoader on official ThreeJs website is updated to 0.148 and doesn’t show info on older versions as it is now pointing at /addons/ folder:
three.js docs
- so I have no idea how to solve this or use FontLoader for 141. I tried many many many many different ways and could not get it to work. I tried local npm install three@0.141 and manually calling the local location of FontLoader into the script and it doesn’t work. Here is the .js, which currently functions under r128:
const preload = () => {
let manager = new THREE.LoadingManager();
manager.onLoad = function () {
const environment = new Environment(typo, particle);
};
var typo = null;
const loader = new THREE.FontLoader(manager);
const font = loader.load(
"https://res.cloudinary.com/dydre7amr/raw/upload/v1612950355/font_zsd4dr.json",
function (font) {
typo = font;
}
);
const particle = new THREE.TextureLoader(manager).load(
"https://res.cloudinary.com/dfvtkoboz/image/upload/v1605013866/particle_a64uzf.png"
);
};
if (
document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
)
preload();
else document.addEventListener("DOMContentLoaded", preload);
let magicDivInViewport = false; // Track if the magic div is in the viewport
class Environment {
constructor(font, particle) {
this.font = font;
this.particle = particle;
this.container = document.querySelector("#magic");
this.scene = new THREE.Scene();
this.createCamera();
this.createRenderer();
this.setup();
this.bindEvents();
this.startTimer();
}
startTimer() {
this.mouseEnabled = false;
this.createParticles.onMouseDown();
this.createParticles.disableMouseTracking(); // Disable mouse tracking
setTimeout(() => {
this.createParticles.onMouseUp();
this.createParticles.enableMouseTracking(); // Enable mouse tracking
this.mouseEnabled = true;
}, 6000);
}
bindEvents() {
window.addEventListener("resize", this.onWindowResize.bind(this));
window.addEventListener("scroll", this.checkMagicDivInViewport.bind(this));
}
checkMagicDivInViewport() {
const magicDiv = document.querySelector("#magic");
const rect = magicDiv.getBoundingClientRect();
magicDivInViewport = rect.top <= window.innerHeight && rect.bottom >= 0;
if (!magicDivInViewport) {
// Magic div is out of view, stop JavaScript associated with it
window.removeEventListener("scroll", this.checkMagicDivInViewport.bind(this));
// Remove other event listeners and clean up resources as needed
}
}
setup() {
this.createParticles = new CreateParticles(
this.scene,
this.font,
this.particle,
this.camera,
this.renderer
);
}
render() {
this.createParticles.render();
this.renderer.render(this.scene, this.camera);
}
createCamera() {
this.camera = new THREE.PerspectiveCamera(
65,
this.container.clientWidth / this.container.clientHeight,
1,
10000
);
this.camera.position.set(0, 0, 100);
}
createRenderer() {
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(
this.container.clientWidth,
this.container.clientHeight
);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.container.appendChild(this.renderer.domElement);
this.renderer.setAnimationLoop(() => {
this.render();
});
}
onWindowResize() {
this.camera.aspect =
this.container.clientWidth / this.container.clientHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(
this.container.clientWidth,
this.container.clientHeight
);
}
}
class CreateParticles {
constructor(scene, font, particleImg, camera, renderer) {
this.scene = scene;
this.font = font;
this.particleImg = particleImg;
this.camera = camera;
this.renderer = renderer;
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2(-200, 200);
this.colorChange = new THREE.Color();
this.mouseEnabled = false; // Initialize as false.
this.mouseTrackingEnabled = false; // Initialize as false.
this.button = false;
this.data = {
text: "FUTURE\nIS NOW",
amount: 1500,
particleSize: 1,
particleColor: 0xffffff,
textSize: 16,
area: 250,
ease: 0.05
};
this.setup();
this.bindEvents();
}
bindEvents() {
window.addEventListener("resize", this.onWindowResize.bind(this));
setTimeout(() => {
this.mouseEnabled = true; // Enable after 3 seconds
this.mouseTrackingEnabled = true; // Enable after 3 seconds
// Attach mouse event listeners after the delay
document.addEventListener("mousedown", this.onMouseDown.bind(this));
document.addEventListener("mousemove", this.onMouseMove.bind(this));
document.addEventListener("mouseup", this.onMouseUp.bind(this));
}, 6000); // 3 seconds delay
}
disableMouseTracking() {
this.mouseTrackingEnabled = false;
}
enableMouseTracking() {
this.mouseTrackingEnabled = true;
}
setup() {
const geometry = new THREE.PlaneGeometry(
this.visibleWidthAtZDepth(100, this.camera),
this.visibleHeightAtZDepth(100, this.camera)
);
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
transparent: true
});
this.planeArea = new THREE.Mesh(geometry, material);
this.planeArea.visible = false;
this.createText();
}
bindEvents() {
this.mouseEnabled = false; // Initially set this to false
setTimeout(() => {
this.mouseEnabled = true; // Enable after 6 seconds
}, 6000); // 6000 milliseconds equals 6 seconds
document.addEventListener("mousedown", this.onMouseDown.bind(this));
document.addEventListener("mousemove", this.onMouseMove.bind(this));
document.addEventListener("mouseup", this.onMouseUp.bind(this));
}
onMouseDown(event) {
// Check if the event is undefined, which means it's a simulated mouse down
if (!event) {
// Simulate mouse down at the center of the window
this.mouse.x = 0;
this.mouse.y = 0;
} else {
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
const vector = new THREE.Vector3(this.mouse.x, this.mouse.y, 0.5);
vector.unproject(this.camera);
const dir = vector.sub(this.camera.position).normalize();
const distance = -this.camera.position.z / dir.z;
this.currenPosition = this.camera.position
.clone()
.add(dir.multiplyScalar(distance));
const pos = this.particles.geometry.attributes.position;
this.buttom = true;
this.data.ease = 0.01;
}
onMouseUp() {
this.buttom = false;
this.data.ease = 0.05;
}
onMouseMove() {
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
render(level) {
const time = ((0.001 * performance.now()) % 12) / 12;
const zigzagTime = (1 + Math.sin(time * 2 * Math.PI)) / 6;
this.raycaster.setFromCamera(this.mouse, this.camera);
const intersects = this.raycaster.intersectObject(this.planeArea);
if (intersects.length > 0) {
const pos = this.particles.geometry.attributes.position;
const copy = this.geometryCopy.attributes.position;
const coulors = this.particles.geometry.attributes.customColor;
const size = this.particles.geometry.attributes.size;
const mx = intersects[0].point.x;
const my = intersects[0].point.y;
const mz = intersects[0].point.z;
for (var i = 0, l = pos.count; i < l; i++) {
const initX = copy.getX(i);
const initY = copy.getY(i);
const initZ = copy.getZ(i);
let px = pos.getX(i);
let py = pos.getY(i);
let pz = pos.getZ(i);
this.colorChange.setHSL(0.5, 1, 1);
coulors.setXYZ(
i,
this.colorChange.r,
this.colorChange.g,
this.colorChange.b
);
coulors.needsUpdate = true;
size.array[i] = this.data.particleSize;
size.needsUpdate = true;
let dx = mx - px;
let dy = my - py;
const dz = mz - pz;
const mouseDistance = this.distance(mx, my, px, py);
let d = (dx = mx - px) * dx + (dy = my - py) * dy;
const f = -this.data.area / d;
if (this.buttom) {
const t = Math.atan2(dy, dx);
px -= f * Math.cos(t);
py -= f * Math.sin(t);
this.colorChange.setHSL(0.5 + zigzagTime, 1.0, 0.5);
coulors.setXYZ(
i,
this.colorChange.r,
this.colorChange.g,
this.colorChange.b
);
coulors.needsUpdate = true;
if (
px > initX + 70 ||
px < initX - 70 ||
py > initY + 70 ||
py < initY - 70
) {
this.colorChange.setHSL(0.15, 1.0, 0.5);
coulors.setXYZ(
i,
this.colorChange.r,
this.colorChange.g,
this.colorChange.b
);
coulors.needsUpdate = true;
}
} else {
if (mouseDistance < this.data.area) {
if (i % 5 == 0) {
const t = Math.atan2(dy, dx);
px -= 0.03 * Math.cos(t);
py -= 0.03 * Math.sin(t);
this.colorChange.setHSL(0.15, 1.0, 0.5);
coulors.setXYZ(
i,
this.colorChange.r,
this.colorChange.g,
this.colorChange.b
);
coulors.needsUpdate = true;
size.array[i] = this.data.particleSize / 1.2;
size.needsUpdate = true;
} else {
const t = Math.atan2(dy, dx);
px += f * Math.cos(t);
py += f * Math.sin(t);
pos.setXYZ(i, px, py, pz);
pos.needsUpdate = true;
size.array[i] = this.data.particleSize * 1.3;
size.needsUpdate = true;
}
if (
px > initX + 10 ||
px < initX - 10 ||
py > initY + 10 ||
py < initY - 10
) {
this.colorChange.setHSL(0.15, 1.0, 0.5);
coulors.setXYZ(
i,
this.colorChange.r,
this.colorChange.g,
this.colorChange.b
);
coulors.needsUpdate = true;
size.array[i] = this.data.particleSize / 1.8;
size.needsUpdate = true;
}
}
}
px += (initX - px) * this.data.ease;
py += (initY - py) * this.data.ease;
pz += (initZ - pz) * this.data.ease;
pos.setXYZ(i, px, py, pz);
pos.needsUpdate = true;
}
}
}
createText() {
let thePoints = [];
let shapes = this.font.generateShapes(this.data.text, this.data.textSize);
let geometry = new THREE.ShapeGeometry(shapes);
geometry.computeBoundingBox();
const xMid =
-0.5 * (geometry.boundingBox.max.x - geometry.boundingBox.min.x);
const yMid =
(geometry.boundingBox.max.y - geometry.boundingBox.min.y) / 2.85;
geometry.center();
let holeShapes = [];
for (let q = 0; q < shapes.length; q++) {
let shape = shapes[q];
if (shape.holes && shape.holes.length > 0) {
for (let j = 0; j < shape.holes.length; j++) {
let hole = shape.holes[j];
holeShapes.push(hole);
}
}
}
shapes.push.apply(shapes, holeShapes);
let colors = [];
let sizes = [];
for (let x = 0; x < shapes.length; x++) {
let shape = shapes[x];
const amountPoints =
shape.type == "Path" ? this.data.amount / 2 : this.data.amount;
let points = shape.getSpacedPoints(amountPoints);
points.forEach((element, z) => {
const a = new THREE.Vector3(element.x, element.y, 0);
thePoints.push(a);
colors.push(this.colorChange.r, this.colorChange.g, this.colorChange.b);
sizes.push(1);
});
}
let geoParticles = new THREE.BufferGeometry().setFromPoints(thePoints);
geoParticles.translate(xMid, yMid, 0);
geoParticles.setAttribute(
"customColor",
new THREE.Float32BufferAttribute(colors, 3)
);
geoParticles.setAttribute(
"size",
new THREE.Float32BufferAttribute(sizes, 1)
);
const material = new THREE.ShaderMaterial({
uniforms: {
color: { value: new THREE.Color(0xffffff) },
pointTexture: { value: this.particleImg }
},
vertexShader: vertexShader,
fragmentShader: fragmentShader,
blending: THREE.AdditiveBlending,
depthTest: false,
transparent: true
});
this.particles = new THREE.Points(geoParticles, material);
this.scene.add(this.particles);
this.geometryCopy = new THREE.BufferGeometry();
this.geometryCopy.copy(this.particles.geometry);
}
visibleHeightAtZDepth(depth, camera) {
const cameraOffset = camera.position.z;
if (depth < cameraOffset) depth -= cameraOffset;
else depth += cameraOffset;
const vFOV = (camera.fov * Math.PI) / 180;
return 2 * Math.tan(vFOV / 2) * Math.abs(depth);
}
visibleWidthAtZDepth(depth, camera) {
const height = this.visibleHeightAtZDepth(depth, camera);
return height * camera.aspect;
}
distance(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
}
// Shaders
const vertexShader = `
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}`;
const fragmentShader = `
uniform vec3 color;
uniform sampler2D pointTexture;
varying vec3 vColor;
void main() {
gl_FragColor = vec4( color * vColor, 1.0 );
gl_FragColor = gl_FragColor * texture2D( pointTexture, gl_PointCoord );
}`;