I have created a path for an image to move along when scrolling and I want text boxes to be placed next to the extremes of the curves. I can move these boxes to a specific positions however the problem I have then is that when people with different screens sizes or when resizing the window the path moves but the text boxes not making them misaligned. Preferably I would also make these text boxes open when the image moves past the extremes but this is optional.
The code I created is provided below and I simply do not know how to continue from here. I am satisfied with the path the image moves along although if someone has advice for improvement this is more than welcome.
var scrollY = 0;
var path = document.getElementById("path");
var car = document.getElementById("c");
document.addEventListener("scroll", function() {
scrollY = window.pageYOffset;
positionBac();
});
function positionBac() {
var maxScrollY = document.documentElement.scrollHeight - window.innerHeight;
var pathLen = path.getTotalLength();
var dist = pathLen * scrollY / maxScrollY;
var pos = path.getPointAtLength(dist);
var angle = calculateAngle(path, pathLen, dist, pos);
car.setAttribute("transform", "translate(" + pos.x + "," + pos.y + ") rotate(" + (rad2deg(angle) + 90) + ")");
}
function calculateAngle(path, pathLen, dist, pos) {
if (dist + 1 <= pathLen) {
var posAhead = path.getPointAtLength(dist + 1);
return Math.atan2(posAhead.y - pos.y, posAhead.x - pos.x);
} else {
var posBehind = path.getPointAtLength(dist - 1);
return Math.atan2(pos.y - posBehind.y, pos.x - posBehind.x);
}
}
function rad2deg(rad) {
return 180 * rad / Math.PI;
}
positionBac();
.route {
width: 90%;
z-index: 1;
position: absolute;
}
.svg {
width: 90%;
height: auto;
}
.box {
width: 150px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 0px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
background: #98CC9B;
border-radius: 6px;
}
.box:nth-child(even) {
margin-left: auto;
padding-left: 200px;
}
.box:nth-child(odd) {
margin-right: auto;
padding-right: 200px;
}
<div id="route">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 2500" id="svgRoute">
<path id="path" d="M500 0
C500 100 1200 300 500 500
C500 500 -200 700 500 900
C500 900 1200 1100 500 1300
C500 1300 -200 1500 500 1700
C500 1700 1200 1900 800 2100
C800 2100 400 2300 400 2500"
stroke-width="3px" stroke="green" fill="none"/>
<image id="c" x="-20" y="-70" width="40" height="70" xlink:href="nani.png"/>
</svg>
</div>
<div class="box">this</div>
<div class="box">is</div>
<div class="box">an</div>
<div class="box">example</div>
<div class="box">Box</div>
I've tried to attach the divs to points at the curve so when you resize the window the boxes remain aligned. This simply doesn't work and I cannot seem to solve this issue.
We know when the car turns (the angle changes from less than 180 to more than 180 or vice versa) and we know its position in terms of the path.
So, we can work out what bend it is on (or near) and show the relevant text at the car's position - thus avoiding having to do any calculation of where a bend actually is.
<!doctype html>
<html>
<body>
<div class="box bend0">this</div>
<div class="box bend1">is</div>
<div class="box bend2">an</div>
<div class="box bend3">example</div>
<div class="box bend4 ">Box</div>
<!--<div class="box bend5 "></div><!-- there is a bend at the very end of the path -->-->
<div class="container">
<div id="route">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 2500" id="svgRoute">
<path id="path" d="M500 0
C500 100 1200 300 500 500
C500 500 -200 700 500 900
C500 900 1200 1100 500 1300
C500 1300 -200 1500 500 1700
C500 1700 1200 1900 800 2100
C800 2100 400 2300 400 2500"
stroke-width="3px" stroke="green" fill="none"/>
<image id="c" x="-20" y="-70" width="40" height="70" xlink:href="nani.png"/>
</svg>
</div>
<script>
bendsY = [500, 900, 1300, 1700, 2100, 2300];
var scrollY = 0;
var path = document.getElementById("path");
var car = document.getElementById("c");
let prevAngle = 0;
let curAngle = 0;
let bend = -1;
let boxes = document.querySelectorAll('.box');
const numBends = boxes.length;
let prevY = 0;
let curY = 0;
let goingDown = true;
document.addEventListener("scroll", function() {
scrollY = window.pageYOffset;
//event.preventDefault();
//event.stopPropagation();
positionBac();
});
function hideBoxes() {
boxes.forEach(box => {
box.style.opacity = 0;
});
}
hideBoxes();
function positionBac() {
var maxScrollY = document.documentElement.scrollHeight - window.innerHeight;
var pathLen = path.getTotalLength();
var dist = pathLen * scrollY / maxScrollY;
var pos = path.getPointAtLength(dist);
var angle = calculateAngle(path, pathLen, dist, pos);
function findBend() {
for (let i = 0; i < numBends; i++) {
if (pos.y <= bendsY[i]) {
bend = i;
break;
}
}
}
function showText() {
findBend();
hideBoxes();
const el = document.querySelector('.box.bend' + bend);
el.style.opacity = 1;
el.style.top = (document.querySelector('image').getBoundingClientRect().y + scrollY) + 'px';
}
car.setAttribute("transform", "translate(" + pos.x + "," + pos.y + ") rotate(" + (rad2deg(angle) + 90) + ")");
prevAngle = curAngle;
curAngle = rad2deg(angle) + 90;
prevY = curY;
curY = scrollY;
if (curY <= .1) {
bend = -1;
goingDown = true;
}
if (curY > prevY) { //going down
if (bend < (numBends - 1)) {
if (atABend()) {
if (!goingDown) {
goingDown = true;
} else if (bend < (numBends - 1)) {
showText();
}
}
}
} else { //going up
if (bend > -1) {
if (atABend()) {
if (goingDown) {
goingDown = false;
} else if (bend > 0) {
showText();
}
}
}
}
}
function atABend() {
return (prevAngle < 180 && curAngle >= 180) || (prevAngle > 180 && curAngle <= 180);
}
function calculateAngle(path, pathLen, dist, pos) {
if (dist + 1 <= pathLen) {
var posAhead = path.getPointAtLength(dist + 1);
return Math.atan2(posAhead.y - pos.y, posAhead.x - pos.x);
} else {
var posBehind = path.getPointAtLength(dist - 1);
return Math.atan2(pos.y - posBehind.y, pos.x - posBehind.x);
}
}
function rad2deg(rad) {
return 180 * rad / Math.PI;
}
function reset() {
window.scrollTo(0, 0);
scrollY = 0;
positionBac();
}
window.onresize = reset;
positionBac();
</script>
<style>
* {
margin: 0;
}
.route {
width: 90%;
z-index: 1;
position: absolute;
}
.svg {
width: 90%;
height: auto;
}
.box {
width: 15%;
aspect-ratio: 3 / 2;
display: flex;
align-items: center;
justify-content: center;
margin-top: 0px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
background: #98CC9B;
border-radius: 6px;
opacity: 0;
position: absolute;
}
.box:nth-child(odd) {
right: 0;
}
.box:nth-child(even) {
left: 0;
}
</style>
https://stackoverflow.com/questions/76913450/attaching-a-div-to-a-path-of-a-moving-image-and-make-it-keep-its-relative-positi
</body>
</html>