Прорисовка SVG-элементов c привязкой к прокрутке страницы в границах видимости определенного блока
На основе вопроса: Прорисовка SVG-элементов c привязкой к прокрутке страницы
У меня есть следующий SVG-элемент, анимация прорисовки которого запускается с привязкой к прокрутке всей страницы документа:
let p = document.getElementById('path');
let pl = p.getTotalLength();
p.style.strokeDasharray = pl; p.style.strokeDashoffset = pl;
$(window).on('scroll',function(){
let scrollTo = $(window).scrollTop() /
(document.documentElement.scrollHeight - document.documentElement.clientHeight);
draw = pl * scrollTo;
p.style.strokeDashoffset = pl - draw;
})
body {margin: 0;}
.header, .footer {height: 150vh; background: chocolate;}
.parallax {height: 5000px; background: gold;}
#svg {
position: fixed;
top: 10px;
left: 10px;
}
path {
fill: none;
stroke: black;
stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header"></div>
<div class="parallax">
<!--котейка-->
<svg id="svg" width="500px" height="200px" viewBox="150 0 1 150">
<path id="path" d="M154.5,2.4c0,0,5.5,8.6,2.9,17.5l-1.1-3.4c0,0,0.1,21.1-8.6,26.2l0-4.3c0,0-7.5,22.9-26.8,13.8 C117.2,39.6,110,30.6,99.1,26c-3.8-2.1-8.3-3.2-13.6-3.2c-4.1-0.3-8.4-0.3-13.2,0.3c-22.5,2.6-41.6,20.1-39.4,52 c0.1,0.8,0.2,1.9,0.3,3c-1.5,0.6-3,1.2-4.5,2l-9-10.4l-4.6,16.6L1,87.5l7.8,9.2l-6.3,0.4c0,0,6.4,10.8,10.5,14.2 c11,8.9,21.8,11.5,31.7,10.7c1.5,4.1,3,7.8,4.5,10.8c-3.6-3.6-4.9,2.6-2.3,5.2c1-0.3,1.6,0,2.6-1c1.3,3.3,3.3,2,3.9-0.6 c5.5,6.3,4.6-9.1-0.3-3.6c-1.1-4-1.9-7.9-2.5-11.8c1.4-0.3,2.8-0.7,4.1-1.1c1.4,4.4,2.9,8.5,4.4,11.8c-1.8,0.2-2.1,4.3-0.1,6.3 c1-0.3,1.6,0,2.6-1c1.3,3.3,3.3,2,3.9-0.6c5.5,6.3,4.6-9.1-0.3-3.6l-2,0c-1.2-5.2-2.1-10.3-2.8-15.1c17.9-8,30.1-24.3,30.1-24.3 l-7.4-0.5l10.1-7L75,82.9l0.5-16l-13.9,9.9c2-6.7,5.1-11.4,9.1-13.6c4.5-1.6,9.4-1.4,14.6,1.1c2.9,1.6,5.9,3.9,9.1,7.1 c14.5,17.9,0.4,52-3.9,61.5l0,0c-3.6-3.6-4.9,2.6-2.3,5.2c1-0.3,1.6,0,2.6-1c1.3,3.3,3.3,2,3.9-0.6c5.4,6.2,4.6-8.6-0.1-3.8 c4.6-6.4,9.1-12.8,13.2-19.2c-1.1,6.2-2.9,12.3-5.6,17.9c-3-2-3.9,3.5-1.5,5.9c1-0.3,1.6,0,2.6-1c1.3,3.3,3.3,2,3.9-0.6 c5.5,6.3,4.6-9.1-0.3-3.6l-0.2,0c2.8-6.3,10-23.9,13-42.3c3.5-10,4.9-20.1,3-30.5c5,3.3,12.8,5.4,23.9,0l-3.6-0.7 c0,0,18.4-1.7,22-16.2l-3,2.1C161.9,44.4,176.7,20.6,154.5,2.4z"/>
<!--глазки-->
<path d="M40.5,107.1c0,0-1.9-8.5-8.4-13.3c-0.4-2.5-1-4.1-1.7-4.1c-0.5,0-1,0.9-1.3,2.3c-1.7-0.8-3.6-1.3-5.8-1.4 c0,0-5.7,6.1-1.3,12.4c1.6,2.3,4.4,3.4,7.3,4c0.3,0.9,0.7,1.4,1.1,1.4c0.3,0,0.7-0.4,0.9-1.1C36,107.8,40.5,107.1,40.5,107.1z"/>
<path d="M68.1,86.2c-2.4,0.4-4.4,1.3-6.1,2.4c-0.7-1.4-1.5-2.2-2-2c-0.6,0.2-0.8,1.8-0.6,4.1 c-5.9,5.6-7.2,14.3-7.2,14.3s6,0.3,11.3-1.4c0.5,0.8,1,1.2,1.4,1.1c0.4-0.1,0.6-0.8,0.7-1.9c1.9-0.9,3.6-2.2,4.6-3.9 C73.8,92,68.1,86.2,68.1,86.2z"/>
</svg>
</div>
<div class="footer"></div>
Вопрос: Как запустить анимацию прорисовки SVG-элемента с привязкой к прокрутке страницы, но в границах видимости блока .parallax? Начинаться анимация прорисовки элемента должна, когда верхняя часть блока .parallax касается верхней части окна браузера. Заканчиваться анимация должна, когда нижняя часть блока .parallax касается нижней части окна браузера.
Интересует подробное описание такой реализации и сама реализация любыми средствами и технологиями, указанными в метках вопроса.
Ответы (1 шт):
Bместо htmlElement.scrollTop, как в другом вопросе, используем parallax.getBoundingClientRect() в установленных границах окна просмотра.
const htmlElem = document.querySelector('html')
const parallax = document.querySelector('.parallax')
const path = document.querySelector('#path')
const pathLength = path.getTotalLength()
path.style.strokeDasharray = pathLength
path.style.strokeDashoffset = pathLength
function rangeCalculator(begin, end, beginRange = 0, endRange = 100) {
const inputRange = end - begin
const outRange = endRange - beginRange
return (current) => (
beginRange + (outRange / (inputRange / (current - begin)))
)
}
const calculate = (() => {
let calc
const remake = () => calc = rangeCalculator(0, parallax.getBoundingClientRect().height - htmlElem.clientHeight, 0, pathLength)
window.addEventListener('resize', remake)
remake()
return () => {
const { top, bottom } = parallax.getBoundingClientRect()
// ... когда parallax не достиг верха, это всегда 0
if (top >= 0) {
return 0
}
// ... а когда коснется низа, это всегда pathLength
if (bottom <= htmlElem.clientHeight) {
return pathLength
}
// инвертируем знак, так как у нас parallax.top отрицательный, когда он выше границы
return calc(-top)
}
})()
document.addEventListener("scroll", () => {
path.style.strokeDashoffset = pathLength - calculate()
})
body {
margin: 0;
}
.header,
.footer {
height: 150vh;
background: chocolate;
}
.parallax {
height: 5000px;
background: gold;
}
#svg {
position: fixed;
top: 10px;
left: 10px;
}
path {
fill: none;
stroke: black;
stroke-width: 1px;
}
<div class="header"></div>
<div class="parallax">
<!--котейка-->
<svg id="svg" width="500px" height="200px" viewBox="150 0 1 150">
<path id="path"
d="M154.5,2.4c0,0,5.5,8.6,2.9,17.5l-1.1-3.4c0,0,0.1,21.1-8.6,26.2l0-4.3c0,0-7.5,22.9-26.8,13.8 C117.2,39.6,110,30.6,99.1,26c-3.8-2.1-8.3-3.2-13.6-3.2c-4.1-0.3-8.4-0.3-13.2,0.3c-22.5,2.6-41.6,20.1-39.4,52 c0.1,0.8,0.2,1.9,0.3,3c-1.5,0.6-3,1.2-4.5,2l-9-10.4l-4.6,16.6L1,87.5l7.8,9.2l-6.3,0.4c0,0,6.4,10.8,10.5,14.2 c11,8.9,21.8,11.5,31.7,10.7c1.5,4.1,3,7.8,4.5,10.8c-3.6-3.6-4.9,2.6-2.3,5.2c1-0.3,1.6,0,2.6-1c1.3,3.3,3.3,2,3.9-0.6 c5.5,6.3,4.6-9.1-0.3-3.6c-1.1-4-1.9-7.9-2.5-11.8c1.4-0.3,2.8-0.7,4.1-1.1c1.4,4.4,2.9,8.5,4.4,11.8c-1.8,0.2-2.1,4.3-0.1,6.3 c1-0.3,1.6,0,2.6-1c1.3,3.3,3.3,2,3.9-0.6c5.5,6.3,4.6-9.1-0.3-3.6l-2,0c-1.2-5.2-2.1-10.3-2.8-15.1c17.9-8,30.1-24.3,30.1-24.3 l-7.4-0.5l10.1-7L75,82.9l0.5-16l-13.9,9.9c2-6.7,5.1-11.4,9.1-13.6c4.5-1.6,9.4-1.4,14.6,1.1c2.9,1.6,5.9,3.9,9.1,7.1 c14.5,17.9,0.4,52-3.9,61.5l0,0c-3.6-3.6-4.9,2.6-2.3,5.2c1-0.3,1.6,0,2.6-1c1.3,3.3,3.3,2,3.9-0.6c5.4,6.2,4.6-8.6-0.1-3.8 c4.6-6.4,9.1-12.8,13.2-19.2c-1.1,6.2-2.9,12.3-5.6,17.9c-3-2-3.9,3.5-1.5,5.9c1-0.3,1.6,0,2.6-1c1.3,3.3,3.3,2,3.9-0.6 c5.5,6.3,4.6-9.1-0.3-3.6l-0.2,0c2.8-6.3,10-23.9,13-42.3c3.5-10,4.9-20.1,3-30.5c5,3.3,12.8,5.4,23.9,0l-3.6-0.7 c0,0,18.4-1.7,22-16.2l-3,2.1C161.9,44.4,176.7,20.6,154.5,2.4z" />
<!--глазки-->
<path
d="M40.5,107.1c0,0-1.9-8.5-8.4-13.3c-0.4-2.5-1-4.1-1.7-4.1c-0.5,0-1,0.9-1.3,2.3c-1.7-0.8-3.6-1.3-5.8-1.4 c0,0-5.7,6.1-1.3,12.4c1.6,2.3,4.4,3.4,7.3,4c0.3,0.9,0.7,1.4,1.1,1.4c0.3,0,0.7-0.4,0.9-1.1C36,107.8,40.5,107.1,40.5,107.1z" />
<path
d="M68.1,86.2c-2.4,0.4-4.4,1.3-6.1,2.4c-0.7-1.4-1.5-2.2-2-2c-0.6,0.2-0.8,1.8-0.6,4.1 c-5.9,5.6-7.2,14.3-7.2,14.3s6,0.3,11.3-1.4c0.5,0.8,1,1.2,1.4,1.1c0.4-0.1,0.6-0.8,0.7-1.9c1.9-0.9,3.6-2.2,4.6-3.9 C73.8,92,68.1,86.2,68.1,86.2z" />
</svg>
</div>
<div class="footer"></div>