如何让我的HTML画布点更自然地移动?

时间:2022-02-19 20:48:07

Link to my code pen here

链接到我的代码笔在这里

I would like opinions on how i could make the canvas dots move in a more fluid, liquid like way.

我希望看到如何使画布点以更流畅,更液体的方式移动。

I've tried limiting the direction for a certain number of renders (draw()), which has improved it a little! But it still lacks fluidity, coming across as rigid and 'hard-coded'.

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

This switch statement is the way direction is set, either passed a random integer or the previous direction.

此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;
}

Thanks!

1 个解决方案

#1


11  

You can do this by:

你可以这样做:

  • Generating a cardinal spline based on random line points
  • 基于随机线点生成基数样条

  • Walk along that line and plot a star on current position
  • 沿着那条线走,并在当前位置上绘制一颗星

Why cardinal spline? A cardinal spline will generate a smooth line between a set of points. If also has a tension value. By exaggerating the tension value (i.e. outside the normal [0,1] range) it will produce curly lines instead.

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

如何让我的HTML画布点更自然地移动?

// 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);
}

We can take advantage of this property and plot a point along such a line to produce a liquid-ish movement. The movement can be refined by de/increase the segment resolution between each line in the spline which will affect the smoothness as well as the speed.

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

Other advantages is that we don't have to calculate anything in the animation itself (there will be an initial "peak" when setting up the points (cache) though), we just update the array pointer and render. And the distribution will be fairly even as the points are forces along somewhat evenly distributed (invisible) paths.

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

How to implement it can vary of course - here is one example of an approach:

如何实现它当然可以改变 - 这是一个方法的一个例子:

Example implementation

Define a star object (it should really be prototyped but for the sake of demo):

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

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

Full example

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

See code for cardinal spline implementation in this answer.

请参阅此答案中的基数样条实现的代码。

Another approach is to use particles and variate the velocity using for example a sinus function. For this to work optimally you may need a velocity grid instead to affect a particle based on location. The grid can have random directions and velocities.

另一种方法是使用粒子并使用例如正弦函数来变化速度。为了使其最佳地工作,您可能需要一个速度网格来代替基于位置的粒子。网格可以具有随机方向和速度。

#1


11  

You can do this by:

你可以这样做:

  • Generating a cardinal spline based on random line points
  • 基于随机线点生成基数样条

  • Walk along that line and plot a star on current position
  • 沿着那条线走,并在当前位置上绘制一颗星

Why cardinal spline? A cardinal spline will generate a smooth line between a set of points. If also has a tension value. By exaggerating the tension value (i.e. outside the normal [0,1] range) it will produce curly lines instead.

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

如何让我的HTML画布点更自然地移动?

// 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);
}

We can take advantage of this property and plot a point along such a line to produce a liquid-ish movement. The movement can be refined by de/increase the segment resolution between each line in the spline which will affect the smoothness as well as the speed.

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

Other advantages is that we don't have to calculate anything in the animation itself (there will be an initial "peak" when setting up the points (cache) though), we just update the array pointer and render. And the distribution will be fairly even as the points are forces along somewhat evenly distributed (invisible) paths.

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

How to implement it can vary of course - here is one example of an approach:

如何实现它当然可以改变 - 这是一个方法的一个例子:

Example implementation

Define a star object (it should really be prototyped but for the sake of demo):

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

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

Full example

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

See code for cardinal spline implementation in this answer.

请参阅此答案中的基数样条实现的代码。

Another approach is to use particles and variate the velocity using for example a sinus function. For this to work optimally you may need a velocity grid instead to affect a particle based on location. The grid can have random directions and velocities.

另一种方法是使用粒子并使用例如正弦函数来变化速度。为了使其最佳地工作,您可能需要一个速度网格来代替基于位置的粒子。网格可以具有随机方向和速度。