Rengga Dev – Try playing with the parameters on the gui to the right. The Apple Cards is just one effect of WebGL.
import * as THREE from 'https://cdn.skypack.dev/three@v0.122.0';
// Helper functions
const rgb = function(r, g, b) {
return new THREE.Vector3(r, g, b);
}
const loader = function(path, texture) {
return new Promise((resolve, reject) => {
let loader = new THREE.FileLoader();
if(typeof texture !== "undefined") {
loader = new THREE.TextureLoader();
}
loader.load(path, (item) => resolve(item));
})
}
const randomInteger = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// -- End Helper Functions
const config = {
individualItem: '.album-item', // class of individual item
carouselWidth: 1000, // in px
carouselId: '#album-rotator', // carousel selector
carouselHolderId: '#album-rotator-holder', // carousel should be <div id="carouselId"><div id="carouselHolderId">{items}</div></div>
colors: [
// Define colors for each item. If more items than colors, then first color will be used as default
// Format { low: rgb(), high: rgb() for each color }
{ low: rgb(0, 114, 255), high: rgb(48, 0, 255) },
{ low: rgb(236, 166, 15), high: rgb(233, 104, 0) },
{ low: rgb(43, 75, 235), high: rgb(213, 51, 248) },
{ low: rgb(175, 49, 49), high: rgb(123, 16, 16) }
]
}
// Async function for generating webGL waves
const createWave = async function(selector, colors) {
if(document.querySelectorAll(selector) !== null && document.querySelectorAll(selector).length > 0) {
// Import all the fragment and vertex shaders
const noise = document.getElementById('noise').textContent;
const fragment = document.getElementById('fragment').textContent
const vertex =document.getElementById('vertex').textContent
let i = 0;
// For each of the selector elements
document.querySelectorAll(selector).forEach(function(item) {
// Create a renderer
const newCanvas = document.createElement('canvas');
newCanvas.id = `canvas-${i}`;
item.appendChild(newCanvas);
const renderer = new THREE.WebGLRenderer({
powerPreference: "high-performance",
antialias: true,
alpha: true,
canvas: document.getElementById(`canvas-${i}`)
});
// Get el width and height
const elWidth = parseFloat(window.getComputedStyle(item).width);
const elHeight = parseFloat(window.getComputedStyle(item).height);
// Set sizes and set scene/camera
renderer.setSize( elWidth, elHeight );
renderer.setPixelRatio( window.devicePixelRatio );
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, elWidth / elHeight, 0.1, 1000 );
// Check on colors to use
let high = colors[0].high;
let low = colors[0].low;
if(typeof colors[i] !== "undefined") {
high = colors[i].high;
low = colors[i].low;
++i;
}
// And use the high color for the subtext.
if(item.querySelector('.subtext') !== null) {
item.querySelector('.subtext').style.background = `rgba(${high.x},${high.y},${high.z},0.75)`;
}
// Create a plane, and pass that through to our shaders
let geometry = new THREE.PlaneGeometry(600, 600, 100, 100);
let material = new THREE.ShaderMaterial({
uniforms: {
u_lowColor: {type: 'v3', value: low },
u_highColor: {type: 'v3', value: high },
u_time: {type: 'f', value: 0},
u_height: {type: 'f', value: 1},
u_rand: {type: 'f', value: new THREE.Vector2(randomInteger(6, 10), randomInteger(8, 10)) }
},
fragmentShader: noise + fragment,
vertexShader: noise + vertex,
});
// Create the mesh and position appropriately
let mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, -300);
mesh.material.needsUpdate = true;
scene.add(mesh);
// On hover effects for each item
let enterTimer, exitTimer;
item.addEventListener('mouseenter', function(e) {
if(typeof exitTimer !== "undefined") {
clearTimeout(exitTimer);
}
enterTimer = setInterval(function() {
if(mesh.material.uniforms.u_height.value >= 0.5) {
mesh.material.uniforms.u_height.value -= 0.05;
} else {
clearTimeout(enterTimer);
}
}, 10);
});
item.addEventListener('mouseleave', function(e) {
if(typeof enterTimer !== "undefined") {
clearTimeout(enterTimer);
}
exitTimer = setInterval(function() {
if(mesh.material.uniforms.u_height.value < 1) {
mesh.material.uniforms.u_height.value += 0.05;
} else {
clearTimeout(exitTimer);
}
}, 10);
});
// Render
renderer.render( scene, camera );
let t = 0;
// Animate
const animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
mesh.material.uniforms.u_time.value = t;
t = t + 0.02;
};
animate();
});
}
}
document.addEventListener("DOMContentLoaded", function(e) {
createWave(config.individualItem, config.colors);
// Get items
const el = document.querySelector(config.individualItem);
const elWidth = parseFloat(window.getComputedStyle(el).width) + parseFloat(window.getComputedStyle(el).marginLeft) + parseFloat(window.getComputedStyle(el).marginRight);
// Track carousel
let mousedown = false;
let movement = false;
let initialPosition = 0;
let selectedItem;
let currentDelta = 0;
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.style.width = `${config.carouselWidth}px`;
});
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.addEventListener('pointerdown', function(e) {
mousedown = true;
selectedItem = item;
initialPosition = e.pageX;
currentDelta = parseFloat(item.querySelector(config.carouselHolderId).style.transform.split('translateX(')[1]) || 0;
});
});
const scrollCarousel = function(change, currentDelta, selectedItem) {
let numberThatFit = Math.floor(config.carouselWidth / elWidth);
let newDelta = currentDelta + change;
let elLength = selectedItem.querySelectorAll(config.individualItem).length - numberThatFit;
if(newDelta <= 0 && newDelta >= -elWidth * elLength) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(${newDelta}px)`;
} else {
if(newDelta <= -elWidth * elLength) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(${-elWidth * elLength}px)`;
} else if(newDelta >= 0) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(0px)`;
}
}
}
document.body.addEventListener('pointermove', function(e) {
if(mousedown == true && typeof selectedItem !== "undefined") {
let change = -(initialPosition - e.pageX);
scrollCarousel(change, currentDelta, document.body);
document.querySelectorAll(`${config.carouselId} a`).forEach(function(item) {
item.style.pointerEvents = 'none';
});
movement = true;
}
});
['pointerup', 'mouseleave'].forEach(function(item) {
document.body.addEventListener(item, function(e) {
selectedItem = undefined;
movement = false;
document.querySelectorAll(`${config.carouselId} a`).forEach(function(item) {
item.style.pointerEvents = 'all';
});
});
});
document.querySelectorAll(config.carouselId).forEach(function(item) {
let trigger = 0;
item.addEventListener('wheel', function(e) {
if(trigger !== 1) {
++trigger;
} else {
let change = e.deltaX * -3;
let currentDelta = parseFloat(item.querySelector(config.carouselHolderId).style.transform.split('translateX(')[1]) || 0;
scrollCarousel(change, currentDelta, item);
trigger = 0;
}
e.preventDefault();
e.stopImmediatePropagation();
return false;
});
});
});





