CSS SVG Wave shape


that's the result I'm trying to achieve

enter image description here

and here's what I've done: https://codepen.io/demedos/pen/gjQNOM

HTML structure:

.container
  .header
  .page-1
    #wave
    #dot
    #text

There is some problem though:

  • Items are positioned using absolute positioning, while I want them anchored to the main wavy line
  • Containers are smaller than their content
  • I want the line to be at 50% of the screen, filling the above space with its background color

Answers:


Here's a solution using a little bit of Javascript. I've simplified your example to keep things clear.

I want the line to be at 50% of the screen, filling the above space with its background color

  • We use a vertical flex box arrangement to fill the height of the screen.
  • We set the viewBox to fit the wave curve and let the browser do normal SVG centering.
  • We use a very tall wave path and rely on SVG overflowing to make the wave fill to the top of the cell.
  • We use SVGPathElement.getPointAtLength() to calculate the correct position on the path for each dot.

function positionDot(dotId, fractionAlongWave) {
  // Get a reference to the "wave-curve" path.
  var waveCurve = document.getElementById("wave-curve");
  // Get the length of that path
  var curveLength = waveCurve.getTotalLength();
  // Get the position of a point that is "fractionAlongWave" along that path
  var pointOnCurve = waveCurve.getPointAtLength(curveLength * fractionAlongWave);
  // Update the dot position
  var dot = document.getElementById(dotId);
  dot.setAttribute("cx", pointOnCurve.x);
  dot.setAttribute("cy", pointOnCurve.y);
}


// Position the first dot 25% the way along the curve
positionDot("dot1", 0.25);

// Position the second dot 90% of the way along the curve
positionDot("dot2", 0.90);
* {
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 0;
}

.container {
  display: flex;
  flex-direction: column;
  width: 640px;
  height: 100vh;
  margin: 0 auto;
  border: 1px solid blue;
}

.header {
  background-color: #333835;
}

.page-1 {
  flex: 1;
  border: 1px solid red;
  position: relative;
}

.page-1 svg {
  position: absolute;
  width: 100%;
  height: 100%;
  border: 1px solid red;
}

#wave {
  fill:#333835;
}

#dot1,
#dot2 {
  fill:#e05a5a;
}
<div class='container'>
  <div class='header'>
    header 
  </div>
  <div class='page-1'>
    <!-- Set the viewBox to match the curve part of the wave.
         Then we can rely on the browser to centre the SVG (and thus the curve) in the parent container. -->
    <svg viewBox="0 342 1366 283">
      <defs>
        <!-- A copy of the curve part of the wave, which we will use to calculate
             the correct position of the dot using getPointAtLength(). -->
        <path id="wave-curve" d="M0,342c595,0,813,283,1366,283"/>
      </defs>
      <!-- SVGs are "overflow: visible" by default.
           If we make this path extend vertically a long way, it will fill to the
           top of the SVG no matter how high the page is. -->
      <path id="wave" d="M0,342c595,0,813,283,1366,283 V -10000 H 0 Z"/>

      <circle id="dot1" cx="0" cy="0" r="12.5"/>
      <circle id="dot2" cx="0" cy="0" r="12.5"/>
    </svg>
  </div>
</div>