I want this shapeless infinity scroll like this site https://www.barlow.agency/ using three.js canva. anyone pleas help me! I have done the dragging and seamless looping but i want to add every card should be a title and by hover card that title should be show
here is my my code i want here every card with tile.i already implemented the semmlesly looping by direction dragging but when i am trying to add custom card / adding title that time i can’t .pleas helped me, also see the reference site there have title description every card i want it
this is my code
Centered Three.js Continuous Looping Image Gallery body { margin: 0; overflow: hidden; } #gallery-container { position: absolute; top: 0; left: 0; right: 0; bottom: 0; } function createContinuousLoopingGallery(targetDiv) { const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); targetDiv.appendChild(renderer.domElement); renderer.setClearColor(0xffffff);
const images = [];
const basePath = "./assets/images/case-study/";
const totalImages = 20;
const numCols = 4;
const numRows = 3;
const imageSize = 769;
const spacing = 16;
const gridWidth = numCols * (imageSize + spacing) - spacing;
const gridHeight = numRows * (imageSize + spacing) - spacing;
for (let row = 0; row < numRows; row++) {
for (let col = 0; col < numCols; col++) {
const i = row * numCols + col + 1;
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load(`${basePath}${i}.jpg`);
const material = new THREE.MeshBasicMaterial({ map: texture });
const geometry = new THREE.PlaneGeometry(imageSize, imageSize);
const imageMesh = new THREE.Mesh(geometry, material);
const x = col * (imageSize + spacing) - gridWidth / 2;
let y = gridHeight / 2 - row * (imageSize + spacing) - gridHeight / 2;
// Adjust the y-position for every second card in each row
if (col % 2 === 1) {
y += 264;
}
imageMesh.position.set(x, y, -5);
scene.add(imageMesh);
images.push(imageMesh);
// Set initial rotation values
imageMesh.rotation.x = 0;
imageMesh.rotation.y = 0;
}
}
// Set the camera's initial position to the center
camera.position.set(0, 0, 600);
let isDragging = false;
let previousX = 0;
let previousY = 0;
document.addEventListener("pointerdown", (event) => {
isDragging = true;
previousX = event.clientX;
previousY = event.clientY;
});
document.addEventListener("pointermove", (event) => {
if (isDragging) {
const deltaX = event.clientX - previousX;
const deltaY = event.clientY - previousY;
for (const image of images) {
// Update the skew effect based on mouse movement
const skewX = deltaX * 0.005;
const skewY = -deltaY * 0.005;
// Apply skew effect using GSAP animation
gsap.to(image.rotation, {
x: skewX,
y: skewY,
duration: 0.2,
});
image.position.x += deltaX;
image.position.y -= deltaY;
}
previousX = event.clientX;
previousY = event.clientY;
}
});
document.addEventListener("pointerup", () => {
isDragging = false;
});
const animate = () => {
requestAnimationFrame(animate);
// Loop the images when they go off-screen in both x and y axes
for (const image of images) {
if (image.position.x > gridWidth / 2) {
image.position.x -= gridWidth + spacing;
} else if (image.position.x < -gridWidth / 2) {
image.position.x += gridWidth + spacing;
}
if (image.position.y > gridHeight / 2) {
image.position.y -= gridHeight + spacing;
} else if (image.position.y < -gridHeight / 2) {
image.position.y += gridHeight + spacing;
}
}
renderer.render(scene, camera);
};
animate();
window.addEventListener("resize", () => {
const newWidth = window.innerWidth;
const newHeight = window.innerHeight;
camera.aspect = newWidth / newHeight;
camera.updateProjectionMatrix();
renderer.setSize(newWidth, newHeight);
});
}
createContinuousLoopingGallery(document.getElementById("gallery-container"));
</script>