<div id="wrap-texture">
<div id="canvas"></div>
<div class="planes">
<div class="draggable-container">
<div class="slider-names-container">
<div class="slider-names">
<div class="slider__name"></div>
<div class="slider__name"></div>
<div class="slider__name"></div>
<div class="slider__name"> </div>
<div class="slider__name"></div>
</div>
</div>
<div class="draggable-hidden"></div>
<div class="draggable-visible">
<div class="plane"><img data-sampler="planeTexture" class="texture" src="https://static.tildacdn.com/tild3539-6536-4739-b561-303438346562/image_76.jpg" crossorigin="anonymous" /></div>
<div class="plane"><img data-sampler="planeTexture" class="texture" src="https://static.tildacdn.com/tild6139-6565-4232-a234-653239623065/scena2_1.jpg" crossorigin="anonymous" /></div>
<div class="plane"><img data-sampler="planeTexture" class="texture" src="https://static.tildacdn.com/tild6135-6134-4437-a362-363965653263/image_73.jpg" crossorigin="anonymous" /></div>
<div class="plane"><img data-sampler="planeTexture" class="texture" src="https://static.tildacdn.com/tild3031-3164-4063-b562-373837653333/scena3_1.jpg" crossorigin="anonymous" /></div>
<div class="plane"><img data-sampler="planeTexture" class="texture" src="https://static.tildacdn.com/tild3637-3263-4638-a331-343638646435/image_74.jpg" crossorigin="anonymous" /></div>
</div>
</div>
</div>
</div>
<script src='https://www.curtainsjs.com/build/curtains.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.6/gsap.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/draggabilly/2.2.0/draggabilly.pkgd.min.js'></script>
<style>
ul {
margin: 0;
padding: 0;
list-style: none;
}
img {
max-width: 100%;
display: block;
}
#wrap-texture {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
#canvas {
/* make the canvas wrapper fits the document */
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index:0;
width: 100%;
height: 100%;
}
#canvas:before {
content: "";
position: fixed;
width: 100%;
height: 100%;
}
.planes {
display: flex;
align-items: center;
width: 100%;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
position: absolute;
cursor: -webkit-grab;
cursor: grab;
white-space: nowrap;
overflow: hidden;
left: 30px;
}
.draggable-container {
position: absolute;
height: 100%;
}
.draggable-visible {
display: grid;
grid-template-columns: repeat(5, minmax(700px, 1fr));
height: 100%;
grid-column-gap: 50px;
}
.draggable-hidden {
z-index: 5;
width: 100%;
height: 100%;
position: absolute;
}
.draggable-hidden .item {
height: 100%;
}
.grabbing {
cursor: -webkit-grabbing;
cursor: grabbing;
}
.plane {
height: 100%;
}
.plane img {
/* hide the img element */
display: none;
-o-object-fit: cover;
object-fit: cover;
width: 100%;
height: 100%;
position: fixed;
}
@media screen and (max-width: 480px) {
.draggable-visible {
grid-template-columns: repeat(5, minmax(350px, 1fr));
}
}
</style>
<script>
console.clear();
const webGLCurtain = new Curtains({ container: "canvas" });
const planeContainer = document.querySelector(".planes");
const planeElements = planeContainer.querySelectorAll(".plane");
const draggableHidden = planeContainer.querySelector(".draggable-hidden");
const draggableVisible = planeContainer.querySelector(".draggable-visible");
const sliderNamesContainer = planeContainer.querySelector(
".slider-names-container"
);
const sliderNames = planeContainer.querySelector(".slider-names");
const names = planeContainer.querySelectorAll(".slider__name");
const items = planeContainer.querySelectorAll(".plane");
const draggie = new Draggabilly(draggableHidden, { axis: "x" });
sliderNamesContainer.style.height =
names[0].getBoundingClientRect().height + "px";
const width = innerWidth;
let mouse = {
currentPosX: 0,
previousPosX: 0,
minPosX: 0,
maxPosX: width - draggableHidden.getBoundingClientRect().width,
isMouseDown: false
};
let sliderNamesProps = {
containerHeight:
sliderNamesContainer.getBoundingClientRect().height -
sliderNames.getBoundingClientRect().height
};
let start = performance.now();
let velocity = 0;
let lastVelocity = 0;
let posNames = 0;
let lastPosNames = 0;
let amplitude = 0.1;
let lastMouse = 0;
let easing = 0.05;
let cancelAnimation = true;
let isOut = false;
const planes = [];
const MathUtils = {
lerp: (a, b, n) => (1 - n) * a + n * b,
map_range: (value, low1, high1, low2, high2) => {
return low2 + ((high2 - low2) * (value - low1)) / (high1 - low1);
}
};
// crear canvas
const createCanvas = () => {
const shader = {
vertex: `
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265359
// those are the mandatory attributes that the lib sets
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
// those are mandatory uniforms that the lib sets and that contain our model view and projection matrix
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 planeTextureMatrix;
// if you want to pass your vertex and texture coords to the fragment shader
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
varying float vDirection;
uniform float uDirection;
void main() {
vec3 position = aVertexPosition;
float x = sin((position.y * 0.5 - 0.5) * PI) * uDirection;
position.x -= x;
gl_Position = uPMatrix * uMVMatrix * vec4(position, 1.0);
// set the varyings
vTextureCoord = (planeTextureMatrix * vec4(aTextureCoord, 0., 1.)).xy;
vVertexPosition = position;
vDirection = uDirection;
}`,
fragment: `
#ifdef GL_ES
precision mediump float;
#endif
#define PI2 6.28318530718
#define PI 3.14159265359
#define S(a,b,n) smoothstep(a,b,n)
// get our varyings
varying vec3 vVertexPosition;
varying vec2 vTextureCoord;
// the uniform we declared inside our javascript
uniform float uTime;
// our texture sampler (default name, to use a different name please refer to the documentation)
uniform sampler2D planeTexture;
varying float vDirection;
void main(){
vec2 uv = vTextureCoord;
float scale = -abs(vDirection) * 0.8;
uv = (uv - 0.5) * scale + uv;
float r = texture2D(planeTexture, vec2(uv.x - vDirection * 0.0, uv.y)).r;
float g = texture2D(planeTexture, vec2(uv.x - vDirection * 0.0, uv.y)).g;
float b = texture2D(planeTexture, vec2(uv.x - vDirection * 0.0, uv.y)).b;
gl_FragColor = vec4(r, g, b, 1.0);
}
`
};
const params = {
vertexShader: shader.vertex, // our vertex shader ID
fragmentShader: shader.fragment, // our framgent shader ID
widthSegments: 40,
heightSegments: 40, // we now have 40*40*6 = 9600 vertices !
uniforms: {
time: {
name: "uTime",
type: "1f",
value: 0
},
direction: {
name: "uDirection",
type: "1f",
value: 0
}
}
};
webGLCurtain.disableDrawing();
// crear nuestros planos
for (let i = 0; i < planeElements.length; i++) {
const plane = webGLCurtain.addPlane(planeElements[i], params);
planes.push(plane);
startPlane(items[i], plane);
}
initEvents();
requestAnimationFrame(onUpdate);
};
const startPlane = (planeElement, plane) => {
plane.onLoading(() => {
plane.mouseOver = false;
planeElement.addEventListener("mouseenter", () => {
plane.mouseOver = true;
webGLCurtain.enableDrawing();
});
planeElement.addEventListener("mouseleave", () => {
plane.mouseOver = false;
});
webGLCurtain.needRender();
});
};
const onUpdate = () => {
if (!cancelAnimation) {
for (let i = 0, l = planes.length; i < l; i++) {
planes[i].uniforms.direction.value = lastVelocity;
planes[i].updatePosition();
}
const { lerp } = MathUtils;
const now = performance.now();
const velocity =
((mouse.currentPosX - lastMouse) / (now - start)) * amplitude;
lastVelocity = lerp(lastVelocity, velocity, easing);
start = now;
lastMouse = mouse.currentPosX;
mouse.previousPosX = lerp(mouse.previousPosX, mouse.currentPosX, easing);
draggableVisible.style.transform = `translate3d(${mouse.previousPosX}px, 0, 0)`;
posNames =
(draggie.position.x / mouse.maxPosX) * sliderNamesProps.containerHeight;
lastPosNames = lerp(lastPosNames, posNames, easing);
sliderNames.style.transform = `translate3d(0,${lastPosNames}px,0)`;
if (Math.round(mouse.previousPosX) === mouse.currentPosX) {
webGLCurtain.disableDrawing();
cancelAnimation = true;
}
}
requestAnimationFrame(onUpdate);
};
// elemento arrastrar
const onDragMove = () => {
cancelAnimation = false;
webGLCurtain.enableDrawing();
// Si esta en el limite se trasladara la mitad del ancho del viewport
if (draggie.position.x > mouse.minPosX) {
mouse.currentPosX = MathUtils.map_range(
draggie.position.x,
0,
innerWidth,
0,
innerWidth / 3
);
} else if (draggie.position.x < mouse.maxPosX) {
mouse.currentPosX = MathUtils.map_range(
draggie.position.x,
mouse.maxPosX,
mouse.maxPosX - innerWidth,
mouse.maxPosX,
mouse.maxPosX - innerWidth / 3
);
} else {
mouse.currentPosX = draggie.position.x;
easing = 0.05;
}
amplitude = 0.1;
};
const onDragStart = (e) => {
planeContainer.style.cursor = "grabbing"; // Aplica al cursor el icono de agarrando;
};
const onDragEnd = () => {
planeContainer.style.cursor = "grab"; // Aplica al cursor el icono de agarrar
// Si esta en el limite volvera a su posicion inicial o final
if (draggie.position.x > mouse.minPosX) {
mouse.currentPosX = 0;
draggie.setPosition(mouse.currentPosX, draggie.position.y);
amplitude = 0;
easing = 0.07;
} else if (draggie.position.x < mouse.maxPosX) {
mouse.currentPosX = mouse.maxPosX;
draggie.setPosition(mouse.currentPosX, draggie.position.y);
amplitude = 0;
easing = 0.07;
} else {
draggie.setPosition(mouse.currentPosX, draggie.position.y);
}
};
// Al reescalar calcula denuevo la posicion maxima
const onResize = () => {
sliderNamesContainer.style.height =
names[0].getBoundingClientRect().height + "px";
mouse.maxPosX =
window.innerWidth - draggableHidden.getBoundingClientRect().width;
sliderNamesProps = {
containerHeight:
sliderNamesContainer.getBoundingClientRect().height -
sliderNames.getBoundingClientRect().height
};
};
const initEvents = () => {
draggie.on("dragMove", () => {
if (isOut) return;
onDragMove();
});
draggie.on("pointerDown", () => {
isOut = false;
onDragStart();
});
draggie.on("pointerUp", onDragEnd);
planeContainer.addEventListener("mouseleave", () => {
isOut = true;
onDragEnd();
});
window.addEventListener("resize", onResize);
};
window.addEventListener("load", createCanvas);
</script>
Ниже создаем блок IM01 загружаем свое изображение и копируем URL путь до изображения
В HTML блоке заменяем URL путь до своего изображения