<div class="block">
<svg class="block__svg svg" viewBox="0 0 1600 900" width="100%" height="100%">
<defs>
<mask id="text-mask" x="0" y="0" width="100%" height="100%">
<text class="svg__mask-line" x="50%" y="96%" text-anchor="middle" fill="#fff">150</text>
</mask>
</defs>
<image href="https://images.unsplash.com/photo-1560114928-40f1f1eb26a0?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=870&q=80" preserveAspectRatio="xMidYMid slice" width="100%" height="100%" mask="url(#text-mask)" />
<g class="svg__first-line">
<text class="svg__line-dark" x="1%" y="65%" text-anchor="start" fill="#000">lorem</text>
<g mask="url(#text-mask)">
<text class="svg__line-light" x="1%" y="65%" text-anchor="start" fill="#fff" >lorem</text>
</g>
</g>
<g class="svg__second-line" stroke="#fff">
<text class="svg__line-dark" x="50%" y="100%" text-anchor="middle" fill="#000">ipsumm</text>
<g mask="url(#text-mask)">
<text class="svg__line-light" x="50%" y="100%" text-anchor="middle" fill="#fff" >ipsumm</text>
</g>
</g>
</svg>
</div>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@700;900&display=swap');
body {
font-family: Roboto, sans-serif;
margin: 0;
height: 400vh;
}
.block {
margin-top: 50vh;
}
.block__svg {
display: block;
}
.svg__mask-line {
font-weight: 900;
font-size: 800px;
}
.svg__first-line {
font-weight: 700;
font-size: 80px;
text-transform: uppercase;
}
.svg__second-line {
font-weight: 700;
font-size: 360px;
text-transform: uppercase;
}
const svg = document.querySelector('.svg');
const textMaskLine = document.querySelector('.svg__mask-line');
const textFirstLineLight = document.querySelector('.svg__first-line .svg__line-light');
const textFirstLineDark = document.querySelector('.svg__first-line .svg__line-dark');
const textSecondLineLight = document.querySelector('.svg__second-line .svg__line-light');
const textSecondLineDark = document.querySelector('.svg__second-line .svg__line-dark');
const svgRect = { x: 0, y: 0, w: 0, h: 0 };
function updateSvgRect() {
const rect = svg.getBoundingClientRect();
svgRect.x = rect.x + window.scrollX;
svgRect.y = rect.y + window.scrollY;
svgRect.w = rect.width;
svgRect.h = rect.height;
}
updateSvgRect();
window.addEventListener('resize', updateSvgRect);
let scrollProgressPrev = clamp((svgRect.y - window.scrollY) / (window.innerHeight - svgRect.h), 0, 1);
function loop(now) {
const y = svgRect.y - window.scrollY;
const scrollProgress = clamp(y / (window.innerHeight - svgRect.h), 0, 1);
scrollProgressPrev = lerp(scrollProgressPrev, scrollProgress, 0.05);
const t = 1 - scrollProgressPrev;
textMaskLine.setAttribute('transform', `translate(0 ${lerp(0, -200, t)})`);
textFirstLineDark.setAttribute('transform', `translate(0 ${lerp(0, -400, t)})`);
textFirstLineLight.setAttribute('transform', `translate(0 ${lerp(0, -400, t)})`);
textSecondLineDark.setAttribute('transform', `translate(0 ${lerp(0, -300, t)})`);
textSecondLineLight.setAttribute('transform', `translate(0 ${lerp(0, -300, t)})`);
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
function clamp(val, min, max) {
return Math.min(Math.max(val, min), max);
}
function lerp(v0, v1, t) {
return v0 * (1 - t) + v1 * t;
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.