在画布上制作和制作波浪

Ant*_*ine 4 javascript html5 canvas

我正在寻找一种在帆布设计的形状中创建波浪的方法.经过大量研究后,我发现了一些非常接近我想要的东西:

var c = document.getElementById('c'),
  ctx = c.getContext('2d'),
  cw = c.width = window.innerWidth,
  ch = c.height = window.innerHeight,
  points = [],
  tick = 0,
  opt = {
    count: 5,
    range: {
      x: 20,
      y: 80
    },
    duration: {
      min: 20,
      max: 40
    },
    thickness: 10,
    strokeColor: '#444',
    level: .35,
    curved: true
  },
  rand = function(min, max) {
    return Math.floor((Math.random() * (max - min + 1)) + min);
  },
  ease = function(t, b, c, d) {
    if ((t /= d / 2) < 1) return c / 2 * t * t + b;
    return -c / 2 * ((--t) * (t - 2) - 1) + b;
  };

ctx.lineJoin = 'round';
ctx.lineWidth = opt.thickness;
ctx.strokeStyle = opt.strokeColor;

var Point = function(config) {
  this.anchorX = config.x;
  this.anchorY = config.y;
  this.x = config.x;
  this.y = config.y;
  this.setTarget();
};

Point.prototype.setTarget = function() {
  this.initialX = this.x;
  this.initialY = this.y;
  this.targetX = this.anchorX + rand(0, opt.range.x * 2) - opt.range.x;
  this.targetY = this.anchorY + rand(0, opt.range.y * 2) - opt.range.y;
  this.tick = 0;
  this.duration = rand(opt.duration.min, opt.duration.max);
}

Point.prototype.update = function() {
  var dx = this.targetX - this.x;
  var dy = this.targetY - this.y;
  var dist = Math.sqrt(dx * dx + dy * dy);

  if (Math.abs(dist) <= 0) {
    this.setTarget();
  } else {
    var t = this.tick;
    var b = this.initialY;
    var c = this.targetY - this.initialY;
    var d = this.duration;
    this.y = ease(t, b, c, d);

    b = this.initialX;
    c = this.targetX - this.initialX;
    d = this.duration;
    this.x = ease(t, b, c, d);

    this.tick++;
  }
};

Point.prototype.render = function() {
  ctx.beginPath();
  ctx.arc(this.x, this.y, 3, 0, Math.PI * 2, false);
  ctx.fillStyle = '#000';
  ctx.fill();
};

var updatePoints = function() {
  var i = points.length;
  while (i--) {
    points[i].update();
  }
};

var renderPoints = function() {
  var i = points.length;
  while (i--) {
    points[i].render();
  }
};

var renderShape = function() {
  ctx.beginPath();
  var pointCount = points.length;
  ctx.moveTo(points[0].x, points[0].y);
  var i;
  for (i = 0; i < pointCount - 1; i++) {
    var c = (points[i].x + points[i + 1].x) / 2;
    var d = (points[i].y + points[i + 1].y) / 2;
    ctx.quadraticCurveTo(points[i].x, points[i].y, c, d);
  }
  ctx.lineTo(-opt.range.x - opt.thickness, ch + opt.thickness);
  ctx.lineTo(cw + opt.range.x + opt.thickness, ch + opt.thickness);
  ctx.closePath();
  ctx.fillStyle = 'hsl(' + (tick / 2) + ', 80%, 60%)';
  ctx.fill();
  ctx.stroke();
};

var clear = function() {
  ctx.clearRect(0, 0, cw, ch);
};

var loop = function() {
  window.requestAnimFrame(loop, c);
  tick++;
  clear();
  updatePoints();
  renderShape();
  //renderPoints();
};

var i = opt.count + 2;
var spacing = (cw + (opt.range.x * 2)) / (opt.count - 1);
while (i--) {
  points.push(new Point({
    x: (spacing * (i - 1)) - opt.range.x,
    y: ch - (ch * opt.level)
  }));
}

window.requestAnimFrame = function() {
  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(a) {
    window.setTimeout(a, 1E3 / 60)
  }
}();

loop();
Run Code Online (Sandbox Code Playgroud)
canvas {
  display: block;
}
Run Code Online (Sandbox Code Playgroud)
<canvas id="c"></canvas>
Run Code Online (Sandbox Code Playgroud)

http://codepen.io/jackrugile/pen/BvLHg

问题是波的运动看起来有点不真实.我想保留这种随机运动的概念,而不是通过从左向右移动来重复自己的形状,但如果我找到一种方法来创建一个"逼真的"水运​​动(良好的流体动力学,碰撞,那将是很好的)这个波浪与其容器的边缘(自定义形状)).

我想我问了很多但是...一小部分研究可以帮助:)

小智 19

干扰

您可以使用干涉制作更逼真的波形.

  • 有一个大波浪(膨胀)慢慢地运动
  • 再运行一两个较小的正弦波(振荡器)
  • 都具有随机振幅
  • 使用平均值水平混合波浪并计算各个点
  • 使用基数样条曲线绘制结果(或者如果分辨率很高,则可以在点之间绘制简单的线条).

使用各种参数,以便您可以实时调整以找到一个好的组合.

您还可以添加振荡器来表示z轴,以便在您想要将波层叠为伪3D波时更加逼真.

我不能给你波浪碰撞,流体动力学 - 这对于SO来说有点过于宽泛但是我可以给你一个流动的波形例子(因为你可以使用每个段的点来进行碰撞检测).

例如,可以创建一个振荡器对象,您可以设置各种设置,如振幅,旋转速度(相位)等.

然后有一个混音器功能,混合你使用的这些振荡器的结果.

这里现场演示(这里是全屏版)

演示快照

此演示中的振荡器对象如下所示:

function osc() {

    /// various settings        
    this.variation = 0.4;  /// how much variation between random and max
    this.max       = 100;  /// max amplitude (radius)
    this.speed     = 0.02; /// rotation speed (for radians)

    var me  = this,        /// keep reference to 'this' (getMax)
        a   = 0,           /// current angle
        max = getMax();    /// create a temp. current max

    /// this will be called by mixer
    this.getAmp = function() {

        a += this.speed;   /// add to rotation angle

        if (a >= 2.0) {    /// at break, reset (see note)
            a = 0;
            max = getMax();
        }

        /// calculate y position
        return max * Math.sin(a * Math.PI) + this.horizon;
    }

    function getMax() {
        return Math.random() * me.max * me.variation +
               me.max * (1 - me.variation);
    }

    return this;
}
Run Code Online (Sandbox Code Playgroud)

这为我们完成了所有设置和计算,我们需要做的就是调用getAmp()每个帧获取一个新值.

而不是手动操作,我们可以使用"混音器".这个混音器允许我们添加我们想要的振荡器:

function mixer() {

    var d = arguments.length,  /// number of arguments
        i = d,                 /// initialize counter
        sum = 0;               /// sum of y-points

    if (d < 1) return horizon; /// if none, return

    while(i--) sum += arguments[i].getAmp(); /// call getAmp and sum

    return sum / d + horizon;  /// get average and add horizon
}
Run Code Online (Sandbox Code Playgroud)

将它放在一个带有点记录器的循环中,点记录器将点向一个方向移动将产生流动的波浪.

上面的演示使用三个振荡器.(这方面的一个提示是保持旋转速度低于大膨胀,否则你会得到小的颠簸.)

注意:我创建一个新的随机最大值的方式并不是我使用断点的最佳方法(但对于演示目的来说很简单).您可以用更好的东西替换它.


Gam*_*ist 14

由于您正在寻找逼真的效果,最好的想法可能是模拟水.实际上并不是那么难:水可以很好地接近连接在一起的弹簧网络.

结果非常好,你可以在这里找到它:http: //jsfiddle.net/gamealchemist/Z7fs5/

在此输入图像描述

因此,我认为它是2D效果,并为水面的每个点构建了一个数组,其加速度,速度和位置.我将它们存储在一个阵列中,分别为3*i + 0,3*i + 1和3*i + 2.
然后在每次更新时,我只是简单地应用具有弹性的牛顿定律,并通过一些摩擦来使运动停止.
我影响每个点,使其进入稳定状态+受到左右邻居的影响.
(如果你想要更流畅的动画,也可以使用i-2和i + 2点,以及更低的kFactor.)

var left = 0, y = -1;
var right = water[2];
for (pt = 0 ; pt < pointCount; pt++, i += 3) {
    y = right;
    right = (pt < pointCount - 1) ? water[i + 5] : 0;
    if (right === undefined) alert('nooo');
    // acceleration
    water[i] = (-0.3 * y + (left - y) + (right - y)) * kFactor - water[i + 1] * friction;
    // speed
    water[i + 1] += water[i] * dt;
    // height
    water[i + 2] += water[i + 1] * dt;
    left = y;
}
Run Code Online (Sandbox Code Playgroud)

平局非常简单:只需迭代点并绘制.但是在绘制时很难获得平滑的效果,因为很难让bezier和quadraticCurve使它们的衍生物匹配.我建议了一些绘图方法,你可以根据需要切换.

然后我加了雨,这样水就可以随意移动了.这里只是非常简单的轨迹,然后我计算是否与水发生碰撞,如果是,我会增加一些速度并移动点.