我正在寻找一种在画布设计的形状中创建波浪的方法。经过大量研究后,我发现了一些非常接近我想要的东西:
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();
canvas {
display: block;
}
<canvas id="c"></canvas>
http://codepen.io/jackrugile/pen/BvLHg
问题在于波的运动看起来有点不真实。我想保留这种随机运动的概念,而不是通过从左向右移动来重复自己的形状,但如果我找到一种方法来创造一个“逼真的”水运动(良好的流体动力学,碰撞,那将是很好的)。这个波浪与其容器的边缘(自定义形状))。
我想我问了很多但是...一小部分研究可以帮助:)
答案 0 :(得分: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;
}
这为我们做了所有设置和计算,我们需要做的就是调用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
}
将它放在带有点记录器的循环中,点记录器将点向一个方向移动将产生流畅的波浪。
上面的演示使用三个振荡器。 (这方面的一个提示是保持旋转速度低于大膨胀,否则你会在上面产生小的颠簸。)
注意:我创建一个新的随机最大值的方式并不是我使用断点的最佳方法(但对于演示目的来说很简单)。你可以用更好的东西替换它。
答案 1 :(得分:14)
由于您正在寻找逼真的效果,最好的想法可能是模拟水。实际上并不是那么难:水可以很好地接近连接在一起的弹簧网络。
结果非常好,你可以在这里找到它: http://jsfiddle.net/gamealchemist/Z7fs5/
所以我认为它是二维效果,并为水面的每个点建立一个数组,保持其加速度,速度和位置。我将它们存储在一个阵列中,分别为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;
}
绘制非常简单:只需迭代点并绘制。但是在绘制时很难获得平滑的效果,因为很难让bezier和quadraticCurve使它们的衍生物匹配。我建议了一些绘图方法,你可以根据需要进行切换。
然后我加了雨,这样水就可以随意移动了。这里只是非常简单的轨迹,然后我计算是否与水发生碰撞,如果是这样,我会增加一些速度并改变点。 p>
答案 2 :(得分:3)
我想创造一个具有良好流体动力学的“逼真”水运动,这种波与自定义边缘的碰撞 容器.. 的
哦,小男孩......那真是一口气。 您可以在此处提出您的问题:gamedev.stackexchange
无论如何,您是否尝试过编程任何类型的wave,或者您只是在寻找WaveCreator.js?
Go和Google了解如何创建2D水的非语言特定指南。
如果你是初学者,那么从简单的事情开始,就可以了解事情背后的想法。 怎么样为“我的世界式”水创造一堆盒子?
这里水的每一条“线”都可以表示为阵列中的位置。然后循环遍历它并根据之前的Array Index设置水的“高度”。 (您可以通过使块非常薄(为您的程序更多工作!)或平滑边缘并根据其他方块给它们一个角度来平滑水。
我认为这可能是一个简洁的解决方案。无论如何。希望能给你一些想法。