草庐IT

javascript - 在 Canvas 中制作波浪并制作动画

coder 2023-08-12 原文

我正在寻找一种在 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();
canvas {
  display: block;
}
<canvas id="c"></canvas>

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

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

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

最佳答案

干扰

您可以使用干涉来制作更逼真的波浪。

  • 有一个大浪​​(涌浪)以大 Action 缓慢运行
  • 再运行一个或两个较小的正弦波(振荡器)
  • 全部具有随机振幅
  • 使用平均值水平混合波并计算各个点
  • 使用基数样条绘制结果(或者如果分辨率很高,您可以只在点之间绘制简单的线)。

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

您还可以添加振荡器来表示 z 轴,以使其更加逼真,以防您想要将波分层以制作伪 3D 波。

例子

我不能给你波浪碰撞、流体动力学——这对 SO 来说有点太宽泛了,但我可以给你一个流体波的例子(因为你有每个部分的点,你可以用它来进行碰撞检测) .

示例是创建一个振荡器对象,您可以在该对象上设置各种设置,例如振幅、旋转速度(相位)等。

然后有一个混频器功能,可以混合您使用的这些振荡器的结果。

Live demo here ( full-screen version here )

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

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
}

将其放入带有点记录器的循环中,点记录器会在一个方向上移动点,从而创建一个看起来很流畅的波浪。

上面的演示使用了三个振荡器。 (在这方面的一个技巧是保持旋转速度低于大浪,否则你会在上面遇到小颠簸。)

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

关于javascript - 在 Canvas 中制作波浪并制作动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19787775/

有关javascript - 在 Canvas 中制作波浪并制作动画的更多相关文章

  1. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

  2. 动漫制作技巧如何制作动漫视频 - 2

    动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、

  3. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  4. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

    我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

  5. LVGL V8动画 - 2

    动画/*INITIALIZEANANIMATION 初始化一个动画*-----------------------*/lv_anim_ta;lv_anim_init(&a);/*MANDATORYSETTINGS 必选设置*------------------*//*Setthe"animator"function 设置“动画”功能*/lv_anim_set_exec_cb(&a,(lv_anim_exec_xcb_t)lv_obj_set_x);/*Setthe"animator"function*/lv_anim_set_var(&a,obj);/*Lengthoftheanim

  6. ruby - 如何在ruby中制作动态多维数组? - 2

    我有一个关于多维数组的初学者ruby​​问题。我想按年份和月份对条目进行排序。所以我想创建一个包含年->月->月条目的多维数组所以数组应该是这样的:2009->08->Entry1->Entry209->Entry32007->10->Entry5现在我有:@years=[]@entries.eachdo|entry|timeobj=Time.parse(entry.created_at.to_s)year=timeobj.strftime("%Y").to_imonth=timeobj.strftime("%m").to_itmparr=[]tmparrentry}@years.pu

  7. javascript - jQuery 的 jquery-1.10.2.min.map 正在触发 404(未找到) - 2

    我看到有关未找到文件min.map的错误消息:GETjQuery'sjquery-1.10.2.min.mapistriggeringa404(NotFound)截图这是从哪里来的? 最佳答案 如果ChromeDevTools报告.map文件的404(可能是jquery-1.10.2.min.map、jquery.min.map或jquery-2.0.3.min.map,但任何事情都可能发生)首先要知道的是,这仅在使用DevTools时才会请求。您的用户不会遇到此404。现在您可以修复此问题或禁用sourcemap功能。修复:获取文

  8. ruby - 如何在不使用 HERE-DOCUMENT 语法的情况下在 Ruby 中制作多行字符串文字? - 2

    问题总结我想尝试使用Ruby来完成我在Python中所做的事情。在Python中它有r"""syntaxtosupportrawstrings,这很好,因为它允许将原始字符串与代码内联,并以更自然的方式连接它们,而无需特殊缩进。在Ruby中,当使用原始字符串时,必须使用其次是EOT在单独的行中,这会破坏代码布局。你可能会问,为什么不使用Ruby的%q{}?嗯,因为%q{}与Python的r"""相比有局限性因为它不会转义多个\\\并且只处理单个\.我正在动态生成Latex代码并写入一个文件,该文件稍后用pdflatex编译。Latex代码包含类似\\\的内容在许多地方。如果我使用Rub

  9. ruby-on-rails - 我将 Rails3 与 tinymce 一起使用。如何呈现用户关闭浏览器javascript然后输入xss? - 2

    我有一个用Rails3编写的站点。我的帖子模型有一个名为“内容”的文本列。在帖子面板中,html表单使用tinymce将“content”列设置为textarea字段。在首页,因为使用了tinymce,post.html.erb的代码需要用这样的原始方法来实现。.好的,现在如果我关闭浏览器javascript,这个文本区域可以在没有tinymce的情况下输入,也许用户会输入任何xss,比如alert('xss');.我的前台会显示那个警告框。我尝试sanitize(@post.content)在posts_controller中,但sanitize方法将相互过滤tinymce样式。例如

  10. ruby - 如何制作 Ruby 1.8 小写非拉丁字符? - 2

    我正在使用Ruby1.8。似乎downcase不会改变非拉丁字符。例如:"Δ".downcase返回“Δ”我知道在Ruby1.9.1及更高版本中,我可以使用UnicodeUtils(fromhere)。我试过了,它工作正常。返回上一个示例的"δ"。是否有适用于1.8Ruby的等效(或任何)解决方案? 最佳答案 nash@nash:~$ruby-vruby1.8.7(2011-02-18patchlevel334)[i686-linux]gem安装unicode(https://rubygems.org/gems/unicode)re

随机推荐