马黑黑 发表于 2022-8-24 21:06

canvas画布上模拟缓动

本帖最后由 马黑黑 于 2022-8-24 21:08 编辑

缓动就是加速度的反例。由于阻力的作用,一个物体在获得第一推动力之后,如果不再有动力,则它的行进过程属于一种缓动过程,其运动亦称减速度运动,直至停下。

我们可以在画布上模拟这一过程。为了简便,我们只设置一个方向的运动,让小球从左边运动到右边,运动过程中不断减速,最后停下。

我们先准备一下一个画布、一个按钮,画布的作用不必多说,按钮是用来给小球下运动指令的。CSS和HTML代码如下:

css:

<style>
      #canv { margin: auto; display: block; border: 1px solid; }
      #btn { margin: 10px auto; display: block; }
</style>

html:

<canvas id="canv"></canvas>
<button id="btn">Go</button>


下面进入JS部分。先大体说一下思路:小球前进当中,距离目的地越来越近,我们就用剩下的路程乘以一个缓动系数,所得乘积累加到小球的X坐标中。缓动系数一般极小,它是一个固定不变的浮点数值,比如可以是 0.05 或其他的数值,由于小球当前剩余的路程越来越短,路程乘以缓动系数所得的累加给小球X坐标的值也越来越小,从而速度变得越来越缓慢。下面进入具体实现环节:

首先,声明相关变量:

let ctx = canv.getContext("2d"), //画笔
      w = canv.width = 740, //定义画布宽度并获取其值
      h = canv.height = 200, //定义画布高度并获取其值
      ball = { x: 20, y: 100, r: 10, easing: 0.01, color: 'red' }, //创建一个 ball 实体对象
      targetX = w - ball.r; // 设定小球的目的地


ball 对象我们写在了一行(为了简洁),可能不便理解,下面将其拆解瞧瞧:

ball = {
      x: 10, //X坐标
      y: 100, //Y坐标
      r: 10, //半径
      easing: 0.01, //缓动系数
      color: 'red', //小球颜色
}

下来,我们给 ball 实体对象添加一个绘制圆形的函数,非常简单:

ball.draw = function () {
      ctx.beginPath(); //画笔开启路径
      ctx.arc(this.x, this.y, this.r, 0, Math.PI*2); //按自身尺寸绘制圆
      ctx.fillStyle = this.color; //用自身颜色赋值给画笔
      ctx.fill(); //填色
}


ball 实体对象的东东处理到这,接下来,定义一个函数,用于改变小球的X坐标,实现减速度运动;同时,函数中还处理一下按钮的可用和禁用状态:

function update() {
      ctx.clearRect(0, 0, w, h); //清空画布
      ball.x += (targetX - ball.x) * ball.easing; //X坐标缓动增速
      ball.draw(); //每次更新都重新绘制小球
      if(ball.x + ball.r < targetX) { //如果小球X坐标+半径小于目标位置
                requestAnimationFrame(update); //则调用定时器运行本函数
                btn.disabled = true; //此时按钮不可用
      } else { //否则,如果小球X坐标+半径如果大于等于目的地
                btn.disabled = false; //按钮禁用状态解除
      }
}


关于缓动增速,前面已做铺垫,这里不再重复解释。其他的语句,注释自身基本都讲清楚了。

按钮点击事件也得有:

btn.onclick = function() {
      ball.x = 20; //恢复小球X坐标
      ball.y = 100;//恢复小球Y坐标
      update(); //运行小球状态更新函数
}


最后,我们还得运行一次 ball.draw 函数,下面函数可以放在JS所有代码的最后,也可以放在 ball 对象及方法已被完全声明过的代码区域的后面:

ball.draw();

效果演示和完整代码将附后。

马黑黑 发表于 2022-8-24 21:07

<style>
        #canv { margin: auto; display: block; border: 1px solid; }
        #btn { margin: 10px auto; display: block; }
</style>

<canvas id="canv"></canvas>
<button id="btn">Go</button>

<script>

let ctx = canv.getContext("2d"),
        w = canv.width = 740,
        h = canv.height = 200,
        ball = { x: 20, y: 100, r: 10, easing: 0.01, color: 'red' },
        targetX = w - ball.r;

ball.draw = function () {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, Math.PI*2);
        ctx.fillStyle = this.color;
        ctx.fill();
}

ball.draw();

btn.onclick = function() {
        ball.x = 10;
        ball.y = 100;
        update();
}

function update() {
        ctx.clearRect(0, 0, w, h);
        ball.x += (targetX - ball.x) * ball.easing;
        ball.draw();
        if(ball.x + ball.r < targetX) {
                requestAnimationFrame(update);
                btn.disabled = true;
        } else {
                btn.disabled = false;
        }
}

</script>

马黑黑 发表于 2022-8-24 21:09

附:二楼完整代码
<style>
        #canv { margin: auto; display: block; border: 1px solid; }
        #btn { margin: 10px auto; display: block; }
</style>

<canvas id="canv"></canvas>
<button id="btn">Go</button>

<script>

let ctx = canv.getContext("2d"),
        w = canv.width = 740,
        h = canv.height = 200,
        ball = { x: 20, y: 100, r: 10, easing: 0.01, color: 'red' },
        targetX = w - ball.r;

ball.draw = function () {
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, Math.PI*2);
        ctx.fillStyle = this.color;
        ctx.fill();
}

ball.draw();

btn.onclick = function() {
        ball.x = 10;
        ball.y = 100;
        update();
}

function update() {
        ctx.clearRect(0, 0, w, h);
        ball.x += (targetX - ball.x) * ball.easing;
        ball.draw();
        if(ball.x + ball.r < targetX) {
                requestAnimationFrame(update);
                btn.disabled = true;
        } else {
                btn.disabled = false;
        }
}

</script>

红影 发表于 2022-8-24 21:17

原来创建小球实体的语句里有那么多内容。点了一下,缓动效果十分明显。{:4_187:}

马黑黑 发表于 2022-8-24 22:03

红影 发表于 2022-8-24 21:17
原来创建小球实体的语句里有那么多内容。点了一下,缓动效果十分明显。

直接这样创建实体对象,它的属性和方法是可以不按顺序的,方便。不过这样的对象是个案的,不方便扩展。

红影 发表于 2022-8-25 09:19

马黑黑 发表于 2022-8-24 22:03
直接这样创建实体对象,它的属性和方法是可以不按顺序的,方便。不过这样的对象是个案的,不方便扩展。

能适合一个个个案也很不错呀{:4_204:}

马黑黑 发表于 2022-8-25 12:25

红影 发表于 2022-8-25 09:19
能适合一个个个案也很不错呀

这不是最科学的方法。关于创建对象,我喜欢用原形法。我一直没介绍的还有工厂模式。

红影 发表于 2022-8-25 16:17

马黑黑 发表于 2022-8-25 12:25
这不是最科学的方法。关于创建对象,我喜欢用原形法。我一直没介绍的还有工厂模式。

工厂模式就跟装配式差不多的吧?

马黑黑 发表于 2022-8-25 18:51

红影 发表于 2022-8-25 16:17
工厂模式就跟装配式差不多的吧?

所以才叫工厂模式

红影 发表于 2022-8-25 19:01

马黑黑 发表于 2022-8-25 18:51
所以才叫工厂模式

这倒是方便。

马黑黑 发表于 2022-8-25 19:02

红影 发表于 2022-8-25 19:01
这倒是方便。

也有缺陷。这里不说这个。

红影 发表于 2022-8-25 19:11

马黑黑 发表于 2022-8-25 19:02
也有缺陷。这里不说这个。

是适应性方面的缺陷么?

马黑黑 发表于 2022-8-25 19:12

红影 发表于 2022-8-25 19:11
是适应性方面的缺陷么?

不说这个了,我们暂时不用它。

红影 发表于 2022-8-25 19:17

马黑黑 发表于 2022-8-25 19:12
不说这个了,我们暂时不用它。

好的。

马黑黑 发表于 2022-8-25 19:23

红影 发表于 2022-8-25 19:17
好的。

{:4_181:}
页: [1]
查看完整版本: canvas画布上模拟缓动