<style>
[class^='mdw-curved-slider'],
[class*=' mdw-curved-slider'] {
height: var(--min-height, 100vh);
overflow: hidden;
position: relative;
}
[class^='mdw-curved-slider'] canvas,
[class*=' mdw-curved-slider'] canvas {
position: absolute;
top: 0;
left: 0;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.160.0/three.min.js"></script>
<script>
if (!MDWNonce111) {
var MDWNonce111 = true;
const selector = "[class^='mdw-curved-slider'], [class*=' mdw-curved-slider']";
const scene = [], renderer = [], options = [], camera = [];
const planes = [], slideAmount = [], time = [];
document.addEventListener('DOMContentLoaded', function () {
function getWidth(gap) {
return 1 + gap / 100;
}
function getPlaneWidth(el, camera) {
const vFov = camera.fov * Math.PI / 180;
const height = 2 * Math.tan(vFov / 2) * camera.position.z;
const aspect = el.clientWidth / el.clientHeight;
const width = height * aspect;
return el.clientWidth / width;
}
function init() {
Array.from(document.querySelectorAll(selector)).forEach(function (el, index) {
const className = el.getAttribute('class');
const classNameIndex = className.indexOf('mdw-curved-slider');
const shortClass = className.substring(classNameIndex, className.indexOf(' ', classNameIndex));
const values = shortClass.split('-');
options[index] = {
speed: 300,
gap: 10,
curve: 15,
direction: -1
};
values.forEach(function (value, i) {
if (value == 'speed' && values[i + 1] && !isNaN(values[i + 1])) {
options[index].speed = values[i + 1];
}
if (value == 'gap' && values[i + 1] && !isNaN(values[i + 1])) {
options[index].gap = values[i + 1];
}
if (value == 'curve' && values[i + 1] && !isNaN(values[i + 1])) {
options[index].curve = values[i + 1];
}
if (value == 'reverse') { options[index].direction = 1; }
});
const images = [];
time[index] = 0;
Array.from(el.querySelectorAll('.elementor-widget-image-gallery .gallery-item')).forEach(function (el) {
images.push(el.querySelector('img').getAttribute('src'));
});
slideAmount[index] = images.length;
const allImages = [...images];
scene[index] = new THREE.Scene();
camera[index] = new THREE.PerspectiveCamera(75, el.clientWidth / el.clientHeight, 0.1, 20);
camera[index].position.z = 2;
renderer[index] = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer[index].setSize(el.clientWidth, el.clientHeight);
renderer[index].setPixelRatio(window.devicePixelRatio);
const previousCanvas = el.querySelector('canvas');
if (previousCanvas) el.removeChild(previousCanvas);
el.appendChild(renderer[index].domElement);
const geometry = new THREE.PlaneGeometry(1, 1, 20, 20);
const planeSpace = getPlaneWidth(el, camera[index]) * getWidth(options[index].gap);
const totalImage = Math.ceil(el.clientWidth / planeSpace) + 1 + images.length;
const initialOffset = Math.ceil(el.clientWidth / (2 * planeSpace) - 0.5);
for (let i = slideAmount[index]; i < totalImage; i++) {
allImages.push(images[i % slideAmount[index]]);
}
planes[index] = [];
allImages.forEach(function (image, i) {
const loader = new THREE.TextureLoader();
loader.load(
image,
function (texture) {
const material = new THREE.ShaderMaterial({
uniforms: {
tex: { value: texture },
curve: { value: options[index].curve }
},
vertexShader: `
uniform float curve;
varying vec2 vertexUV;
void main(){
vertexUV = uv;
vec3 newPosition = position;
float distanceFromCenter = abs(modelMatrix*vec4(position, 1.0)).x;
newPosition.y *= 1.0 + (curve/100.0)*pow(distanceFromCenter,2.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`,
fragmentShader: `
uniform sampler2D tex;
varying vec2 vertexUV;
void main(){
gl_FragColor = texture2D(tex, vertexUV);
}
`
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.x = -1 * options[index].direction * (i - initialOffset) * getWidth(options[index].gap);
mesh.frustumCulled = false;
planes[index].push(mesh);
scene[index].add(mesh);
}
);
});
});
}
init();
// ✅ Scroll tracking system
let previousScrollY = window.scrollY;
let scrollPosition = 0;
let smoothedScroll = 0;
function animate() {
scrollPosition = window.scrollY;
// Smooth the scroll position with easing (0.1 = ease factor)
smoothedScroll += (scrollPosition - smoothedScroll) * 0.1;
const scrollDelta = smoothedScroll - previousScrollY;
Array.from(document.querySelectorAll(selector)).forEach(function (el, index) {
if (!planes[index]) return;
const direction = scrollDelta > 0 ? 1 : -1;
const deltaAmount = Math.abs(scrollDelta) * 0.01;
const planeWidth = getWidth(options[index].gap);
const loopWidth = planeWidth * planes[index].length;
planes[index].forEach(function (plane) {
plane.position.x += direction * deltaAmount;
if (plane.position.x > loopWidth / 2) {
plane.position.x -= loopWidth;
} else if (plane.position.x < -loopWidth / 2) {
plane.position.x += loopWidth;
}
});
renderer[index].render(scene[index], camera[index]);
});
previousScrollY = smoothedScroll;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
});
}
</script>