Инструкция:
Шаг 1
Создаём Zero-блок и вешаем на него класс uc-snake-1
Шаг 2
В этом же Zero-блоке создаём HTML-блок и добавляем код, его можно найти внизу страницы "Добавить в Zero-блок". Размеры блока на скрине ниже
Шаг 3
Создаём необходимое кол. карточек и присваиваем им классы со своим порядковым номером: snake-img-1, snake-img-2, snake-img-3 и тд.

Выравниваем все карточки по гриду, левый верхний угол (автоскейл)
Шаг 4
Далее создаем 2 шейпа:
Первый шейп с классом snake-top - он нужен для запуска анимации.
Второй шейп с классом snake-bottom - он нужен для завершения анимации.
Убираем у них кликабельность с помощью свойства pointer-events: none; в дальнейшем и цвет
Шаг 6
Первый шейп с классом snake-top выравниваем по верхней границе Zero блока и вытягиваем немного вниз. Нижняя граница этого шейпа будет точкой запуска анимации.
Шаг 7
Второй шейп с классом snake-bottom выравниваем по нижней границе Zero блока, а по высоте вытягиваем вверх. Когда мы коснемся её верхней границы - анимация закончится. То растояние, что у вас останется между двумя шейпами и будет продолжительностью анимации. В моём примере это 1860px
Из кода переместите часть "Добавить в Zero-блок" в HTML-блок в Zero-блоке. Не забудьте выключить цвет у snake-top и snake-bottom
Шаг 8
Код из видео (autoscale)

<!--Добавить в Zero-блок-->
</div>
<div class="scene">
<div class="helix" id="helix"></div>
</div>
<!---->
<style>
.t396__elem[class*="snake-img-"] {
zoom: 1 !important;
}
.scene {
width: 100%;
height: 100%;
perspective: 1500px;
display: flex;
align-items: center;
justify-content: center;
}
.helix {
position: relative;
width: 1px;
height: 1px;
transform-style: preserve-3d;
}
</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;
}
let RADIUS; // радиус винта (ширина спирали / расстояние от центра)
let STEP_Y; // вертикальная плотность (шаг по Y между карточками)
let LOOK_AHEAD; // не менять: стабилизирует поворот карточек по ходу движения
let T_END; // количество оборотов винта
function setSnakeParams() {
const w = window.innerWidth;
if (w >= 1200) {
RADIUS = 220;
STEP_Y = 30;
LOOK_AHEAD = 0.015;
T_END = 2;
}
else if (w >= 960) {
RADIUS = 220;
STEP_Y = 30;
LOOK_AHEAD = 0.015;
T_END = 2;
}
else if (w >= 640) {
RADIUS = 220;
STEP_Y = 30;
LOOK_AHEAD = 0.015;
T_END = 2;
}
else if (w >= 480) {
RADIUS = 150;
STEP_Y = 30;
LOOK_AHEAD = 0.015;
T_END = 2;
}
else {
RADIUS = 80;
STEP_Y = 30;
LOOK_AHEAD = 0.015;
T_END = 2;
}
}
setSnakeParams();
let i = 1;
while (true) {
const container = document.querySelector(`.uc-snake-${i}`);
if (!container) break;
const helix = container.querySelector('.helix');
if (!helix) {
i++;
continue;
}
const triggerTop = container.querySelector('.snake-top');
const triggerBottom = container.querySelector('.snake-bottom');
if (!triggerTop || !triggerBottom) {
i++;
continue;
}
const offsetTop = getEffectiveHeight(triggerTop);
const offsetBottom = getEffectiveHeight(triggerBottom);
const containerHeight = container.offsetHeight;
const startTrigger = `top+=${offsetTop} center`;
const endTrigger = `top+=${containerHeight - offsetBottom} center`;
const cards = gsap.utils.toArray(
container.querySelectorAll('[class*="snake-img-"]')
);
if (!cards.length) {
i++;
continue;
}
cards.forEach(card => helix.appendChild(card));
const COUNT = cards.length;
const T_STEP = 1 / (COUNT - 1);
const state = { t: 0 };
function update() {
cards.forEach((card, index) => {
const t = state.t + index * T_STEP;
const tNext = t + LOOK_AHEAD;
const x = Math.sin(t * Math.PI * 2) * RADIUS;
const z = Math.cos(t * Math.PI * 2) * RADIUS;
const y = index * STEP_Y - (COUNT * STEP_Y) / 2;
const x2 = Math.sin(tNext * Math.PI * 2) * RADIUS;
const z2 = Math.cos(tNext * Math.PI * 2) * RADIUS;
const rotY = Math.atan2(x2 - x, z2 - z) * 180 / Math.PI;
const w = card.offsetWidth / 2;
const h = card.offsetHeight / 2;
gsap.set(card, {
transform: `
translate3d(
${x - w}px,
${-y - h}px,
${z}px
)
rotateY(${rotY}deg)
`
});
});
}
update();
gsap.to(state, {
t: T_END,
ease: "none",
scrollTrigger: {
trigger: container,
start: startTrigger,
end: endTrigger,
scrub: true,
markers: false
},
onUpdate: update
});
i++;
}
window.addEventListener('resize', () => {
setSnakeParams();
ScrollTrigger.refresh();
});
});
</script>
Примечание
Если нужно создать новую анимацию в другом Zero-блоке, то у него уже меняем свой порядковый номер на uc-snake-2
© 2022-2025 все права защищены
ИП Нестерчук Кристина Юрьевна ИНН: 251110424315