oldboy
oldboy

Reputation: 5954

Dynamic Wavy Path/Border

There is something I need to build, but my math ability is not up to par. What I am looking to build is something like this demo, but I need it to be a hybrid of a circle and polygon instead of a line, so to speak. The black line should be dynamic and randomly generated that basically acts as a border on the page.

Currently, I am dissecting this answer with the aim of hopefully being able to transpose it into this, but I am having massive doubts that I will be able to figure this out.

enter image description here

Any idea how to do this or can anybody explain the mathematics?

Below are my notes about the code from the answer I linked above.

var
  cw = cvs.width = window.innerWidth,
  ch = cvs.height = window.innerHeight,
  cx = cw / 2,
  cy = ch / 2,
  xs = Array(),
  ys = Array(),
  npts = 20,
  amplitude = 87, // can be val from 1 to 100
  frequency = -2, // can be val from -10 to 1 in steps of 0.1

ctx.lineWidth = 4

// creates array of coordinates that
// divides page into regular portions
// creates array of weights
for (var i = 0; i < npts; i++) {
  xs[i] = (cw/npts)*i
  ys[i] = 2.0*(Math.random()-0.5)*amplitude
}

function Draw() {
  ctx.clearRect(0, 0, cw, ch);
  ctx.beginPath();

  for (let x = 0; x < cw; x++) {
    y = 0.0
    wsum = 0.0

    for (let i = -5; i <= 5; i++) {
      xx = x; // 0 / 1 / 2 / to value of screen width

      // creates sequential sets from [-5 to 5] to [15 to 25]
      ii = Math.round(x/xs[1]) + i

      // `xx` is a sliding range with the total value equal to client width
      // keeps `ii` within range of 0 to 20
      if (ii < 0) {
        xx += cw
        ii += npts
      }
      if (ii >= npts){
        xx -= cw
        ii -= npts
      }

      // selects eleven sequential array items
      // which are portions of the screen width and height
      // to create staggered inclines in increments of those portions
      w = Math.abs(xs[ii] - xx)

      // creates irregular arcs
      // based on the inclining values
      w = Math.pow(w, frequency)

      // also creates irregular arcs therefrom
      y += w*ys[ii];

      // creates sets of inclining values
      wsum += w;

    }

    // provides a relative position or weight
    // for each y-coordinate in the total path
    y /= wsum;

    //y = Math.sin(x * frequency) * amplitude;
    ctx.lineTo(x, y+cy); 
  }
  ctx.stroke();
}
Draw();

Upvotes: 3

Views: 545

Answers (1)

enxaneta
enxaneta

Reputation: 33024

This is my answer. Please read the comments in the code. I hope this is what you need.

// initiate the canvas
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let cw = (canvas.width = 600),
  cx = cw / 2;
let ch = (canvas.height = 400),
  cy = ch / 2;
ctx.fillStyle = "white"

// define the corners of an rectangle
let corners = [[100, 100], [500, 100], [500, 300], [100, 300]];

let amplitud = 20;// oscilation amplitude
let speed = 0.01;// the speed of the oscilation
let points = []; // an array of points to draw the curve

class Point {
  constructor(x, y, hv) {
    // the point is oscilating around this point (cx,cy)
    this.cx = x;
    this.cy = y;
    // the current angle of oscilation
    this.a = Math.random() * 2 * Math.PI;
    this.hv = hv;// a variable to know if the oscilation is horizontal or vertical

    this.update();
  }

  // a function to update the value of the angle
  update() {
    this.a += speed;

    if (this.hv == 0) {
      this.x = this.cx;
      this.y = this.cy + amplitud * Math.cos(this.a);
    } else {
      this.x = this.cx + amplitud * Math.cos(this.a);
      this.y = this.cy;
    }
  }
}


// a function to divide a line that goes from a to b in n segments
// I'm using the resulting points to create a new point object and push this new point into the points array
function divide(n, a, b) {
  for (var i = 0; i <= n; i++) {
    let p = {
      x: (b[0] - a[0]) * i / n + a[0],
      y: (b[1] - a[1]) * i / n + a[1],
      hv: b[1] - a[1]
    };
    points.push(new Point(p.x, p.y, p.hv));
  }
}

divide(10, corners[0], corners[1]);points.pop();
divide(5, corners[1], corners[2]);points.pop();
divide(10, corners[2], corners[3]);points.pop();
divide(5, corners[3], corners[0]);points.pop();


// this is a function that takes an array of points and draw a curved line through those points
function drawCurves() {
  //find the first midpoint and move to it
  let p = {};
  p.x = (points[points.length - 1].x + points[0].x) / 2;
  p.y = (points[points.length - 1].y + points[0].y) / 2;
  ctx.beginPath();
  ctx.moveTo(p.x, p.y);
  //curve through the rest, stopping at each midpoint
  for (var i = 0; i < points.length - 1; i++) {
    let mp = {};
    mp.x = (points[i].x + points[i + 1].x) / 2;
    mp.y = (points[i].y + points[i + 1].y) / 2;
    ctx.quadraticCurveTo(points[i].x, points[i].y, mp.x, mp.y);
  }
  //curve through the last point, back to the first midpoint
  ctx.quadraticCurveTo(
    points[points.length - 1].x,
    points[points.length - 1].y,
    p.x,
    p.y
  );
  ctx.stroke();
  ctx.fill();
}

function Draw() {
  window.requestAnimationFrame(Draw);
  ctx.clearRect(0, 0, cw, ch);
  points.map(p => {
    p.update();
  });
  drawCurves();
}

Draw();
canvas{border:1px solid; background:#6ab150}
<canvas></canvas>

Upvotes: 3

Related Questions