动态波浪路径/边框

3
有一个项目需要我建造,但我的数学能力不够强。我想要建造的是类似于这个演示,但我需要它是圆形和多边形的混合,而不是像线一样。黑色线条应该是动态和随机生成的,基本上作为页面的边框。

目前,我正在解剖这个答案,希望能够将其转化为这个项目,但我非常怀疑我能否解决这个问题。

enter image description here

有什么想法可以做到这一点或者有人能解释一下其中的数学原理吗?

下面是我从上面链接中的代码中得出的笔记。

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();

形状上有任何限制吗?是否给定了某条路径,波浪线大致应该遵循它? - Lutz Lehmann
@LutzL 它应该是一个矩形的粗略近似(或者说是屏幕的 clientWidth)。它几乎像是页面周围的边框。它应该代表视频游戏中的战争迷雾。但我想让波浪线变得动态,并且也很好能够调整绿色部分的厚度,或者换句话说缩小可见区域的“矩形”。 - oldboy
@LutzL 有点像边框,但内侧是波浪形而不是直线。就像这样。,但可能更加波动。我计划使波浪动态化,并且我计划在外缘使其不透明,然后向中心靠近时逐渐变为透明。我得去睡觉了,但明天会回来查看<3 - oldboy
1个回答

3

这是我的答案。请阅读代码中的注释。我希望这是你需要的。

// 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>


非常感谢你!!现在是我浏览代码并理解你是如何做到这一点的时间了!<33 - oldboy
我还没有自己查看所有的代码,但有没有办法应用渐变,使得绿色部分动态内边与白色部分相遇时始终是透明的,而最外侧完全是绿色的,换句话说,例如,内部绿色区域的一半始终是半透明的?这样说清楚了吗? - oldboy
在演示中,有一个带有绿色背景和白色填充的波浪形状的画布。您可以将形状的填充样式更改为渐变,但恐怕这不是您想要的。请查看此笔:Aquarelle Animation 或许这正是您需要的。 - enxaneta
1
@enxaneta 很棒的回答!总是对你的回答感兴趣。 - Alexandr_TT
是的,那差不多就是我所说的。不是在战争迷雾边界有实体边缘,而是它渗入中间。我知道我可以通过叠加图层或使用像那个演示中的蒙版,并动画化蒙版的位置来制作类似的东西。这可能是最简单的方法。 - oldboy
嘿,你能指向任何生成随机波浪(在加载时)像黏糊一样沿着屏幕渗透的信息(例如文章、演示等)吗?或者至少有帮助我理解创建波浪的数学的文章吗? - oldboy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接