Section Scroll Bend

Scroll
JS
SVG
Last Updated: Mar 3, 2026
  • Works best with Lenis Smooth Scroll enabled
  • The height and top margin on .scroll-7_svg must use a matching clamp
<script>
document.addEventListener("DOMContentLoaded", function () {
	document.querySelectorAll(".scroll-7_wrap").forEach((component) => {
		if (component.hasAttribute("data-scroll-7")) return;
		component.setAttribute("data-scroll-7", "");

		const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
		if (prefersReducedMotion) return;

		const path = component.querySelector('path');
		const SVG_W = 1200;
		const SVG_H = 400;
		const BASE_Y = 200;
		const SENSITIVITY = 5;
		const DAMPING = 0.9;
		const MAX_BEND = 240;
		let currentBend = 0;
		let lastScrollY = window.scrollY;
		let smoothVelocity = 0;
		let animating = false;
		function updateCurve() {
			const scrollY = window.scrollY;
			const velocity = scrollY - lastScrollY;
			lastScrollY = scrollY;
			smoothVelocity += (velocity - smoothVelocity) * 0.5;
			const target = Math.max(-MAX_BEND, Math.min(MAX_BEND, smoothVelocity * SENSITIVITY));
			currentBend += (target - currentBend) * DAMPING;
			if (Math.abs(currentBend) < 0.1) currentBend = 0;
			const controlY = BASE_Y + currentBend;
			path.setAttribute('d', `M 0,${BASE_Y} Q ${SVG_W / 2},${controlY} ${SVG_W},${BASE_Y} L ${SVG_W},${SVG_H} L 0,${SVG_H} Z`);
			if (Math.abs(currentBend) > 0.1 || Math.abs(smoothVelocity) > 0.5) {
				requestAnimationFrame(updateCurve);
			} else {
				smoothVelocity = 0;
				animating = false;
			}
		}
		window.addEventListener('scroll', () => {
			if (!animating) {
				animating = true;
				requestAnimationFrame(updateCurve);
			}
		}, { passive: true });

	});
});
</script>