【问题标题】:How can i make my HTML canvas dots move more naturally?如何让我的 HTML 画布点更自然地移动?
【发布时间】:2015-08-16 19:14:23
【问题描述】:

链接到我的代码笔here

我想就如何使画布点以更流畅、类似液体的方式移动提出意见。

我已经尝试限制一定数量的渲染(draw())的方向,这已经改进了一点!但它仍然缺乏流动性,给人以僵化和“硬编码”的印象。

这个switch语句是设置方向的方式,要么传入一个随机整数,要么传入前一个方向。

 switch (direction) {
                    case 1:
                      star.x--;
                      break;
                    case 2:
                      star.x++;
                      break;
                    case 3:
                      star.y--;
                      break;
                    case 4:
                      star.y++;
                      break;
                    case 5:
                      star.y--;
                      star.x--;
                      break;
                    case 6:
                      star.y--;
                      star.x++;
                      break;
                    case 7:
                      star.y++;
                      star.x++;
                      break;
                    case 8:
                      star.y++;
                      star.x--;
                      break;
}

谢谢!

【问题讨论】:

  • 当粒子改变方向时,我会使用缓动函数来减速/加速粒子。
  • 我认为让我看起来“不自然”的主要原因是它们基本上在同一时间进行了大的方向变化。当然,我把它们想象成虫子,所以..

标签: javascript html canvas


【解决方案1】:

您可以这样做:

  • 根据随机线点生成基本样条
  • 沿着那条线走,在当前位置画一颗星星

为什么是基数样条?基数样条会在一组点之间生成一条平滑线。如果也有张力值。通过夸大张力值(即超出正常 [0,1] 范围),它会产生卷曲线。

// draw example lines
var ctx = c.getContext("2d"), p = [0,100, 25,40, 50,70, 75,50, 100,80, 125,32, 150,100, 175,60];
ctx.font = "bold 16px sans-serif";

render(0); render(0.5); render(-2);
ctx.setTransform(1,0,0,1,0, 110);

render(-2, 3, "Segments: 3"); render(-2, 9, "Segments: 9"); render(-2, 25, "Segments: 25");

function render(t, seg, txt) {
  ctx.beginPath();
  ctx.moveTo(0, 100);
  ctx.curve(p, t, seg || 20);
  ctx.stroke();
  ctx.fillText(txt ? txt : (!t ? "Plain poly-line" : "Cardinal, tension: " + t), 0, 20);
  ctx.translate(200,0);
}

我们可以利用这个属性并沿着这条线绘制一个点来产生一种液体状的运动。可以通过降低/增加样条中每条线之间的分段分辨率来细化运动,这将影响平滑度和速度。

其他优点是我们不必在动画本身中计算任何东西(虽然在设置点(缓存)时会有一个初始“峰值”),我们只需更新数组指针并渲染。并且分布将相当均匀,因为这些点是沿着稍微均匀分布(不可见)的路径的力。

当然,如何实现它可能会有所不同 - 这是一种方法的一个示例:

示例实现

定义一个星形对象(它确实应该是原型,但为了演示):

function Star(ctx, xseg) {

    var points = [],              // holds points for cardinal points
        cPos = 0, oPos = -1,      // positions in line
        len,
        w = ctx.canvas.width,
        x = -10, y = -10;

    // this iterates and loop the point list            
    this.animate = function() {
        cPos++;

        if (cPos > len - 2) {
            cPos = 0; oPos = -1;
        }

        var pos = cPos * 2;
        x = points[pos];
        y = points[pos + 1];

        drawStar();
    }

    // render some star
    function drawStar() {
        ctx.rect(x, y, 2, 2);
    }

    // This generate a set of random points, then converts those into
    // points for a cardinal spline (linked as script).
    function generatePath() {
        var w = ctx.canvas.width,
            h = ctx.canvas.height,
            numOfSeg = 20,
            dh = h / numOfSeg,
            i= 0, l, x, y;

        for(; i<= numOfSeg; i++) {          
            x = xseg + w / 8 * Math.random();
            y = h - (i * dh + ((dh / 2) * Math.random() - (dh / 4)));
            points.push(x, y);
        }

        points = curve(points, -2, 200 * Math.random() + 100);
        l = points.length;

        // adjust for out of edges
        for(i = 0; i < l; i += 2) if (points[i] > w) points[i] -= w;
        len = points.length / 2;
        cPos = parseInt(len * Math.random());
    }   
    generatePath();
}

完整示例

function Star(ctx, xseg) {

  var points = [],              // holds points for cardinal points
      cPos = 0, oPos = -1,      // positions in line
      len,
      w = ctx.canvas.width,
      x = -10, y = -10;

  this.animate = function() {
    cPos++;
    if (cPos > len - 2) {
      cPos = 0;  oPos = -1;
    }

    var pos = cPos * 2;
    x = points[pos];
    y = points[pos + 1];

    drawStar();
  };

  function drawStar() {
    ctx.moveTo(x + 2, y);
    ctx.arc(x, y, 2, 0, Math.PI*2);
  }

  function generatePath() {
    var w = ctx.canvas.width,
        h = ctx.canvas.height,
        numOfSeg = 20,
        dh = h / numOfSeg,
        i= 0, l, x, y;

    for(; i <= numOfSeg; i++) {         
      x = xseg + w / 8 * Math.random();
      y = h - (i * dh + ((dh / 2) * Math.random() - (dh / 4)));
      points.push(x, y);
    }

    points = getCurvePoints(points, -2, (400 * Math.random() + 200)|0);
    l = points.length;

    for(i = 0; i < l; i += 2) if (points[i] > w) points[i] -= w;
    len = points.length / 2;
    cPos = (len * Math.random())|0;

  }
  generatePath();
}

// Main code
var canvas = document.querySelector("canvas"),
    ctx = canvas.getContext("2d"),
    stars = [],
    numOfStars = 100,
    segs = canvas.width / numOfStars, 
    i = 0,
    throttle = 0,
    delay = 2;

// create stars
for(; i < numOfStars; i++) stars.push(new Star(ctx, i * segs - segs));

ctx.fillStyle = "#fff";
ctx.shadowColor ="#fff";
ctx.shadowBlur = 7;

// ANIMATE
(function animate() {      
  if (!throttle) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.beginPath();
    for(var i = 0; i < stars.length; i++) stars[i].animate();
    ctx.fill(); 
  }

  throttle++;
  if (throttle === delay) throttle = 0;

  requestAnimationFrame(animate);
})();

请参阅this answer 中的基数样条实现代码。

另一种方法是使用粒子并使用例如正弦函数来改变速度。为此,您可能需要一个速度 grid 来根据位置影响粒子。网格可以有随机的方向和速度。

【讨论】:

  • 为什么这些圆点看起来会“颤动”? (像小蝴蝶)
  • @WoodrowBarlow 它实际上是基于早期代码的遗留物。当时我有那个去模拟金片。我正在考虑删除该功能以简化代码。
  • 微光效果移除,代码在history.
  • 感谢您的详细回答,我稍后会检查并告诉您进展如何!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-30
  • 1970-01-01
  • 2020-05-30
相关资源
最近更新 更多