Position any kind of elements on SVG Curves

Viewed 187

I want to position elements on along an SVG curve. I found that we can use textpath to position text along the path of an SVG can we do the same with list-elements? If so how to achieve that functionality

Html for positioning text along an SVG

<svg viewBox="0 0 425 300">
    <path id="curve"
        d="M6,150C49.63,93,105.79,36.65,156.2,47.55,207.89,58.74,213,131.91,264,150c40.67,14.43,108.57-6.91,229-145" />
    <text x="25">
        <textPath xlink:href="#curve">
            This text is now curved
        </textPath>
    </text>
</svg>

CSS for the same - Just makes it look nice

body {
  background-color: #333;
  font-family: 'Luckiest Guy', cursive;
  font-size: 40px;
}

path {
  fill: transparent;
}

text {
  fill: #FF9800;
}  

body {
  background-color: #333;
  font-family: 'Luckiest Guy', cursive;
  font-size: 40px;
}

path {
  fill: transparent;
}

text {
  fill: #FF9800;
}
<svg viewBox="0 0 425 300">
    <path id="curve"
        d="M6,150C49.63,93,105.79,36.65,156.2,47.55,207.89,58.74,213,131.91,264,150c40.67,14.43,108.57-6.91,229-145" />
    <text x="25">
        <textPath xlink:href="#curve">
            This text is now curved
        </textPath>
    </text>
</svg>

Can I achieve this functionality for other elements? list elements for example

Note: My final objective is to position small circles (filled) along the curve that are equally spaced.

I'm not very well versed with CSS so if you could point me to resources that will help me achieve this functionality I'll be very grateful

Thanks in Advance

3 Answers

You can't do what you want with CSS.

You'll need to use some Javascript. Here's a little example where you can adjust the number of circles, and they will be spaced out evenly along the total length of the path.

let countField = document.getElementById("count");


function updateCircles()
{
   let n = countField.value;
   // Find the curve path element
   let curve = document.getElementById("curve");
   // Get the total length of the path
   let curveLength = curve.getTotalLength();
   // Calculate the spacing between the circles
   let spacing = curveLength / (n - 1);
   
   let svg = curve.ownerSVGElement;
   // Remove any existing circles
   let oldCircles = svg.querySelectorAll("circle");
   oldCircles.forEach(oldCircle => svg.removeChild(oldCircle));
   
   // Add the n new circles
   for (var i = 0; i < n; i++)
   {
      // Get the x,y position of a point x along the path
      let coords = curve.getPointAtLength(i * spacing);
      // Make a circle
      let circle = document.createElementNS(svg.namespaceURI, "circle");
      circle.cx.baseVal.value = coords.x;
      circle.cy.baseVal.value = coords.y;
      circle.r.baseVal.value = 6;
      // Append it to the SVG
      svg.appendChild(circle);
   }
}


// Update the circles at the start to show the initial set of circles
updateCircles();
// Update the circles whenever the input changes
countField.addEventListener("input", updateCircles);
path {
  fill: none;
  stroke: linen;
}

circle {
  fill: rgba(0,0,0, 0.5);
}
<svg viewBox="0 0 500 160">
    <path id="curve"
        d="M6,150C49.63,93,105.79,36.65,156.2,47.55,207.89,58.74,213,131.91,264,150c40.67,14.43,108.57-6.91,229-145" />
</svg>


<input type="range" id="count" value="4" min="3" max="32"/>

You can't place shapes arbitrarily along a path in SVG using just CSS. Your choices here are:

  • Don't use a shape, use a text element with tabs and circle characters instead ( e.g. Unicode circle (U+23FA)) and change the font-color to whatever you want
  • Use a marker - but placement of these is set to the start, middle & end of each curve segment (marker tutorial)
  • Use javascript (e.g. pointAtLength) to calculate the locations of points along a path that you would like to draw circles and set the cx, cy of your circles to those points.

Note: My final objective is to position small circles (filled) along the curve that are equally spaced.

textPath is used to place text along a curve. But you can use Unicode characters instead of text characters.

By using textPath and placing unicode characters inside

<tspan dx =" 5 "fill =" gold "> &#9899; </ tspan> tags, you can easily place as many circles as you like on one curve.

Here is a link to the unicode characters of the various circles.

Unicode characters can be styled - fill =" red ", resized - font-size =" 32px " set the distance between circles dx =" 10 "

<svg width="600" height="400" viewBox="80 100 400 300">
 <path id="pathChain" d="M100,200 C100,100 250,100 250,200 S 400,300 400,200" stroke="skyblue" fill="none"/>
<text font-size="14px" x="0" y="0" font-family="Times New Roman" fill="grey" shape-rendering ="crispEdges" >
<textPath id="result"    xlink:href="#pathChain">
  <tspan dx="0" dy="-2" fill="green" > &#9899; </tspan> <tspan dx="5" dy="-2" fill="gold"> &#9899;</tspan><tspan dx="5" fill="crimson"> &#9899;</tspan><tspan dx="10" fill="dodgerblue">  &#9899;</tspan><tspan dx="0" fill="green" > &#9899; </tspan> <tspan dx="5" fill="gold"> &#9899;</tspan><tspan dx="5" fill="crimson"> &#9899;</tspan><tspan dx="10" fill="dodgerblue">  &#9899;</tspan><tspan dx="0" fill="green" > &#9899; </tspan> <tspan dx="5" fill="gold"> &#9899;</tspan><tspan dx="5" fill="crimson"> &#9899;</tspan><tspan dx="10" fill="dodgerblue">  &#9899;</tspan><tspan dx="0" fill="green" > &#9899; </tspan> <tspan dx="5" fill="gold"> &#9899;</tspan><tspan dx="5" fill="crimson"> &#9899;</tspan><tspan dx="10" fill="dodgerblue">  &#9899;</tspan>
  </textPath>
</text>             
</svg>

Another example that mimics the chain using unicode characters
Added animation of chain links movement

<svg width="600" height="400" viewBox="100 100 400 300">
 <path id="pathChain" d="M100,200 C100,100 250,100 250,200 S 400,300 400,200" stroke="grey" fill="none"/>
<text font-size="36"  font-family="Times New Roman" fill="grey" >
  <textPath id="result"    xlink:href="#pathChain">
  <tspan dx="0" > &#7441; </tspan> <tspan dx="-15">   &#45; </tspan><tspan dx="-15"> &#7441;</tspan><tspan dx="-15">   &#45;</tspan><tspan dx="-15">   &#7441; </tspan><tspan dx="-15">   &#45; </tspan><tspan dx="-15"> &#7441;</tspan><tspan dx="-15">   &#45;</tspan><tspan dx="-15">   &#7441; </tspan><tspan dx="-15">   &#45; </tspan><tspan dx="-15"> &#7441;</tspan><tspan dx="-15">   &#45;</tspan><tspan dx="-15">   &#7441; </tspan><tspan dx="-15">   &#45; </tspan><tspan dx="-15"> &#7441;</tspan><tspan dx="-15">   &#45;</tspan><tspan dx="-15">   &#7441; </tspan><tspan dx="-15">   &#45; </tspan><tspan dx="-15"> &#7441;</tspan><tspan dx="-15">   &#45;</tspan><tspan dx="-15">   &#7441; </tspan>
<animate  dur="10s" repeatCount="2" attributeName="startOffset" values="1%;55%;1%"/> 
</textPath>
</text>             
</svg>   

Related