Инструкция:
Шаг 1
Создаём Zero-блок и вешаем на него класс uc-joker-card-1
Шаг 2
В этом же Zero-блоке создаём HTML-блок и добавляем код, его можно найти внизу страницы "Добавить в Zero-блок". Размеры блока на скрине ниже
Шаг 3
Создаём 8 одинаковых карточек тех же размеров, что и html-блок, присваиваем им классы.
Передние стороны:
joker-card-after-1 (2,3,4)
Задние стороны:
joker-card-before-1 (2,3,4)

Выравниваем все карточки по гриду, левый верхний угол (автоскейл)
Шаг 4
Создаём пошаговую анимацию для HTML-блока:
Event: on Scroll;
Start trigger: on Window center;
Trigger offset: -172 (это половина высоты самого блока, возможно в вашем случае будет другое значение, высота моего 344px);
На первый шаг задаём нужную дистанцию к примеру 700px и фиксируем её (fix: fixed).
Шаг 5
Далее создаем 2 шейпа:
Первый шейп с классом joker-card-top - он нужен для запуска анимации.
Второй шейп с классом joker-card-bottom- он нужен для завершения анимации.
Убираем у них кликабельность с помощью свойства pointer-events: none; в дальнейшем и цвет
Шаг 6
Первый шейп с классом joker-card-top выравниваем по вверхней границе Zero блока, а по высоте вытягиваем до html, что анимируем + половина высоты HTML (у меня 172px). К примеру: HTML-блок стоит по Y на 150px, значит высота joker-card-top должна быть 150px + 172px = 322px.
Шаг 7
Второй шейп с классом joker-card-bottom выравниваем по нижней границе Zero блока, а по высоте вытягиваем до joker-card-top с учетом отступа в продолжительность анимации. То есть, то пространство (отступ) что вы оставите между двумя этими шейпами и будет продолжительность анимации. joker-card-top и joker-card-bottom - это своего рода отступы, которые позволяют контролировать продолжительность анимации. В моём случае отступ между ними в 500px
Из кода переместите часть "Добавить в Zero-блок" в HTML-блок в Zero-блоке. Не забудьте выключить цвет у joker-card-top и joker-card-bottom
Шаг 8
Код из видео (autoscale)

<!--Добавить в Zero-блок-->
<div class="joker-wrapper-inside-1">
<div class='joker-wrapper-1'>
<div class="joker-wrapper-inside-anim-1">
<div class='joker-1 joker-card-1'></div>
<div class='joker-2 joker-card-2'></div>
<div class='joker-3 joker-card-3'></div>
<div class='joker-4 joker-card-4'></div>
</div>
</div>
</div>
<!---->
<script>
[1, 2].forEach(function(setNum) {
for (let cardNum = 1; cardNum <= 4; cardNum++) {
const baseSelector = `.uc-joker-card-${setNum} .joker-card-${cardNum}`;
$(`.uc-joker-card-${setNum} .joker-card-before-${cardNum}`).appendTo(baseSelector);
$(`.uc-joker-card-${setNum} .joker-card-after-${cardNum}`).appendTo(baseSelector);
}
});
</script>
<style>
.t396__elem[class*="joker-card-anim-"] {
perspective: 2000px;
transform-style: preserve-3d;
}
.t396__elem[class*="joker-content-after-"] {
zoom: 0 !important;
}
[class^="joker-wrapper-"] {
width: 100%;
height: 100%;
}
[class^="joker-wrapper-inside-"] {
perspective: 2000px;
backface-visibility: hidden;
width: 100%;
height: 100%;
transform-style: preserve-3d;
}
.t396__elem[class*="joker-card-after-"] {
transform: rotateY(-180deg);
}
}
[class^="joker-card-before-"] .tn-atom,
[class^="joker-card-after-"] .tn-atom {
backface-visibility: hidden;
}
[class^="joker-card-before-wrapper-"] div,
[class^="joker-card-after-wrapper-"] div,
.t396__elem[class*="joker-card-after-"],
.t396__elem[class*="joker-card-before-"] {
perspective: 2000px;
backface-visibility: hidden;
transform-style: preserve-3d;
}
[class^="joker-card-before-wrapper-"],
[class^="joker-card-after-wrapper-"] {
perspective: 2000px;
transform-style: preserve-3d;
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
}
[class^="joker-card-after-wrapper-"] {
transform: rotateY(-180deg);
}
[class^="joker-inside-"] {
backface-visibility: hidden;
}
[class^="joker-"] {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
perspective: 2000px;
transform-style: preserve-3d;
}
@media only screen and (min-width: 320px) {
[class^="joker-card-before-"],
[class^="joker-card-after-"] {
backface-visibility: hidden;
zoom: 0 !important;
}
[class^="joker-card-before-wrapper-"] div,
[class^="joker-card-after-wrapper-"] div,
.t396__elem[class*="joker-card-after-"],
.t396__elem[class*="joker-card-before-"] {
zoom: 0 !important;
}
}
</style>
<script src="https://matilda-design.ru/library/GSAP.js"></script>
<script src="https://matilda-design.ru/library/ScrollTrigger.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
gsap.registerPlugin(ScrollTrigger);
function getEffectiveHeight(elem) {
if (!elem) return 0;
const height = elem.offsetHeight;
const zoom = parseFloat(getComputedStyle(elem).zoom || "1");
return height * zoom;
}
const width = window.innerWidth;
const isMobile = width >= 320 && width <= 479;
const isSmallTablet = width >= 480 && width <= 639;
const isTablet = width >= 640 && width <= 959;
const isLaptop = width >= 960 && width <= 1199;
const isLarge = width >= 1200;
let initialTransforms;
if (isMobile) {
initialTransforms = {
1: { x: 0, y: 0, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
2: { x: 0, y: 430, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
3: { x: 0, y: 860, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
4: { x: 0, y: 1290, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
};
} else if (isSmallTablet) {
initialTransforms = {
1: { x: -120, y: 0, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
2: { x: 120, y: 0, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
3: { x: -120, y: 330, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
4: { x: 120, y: 330, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
};
} else if (isTablet) {
initialTransforms = {
1: { x: -160, y: 0, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
2: { x: 160, y: 0, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
3: { x: -160, y: 440, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
4: { x: 160, y: 440, z: 0, rotation: 0, rotationX: 0, rotationY: 0, rotationZ: 0 },
};
} else if (isLaptop) {
initialTransforms = {
1: { x: 0, y: 0, z: 20, rotation: 5, rotationX: 0, rotationY: 0, rotationZ: 0 },
2: { x: 0, y: 0, z: 10, rotation: 2, rotationX: 0, rotationY: 0, rotationZ: 0 },
3: { x: 0, y: 0, z: 0, rotation: -2, rotationX: 0, rotationY: 0, rotationZ: 0 },
4: { x: 0, y: 0, z: -10, rotation: -5, rotationX: 0, rotationY: 0, rotationZ: 0 },
};
} else if (isLarge) {
initialTransforms = {
1: { x: 0, y: 0, z: 20, rotation: 5, rotationX: 0, rotationY: 0, rotationZ: 0 },
2: { x: 0, y: 0, z: 10, rotation: 2, rotationX: 0, rotationY: 0, rotationZ: 0 },
3: { x: 0, y: 0, z: 0, rotation: -2, rotationX: 0, rotationY: 0, rotationZ: 0 },
4: { x: 0, y: 0, z: -10, rotation: -5, rotationX: 0, rotationY: 0, rotationZ: 0 },
};
} else {
initialTransforms = {
1: { x: 0, y: 0, z: 20, rotation: 5, rotationX: 0, rotationY: 0, rotationZ: 0 },
2: { x: 0, y: 0, z: 10, rotation: 2, rotationX: 0, rotationY: 0, rotationZ: 0 },
3: { x: 0, y: 0, z: 0, rotation: -2, rotationX: 0, rotationY: 0, rotationZ: 0 },
4: { x: 0, y: 0, z: -10, rotation: -5, rotationX: 0, rotationY: 0, rotationZ: 0 },
};
}
function getAnimParams() {
if (isMobile) {
return [
[{ y: 0, rotationY: 0 }, { rotationY: 180 }],
[{ y: 430, rotationY: 0, delay: 0.4 }, { rotationY: 180, delay: 0.4 }],
[{ y: 860, rotationY: 0 }, { rotationY: 180, delay: 5 }],
[{ y: 1290, rotationY: 0 }, { rotationY: 180, delay: 7.5 }]
];
} else if (isSmallTablet) {
return [
[{ x: -120, rotationY: 0 }, { rotationY: 180 }],
[{ x: 120, rotationY: 0, delay: 0.4 }, { rotationY: 180, delay: 0.4 }],
[{ x: -120, rotationY: 0, y: 330, delay: 0.8 }, { rotationY: 180, delay: 0.8 }],
[{ x: 120, rotationY: 0, y: 330, delay: 1.2 }, { rotationY: 180, delay: 1.2 }]
];
} else if (isTablet) {
return [
[{ x: -160, rotationY: 0 }, { rotationY: 180 }],
[{ x: 160, rotationY: 0, delay: 0.4 }, { rotationY: 180, delay: 0.4 }],
[{ x: -160, y: 440, rotationY: 0, delay: 0.8 }, { rotationY: 180, delay: 0.8 }],
[{ x: 160, y: 440, rotationY: 0, delay: 1.2 }, { rotationY: 180, delay: 1.2 }]
];
} else if (isLaptop) {
return [
[{ x: 300, rotationY: 30, rotationX: 10, rotationZ: 20 }, { x: 357, rotationY: 180, rotationX: 0, rotationZ: 0 }],
[{ x: 100, rotationY: 30, rotationX: 10, rotationZ: 15 }, { x: 120, rotationY: 180, rotationX: 0, rotationZ: 0 }],
[{ x: -100, rotationY: 30, rotationX: 10, rotationZ: 0 }, { x: -120, rotationY: 180, rotationX: 0, rotationZ: 0 }],
[{ x: -300, rotationY: 30, rotationX: 10, rotationZ: -15 }, { x: -357, rotationY: 180, rotationX: 0, rotationZ: 0 }]
];
} else if (isLarge) {
return [
[{ x: 300, rotationY: 30, rotationX: 10, rotationZ: 20 }, { x: 405, rotationY: 180, rotationX: 0, rotationZ: 0 }],
[{ x: 100, rotationY: 30, rotationX: 10, rotationZ: 15 }, { x: 135, rotationY: 180, rotationX: 0, rotationZ: 0 }],
[{ x: -100, rotationY: 30, rotationX: 10, rotationZ: 0 }, { x: -135, rotationY: 180, rotationX: 0, rotationZ: 0 }],
[{ x: -300, rotationY: 30, rotationX: 10, rotationZ: -15 }, { x: -405, rotationY: 180, rotationX: 0, rotationZ: 0 }]
];
}
}
const animParams = getAnimParams();
let i = 1;
while (true) {
const container = document.querySelector(`.uc-joker-card-${i}`);
if (!container) break;
const triggerStart = container.querySelector(`.joker-card-top`);
const triggerEnd = container.querySelector(`.joker-card-bottom`);
if (!triggerStart || !triggerEnd) {
i++;
continue;
}
const offsetTop = getEffectiveHeight(triggerStart);
const offsetBottom = getEffectiveHeight(triggerEnd);
const containerHeight = container.offsetHeight;
const startTrigger = `top+=${offsetTop} center`;
const endTrigger = `top+=${containerHeight - offsetBottom} center`;
const cardsCount = 4;
for (let c = 1; c <= cardsCount; c++) {
const card = container.querySelector(`.joker-card-${c}`);
if (!card) continue;
const init = initialTransforms[c];
if (init) {
gsap.set(card, {
x: init.x,
y: init.y,
z: init.z,
rotation: init.rotation,
rotationX: init.rotationX,
rotationY: init.rotationY,
rotationZ: init.rotationZ,
});
}
}
for (let c = 1; c <= cardsCount; c++) {
const card = container.querySelector(`.joker-card-${c}`);
if (!card) continue;
const [startAnim, endAnim] = animParams[c - 1] || [{}, {}];
const tl = gsap.timeline({
scrollTrigger: {
trigger: container,
start: startTrigger,
end: endTrigger,
scrub: true,
markers: false
}
});
tl.to(card, {
...startAnim,
duration: 1,
ease: "none",
});
tl.to(card, {
...endAnim,
duration: 1,
ease: "none",
});
}
i++;
}
});
</script>
Примечание
Если нужно создать новую анимацию в другом Zero-блоке, то у него уже меняем свой порядковый номер на uc-joker-card-2
© 2022-2025 все права защищены
ИП Нестерчук Кристина Юрьевна ИНН: 251110424315
ЗАДАТЬ ВОПРОС