0

I am trying to create a page (in Wordpress), which is essentially a timeline. As the user scrolls to the next sections, I have a vertical line that "connects" to the next content section. After a LOT of trial and error, I was able to create a line that "draws" itself on scroll, and reverses when scrolling back up. My issue is, when I try to use the code again in the same page, it is already drawn, -- in other words, I *think there is an issue with the code not knowing that is is not supposed to trigger yet. I do not know enough about this to know why it is not working. ideally, I want each line to start drawing as the view-box/browser window is in view.

I have tried creating unique ID's, unique div's and ID's, etc. I originally thought it may be an issue with needing unique containers/ID's. Now, I am *thinking it might be because I do not know how to tell the "line" to not be visible until it is pulled into view.

Here is my pen:

// Get the id of the <path> element and the length of <path>
var triangle = document.getElementById("triangle");
var length = triangle.getTotalLength();

// The start position of the drawing
triangle.style.strokeDasharray = length;

// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
triangle.style.strokeDashoffset = length;

// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);

function myFunction() {
  var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);

  var draw = length * scrollpercent;

  // Reverse the drawing (when scrolling upwards)
  triangle.style.strokeDashoffset = length - draw;
}
body {
  height: 200vh;
}

#mySVG {
  position: relative;
  top: 15%;
  left: 50%;
  width: 50px;
  height: 710px;
}
<svg id="mySVG" preserveAspectRatio="none" viewBox="0 0 4 100">
  <path fill="none" stroke="#000000" stroke-width="1"
    id="triangle" d="M 0 0 V 100 0"/>
</svg>
chrwahl
  • 8,675
  • 2
  • 20
  • 30
Willjamin
  • 21
  • 1

1 Answers1

1

Whenever you need to manipulate multiple elements, you need to query these elements to get an array/list and then loop through all nodes in this array.

Usually it's a better approach to use class names to avoid non-unique IDs like so

let triangles = document.querySelectorAll(".triangle");
    triangles.forEach( (triangle) => {
    // do something
});

So add class names (or just replace id attributes) to your <path> elements and add a scroll EventListener

let triangles = document.querySelectorAll(".triangle");

// calculate path length and init
triangles.forEach((triangle) => {
  let pathLength = triangle.getTotalLength();
  triangle.setAttribute("stroke-dasharray", `0 ${pathLength}`);
});

// Find scroll percentage on scroll 
window.addEventListener("scroll", (e) => {
  drawLines(triangles);
});

function drawLines(triangle, pathLength) {
  var scrollpercent =
    (document.body.scrollTop + document.documentElement.scrollTop) /
    (document.documentElement.scrollHeight -
      document.documentElement.clientHeight);
  triangles.forEach((triangle) => {
    let pathLength = triangle.getAttribute("stroke-dasharray").split(" ")[1];
    var dashLength = pathLength * scrollpercent;
    triangle.setAttribute("stroke-dasharray", `${dashLength} ${pathLength}`);
  });
}
body {
  padding:0 5em;
  height: 200vh;
  margin:0;

}

svg {
  position: relative;
  top: 15%;
  left: 50%;
  width: 50px;
  height: 710px;

}

path{
  transition:0.4s 0.4s;
}
<svg  preserveAspectRatio="none" viewBox="0 0 4 100">
  <path fill="none" stroke="#000000" stroke-width="1" class="triangle" d="M 0 0 V 100 0" />
</svg>

<svg  preserveAspectRatio="none" viewBox="0 0 4 100">
  <path fill="none" stroke="#000000" stroke-width="1" class="triangle" d="M 0 0 V 100 0"/>
</svg>

You can also simplify your stroke animation by using the stroke-dasharray attributes specifying 2 arguments:

  1. dashlength
  2. dash gap
    stroke-dasharray="50 100"

You can even skip the length calculation getTotalLength() by applying these fixed initial attributes pathLength="100" and stroke-dasharray="0 100".
This way you can work with percentages without the need to calculate the exact length of your element.

let triangles = document.querySelectorAll(".triangle");

// Find scroll percentage on scroll 
window.addEventListener("scroll", (e) => {
  drawLines(triangles);
});

function drawLines(triangle, pathLength) {
  var scrollpercent =
    (document.body.scrollTop + document.documentElement.scrollTop) /
    (document.documentElement.scrollHeight -
      document.documentElement.clientHeight);
  triangles.forEach((triangle) => {
    var dashLength = 100 * scrollpercent;
    triangle.setAttribute("stroke-dasharray", `${dashLength} 100`);
  });
}
body {
  padding:0 5em;
  height: 200vh;
  margin:0;
}

svg {
  position: relative;
  top: 15%;
  left: 50%;
  width: 50px;
  height: 710px;
}

path{
  transition:0.4s 0.4s;
}
<svg  preserveAspectRatio="none" viewBox="0 0 4 100">
  <path fill="none" stroke="#000000" stroke-width="1" class="triangle" d="M 0 0 V 100 0" pathLength="100" stroke-dasharray="0 100"/>
</svg>

<svg  preserveAspectRatio="none" viewBox="0 0 4 100">
  <path fill="none" stroke="#000000" stroke-width="1" class="triangle" d="M 0 0 V 100 0" pathLength="100" stroke-dasharray="0 100"/>
</svg>
herrstrietzel
  • 11,541
  • 2
  • 12
  • 34
  • I am speechless. I am getting back to this in a few hours, may I please update you using this new code? I can't tell you enough how thankful I am. Believe me, I tried everything before reaching out. I am sure you are busy, and obviously very skilled. So, just wanted to thank you in advance. i will update you soon. So grateful for this! – Willjamin Jan 24 '23 at 19:46