canvas学习笔记----运动和三角函数

[canvas]--运动和三角函数

image.png

image.png

image.png

image.png

image.png

image.png

建立一个画布

image.png

<style>
    body,html {
      margin:0;
      height:100%;
    }
    #canvas{
      box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
      position: absolute;
      top: 10%;
      left: 5%;
    }
  </style>
</head>
<body>
  <canvas id="canvas"></canvas>
  <script src="/scriopt/utils.js"></script>
  <script>
    const canvas = document.getElementById('canvas')
    const ctx=canvas.getContext('2d');
​
    let W = canvas.width = 800;
    let H = canvas.height = 600;
    let pos = C.getOffset(canvas);
    canvas.onclick = function(){
      console.log(pos.x,pos.y)
    }
  </script>
复制代码

工具函数

let C = {};
//获取鼠标在元素上的坐标
C.getOffset = function(ele){
  let mouse = {x: 0,y: 0};
  ele.addEventListener('mousemove',function(e){
  let {x,y} = C.eventWrapper(e);
  mouse.x = x;
  mouse.y = y;
  })
  return mouse;
};
//坐标系转换
C.eventWrapper = function (ev){
  let {pageX,pageY,target} = ev;
  let {left,top} = target.getBoundingClientRect();
  return {x: pageX - left,y:pageY - top};
  };
 //角度转弧度
 C.toRad = function (angle){
   return angle * Math.PI / 180;
 };
 //弧度转角度
 C.toAngle = function(rad){
 return rad * 180 / Math.PI
 };
复制代码

画布建立坐标系

image.png

<style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
  let W = canvas.width = 800;
  let H = canvas.height = 600;
​
  //获取鼠标所在坐标
  let pos = C.getOffset(canvas);
    canvas.onclick = function(){
      console.log(pos.x,pos.y)
    }
​
  //获取实时的鼠标在画布上的位置
  let mouse = C.getOffset(canvas);
​
  drawSystem()
​
​
  //添加一个鼠标跟踪器
  canvas.onmousemove = function (){
    //清除画布
    ctx.clearRect(0,0,W,H)
​
    //计算跟踪线与X坐标轴之间的夹角
    let dx = mouse.x - W/2;
    let dy = mouse.Y - H/2;
    //计算
    // let angle = Math.atan(dy/dx) * 180 / Math.PI;
    let angle = Math.atan2(dy,dx) * 180 / Math.PI;
​
​
    drawSystem()
    //起始新的路径
    ctx.beginPath()
    ctx.lineTo(mouse.x,mouse.y);
    //画布中心点
    ctx.lineTo(W/2,H/2);
    ctx.stroke()
​
    //绘制文本
    ctx.fillText(angle,mouse.x,mouse.y);
​
  }
  //设立直角坐标系
  function drawSystem(){
    //保存绘图环境
    ctx.save();
    //设定线条宽度
    ctx.lineWidth = 1.5;
    //起始一个新的路径
    ctx.beginPath();
    //先将画笔移动到0,二分之一h的位置
    ctx.moveTo(0,H/2);
    //再移动到下面的这个位置上
    ctx.lineTo(W,H/2);
    ctx.moveTo(W/2,0);
    ctx.lineTo(W/2,H);
​
    //调用描边函数
    ctx.stroke();
​
    //恢复绘图环境
    ctx.restore();
  }
​
</script>
复制代码

鼠标跟踪器

QQ录屏20220603101730_.gif

首先绘制箭头

image.png

class Arrow{
  constructor(props){
  //constructor () 方法是一种特殊的方法,用于创建和初始化在类中创建的对象。
    this.x=0;
    this.y=0
    this.w=60
    this.h=30
    //旋转角度
    this.rotation=0
    this.fillStyle='rgba(57,119,224)'
    //描边
    this.strokeStyle='rgba(0,0,0)'
    //对传进来的props参数进行初始化
    //Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
    Object.assign(this,props)
    return this;
  }
  //用来指定箭头的绘制路径
  createPath(ctx){
    let {w,h} = this
    //起始一个新的路径
    ctx.beginPath()
    ctx.moveTo(-w/2,-h/2)
    ctx.lineTo(w/10,-h/2)
    ctx.lineTo(w/10,-h)
    ctx.lineTo(w/2,0)
    ctx.lineTo(w/10,h)
    ctx.lineTo(w/10,h/2)
    ctx.lineTo(-w/2,h/2)
    ctx.closePath();
    return this
​
  }
  //实际用来绘制箭头的一个方法
  render(ctx){
    //填充颜色
    let {fillStyle,strokeStyle,rotation,x,y} = this;
    ctx.save();
    ctx.fillStyle=fillStyle
    ctx.strokeStyle=strokeStyle;
    ctx.translate(x,y);
    ctx.rotate(rotation)
    this.createPath(ctx)
    ctx.fill()
    ctx.stroke()
    ctx.restore()
    return this
  }
}
​
复制代码

rotateArrow.html

<style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script src="/scriopt/Arrow.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
  let W = canvas.width = 800;
  let H = canvas.height = 600;
  
  //实时获取鼠标在画布上的位置
  let mouse = C.getOffset(canvas)
  //创建箭头的函数
  //实例化一个箭头的类
  const arrow = new Arrow({
    //在画布中心点绘制这个箭头
    x:W/2,
    y:H/2,
    w:180,
    h:60
  }).render(ctx)
​
  //添加鼠标移动事件
  //首先添加鼠标的坐标值
  canvas.onmousemove = function(){
    //先求鼠标坐标点与箭头中心之间的夹角
    let dx=mouse.x - arrow.x;
    let dy =mouse.y-arrow.y
    arrow.rotation = Math.atan2(dy,dx)
    //移动过程中不断地清除画布,然后重新绘制这个箭头
    ctx.clearRect(0,0,W,H)
    arrow.render(ctx)
​
  }
</script>
  
</body>
复制代码

正弦波的应用

image.png 正弦波:

image.png 小球的基本特征绘制

image.png

ball.js

class Ball {
  constructor(props) {
    this.x = 0;
    this.y = 0;
    this.r = 20;
    //横向缩放倍数
    this.scaleX = 1;
    //纵向缩放倍数
    this.scaleY = 1;
    this.strokeStyle = 'rgba(1,0,0,0)';
    this.fillStyle = 'rgba(57,119,224)'
    this.alpha = 1
    Object.assign(this, props);
    return this;
  }
  render(ctx) {
    let { x, y, r, scaleX, scaleY, fillStyle, strokeStyle, alpha } = this
    ctx.save()
    ctx.translate(x, y)
    ctx.scale(scaleX, scaleY);
    ctx.strokeStyle = strokeStyle;
    ctx.fillStyle = fillStyle;
    ctx.globalAlpha = alpha;
    ctx.beginPath()
    ctx.arc(0, 0, r, 0, 2 * Math.PI)
    ctx.fill();
    ctx.stroke();
    ctx.restore();
    return this
  }
}
​
复制代码

平滑运动

小球左右摆动

QQ录屏20220603102415_.gif

 <style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(10, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script src="/scriopt/ball.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
  let W = canvas.width = 800;
  let H = canvas.height = 600;
​
  //在画布中间绘制一个半径为50的小球
  const ball = new Ball({
    x:W/2,
    y:H/2,
    r:50,
  }).render(ctx);
​
  let angle = 0;
  //振幅
  const SWING = 160;
​
  (function move(){
    //动画帧
    // window.requestAnimationFrame(move);
    console.log(move)
​
    //清除画布
    ctx.clearRect(0,0,W,H);
    //每一帧改变小球的位置,就等于画布中央的横向位置
    ball.x = W/2 + Math.sin(angle) * SWING;
​
    angle += 0.05;
    angle = angle%(Math.PI*2);
    ball.render(ctx)
​
  })()
  // move()
​
  </script>
复制代码

平滑运动

QQ录屏20220603102803_.gif

<style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script src="/scriopt/ball.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
​
 
  let W = canvas.width = 800;
  let H = canvas.height = 600;
​
  const ball = new Ball({
    x:100,
    y:H/2,
    r:30,
  }).render(ctx);
​
  //起始弧度
  let angle=0;
  let vx=1;
​
  (function move(){
    window.requestAnimationFrame(move);
    console.log(move,'222222222')
    ctx.clearRect(0,0,W,H);
    ball.x +=vx;
    ball.render(ctx)
  })()
  </script>
复制代码

曲线运动

QQ录屏20220603105831_.gif

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
   <style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script src="/scriopt/ball.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
​
 
  let W = canvas.width = 800;
  let H = canvas.height = 600;
​
  const ball = new Ball({
    x:100,
    y:H/2,
    r:30,
  }).render(ctx);
​
  //起始弧度
  let angle=0;
  let vx=1;
  let vy=0.5; 
  const SWING = 60;
​
  (function move(){
    window.requestAnimationFrame(move);
    console.log(move,'222222222')
    ctx.clearRect(0,0,W,H);
    ball.x +=vx;
    ball.y = H/2 + Math.sin(angle)*SWING;
    angle+=0.05;
    angle%=Math.PI*2
​
    ball.render(ctx)
  })()
  </script>
复制代码

脉冲运动

QQ录屏20220603111617_.gif

就是将sin函数运用到物体的大小变化中,以达到平滑的放大和缩小的效果

 <style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script src="/scriopt/ball.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
  let W = canvas.width = 800;
  let H = canvas.height = 600;
   const ball = new Ball({
    x:W/2,
    y:H/2,
    r:50,
  }).render(ctx);
​
  //小球起始弧度
  let angle = 0;
  //小球起始缩放比
  let initScale=1;
​
  const SWING = 0.5;
​
  (function move(){
    window.requestAnimationFrame(move)
    ctx.clearRect(0,0,W,H);
    ball.scaleX = ball.scaleY=initScale+Math.sin(angle)*SWING;
    angle+=0.05;
    angle%=Math.PI*2
    ball.render(ctx)
  })()
​
​
  </script>
复制代码

\

正圆运动

1_.gif

<style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script src="/scriopt/ball.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
  let W = canvas.width = 800;
  let H = canvas.height = 600;
​
  //绘制小球及其位置
  const ball = new Ball({
    x:W/2,
    y:H/2,
    r:35
  }).render(ctx);
  let angle = 0;//弧度
  let speed =0.02;
  let r = 150;
​
  (function move(){
    window.requestAnimationFrame(move)
    ctx.clearRect(0,0,W,H)
    ctx.beginPath();
    ctx.arc(W/2,H/2,r,0,Math.PI*2)
    ctx.stroke();
    ball.x=W/2+r*Math.cos(angle);
    ball.y=H/2+r*Math.sin(angle);
    angle+=speed;
    angle%=Math.PI*2;
    ball.render(ctx)
  })()
​
​
​
</script>
复制代码

椭圆运动

2_.gif

<style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script src="/scriopt/ball.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
  let W = canvas.width = 800;
  let H = canvas.height = 600;
​
  //绘制小球及其位置
  const ball = new Ball({
    x:W/2,
    y:H/2,
    r:35
  }).render(ctx);
  let angle = 0;//弧度
  let speed =0.02;
  let rx = 200;
  let ry = 80;
​
  (function move(){
    window.requestAnimationFrame(move)
    ctx.clearRect(0,0,W,H)
    ctx.save()
    //位移到画布的中心位置
    ctx.translate(W/2,H/2);
    //调整缩放比
    ctx.scale(1,0.4)
    ctx.beginPath();
    ctx.arc(0,0,rx,0,Math.PI*2)
    ctx.stroke();
    ctx.restore()
    ball.x=W/2+rx*Math.cos(angle);
    ball.y=H/2+ry*Math.sin(angle);
    angle+=speed;
    angle%=Math.PI*2;
​
    ball.render(ctx)
  })()
​
​
​
</script>
复制代码

速度向量

image.png 向量:带有箭头的线段,线段的长度就是速度的大小,方向就是向量的方向

在动画中紧紧描述物体的速度移动是不够的的,还需要加上移动的方向

image.png

从左向右运动,就是从左向右每一帧的像素值

单线运动:

1_1.gif

//绘制小球及其位置
  const ball = new Ball({
    x:50,
    y:H/2,
    r:40
  }).render(ctx);
  
  let vx = 0.5;
​
  (function move(){
    window.requestAnimationFrame(move)
    //每一帧中清空画布
    ctx.clearRect(0,0,W,H)
    //每一帧中不断的加上vx
    ball.x+=vx
    ball.render(ctx)
  })()
​
复制代码

速度向量的合成

image.png

2_2.gif

//绘制小球及其位置
  const ball = new Ball({
    x:50,
    y:50,
    r:30
  }).render(ctx);
  
  let vx = 0.5;
  let vy = 0.5;
​
  (function move(){
     window.requestAnimationFrame(move)
    //每一帧中清空画布
    ctx.clearRect(0,0,W,H)
    //每一帧中不断的加上vx
    ball.x+=vx;
    ball.y+=vy;
    ball.render(ctx)
  })()
复制代码

速度向量的分解

3_.gif

image.png

//绘制小球及其位置
  const ball = new Ball({
    x:50,
    y:50,
    r:30
  }).render(ctx);
  
  let speed=2;
  let angle=30*Math.PI/180;
​
  (function move(){
    window.requestAnimationFrame(move)
    //每一帧中清空画布
    ctx.clearRect(0,0,W,H)
    let vx = speed*Math.cos(angle);
    let vy = speed*Math.sin(angle)
    ball.x+=vx;
    ball.y+=vy;
​
    ball.render(ctx)
  })()
复制代码

案例:箭头跟随鼠标运动并且可以旋转

4_.gif

 <style>
  body,html {
    margin:0;
    height:100%;
  }
  #canvas{
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.5);
    position: absolute;
    top: 10%;
    left: 5%;
  }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="/scriopt/utils.js"></script>
<script src="/scriopt/Arrow.js"></script>
<script>
  const canvas = document.getElementById('canvas')
  const ctx=canvas.getContext('2d');
​
  let W = canvas.width = 800;
  let H = canvas.height = 600;
​
  const arrow = new Arrow({
    x:W/2,
    y:H/2,
    w:140,
    h:60
  }).render(ctx);
  //获取鼠标实时坐标
  let mouse = C.getOffset(canvas)
  let speed = 3;
​
  (function move(){
    // window.requestAnimationFrame(move)
    
    let dx = mouse.x-arrow.x;
    let dy = mouse.y-arrow.y;
    let angle = Math.atan2(dy,dx)
​
    let vx = speed*Math.cos(angle);
    let vy = speed*Math.sin(angle)
    arrow.x+=vx;
    arrow.y+=vy;
    arrow.rotation = angle;
    //每一帧中清空画布
    ctx.clearRect(0,0,W,H)
    arrow.render(ctx)
  })()
​
​
​
</script>
复制代码

箭头旋转运动

5_.gif

const arrow = new Arrow({
    x:W/2,
    y:H/2,
    w:140,
    h:60
  }).render(ctx);
  //获取鼠标实时坐标
  let vr = 2*Math.PI/180;
​
  (function move(){
    window.requestAnimationFrame(move)
    ctx.clearRect(0,0,W,H)
    arrow.rotation+=vr
    arrow.render(ctx)
  })()
复制代码

加速度

image.png 单轴上的速度

image.png

6_.gif

​
  //绘制小球及其位置
  const ball = new Ball({
    x:70,
    y:H/2,
    r:30
  }).render(ctx);
  
  let vx = 0;
  let ax = 0.1;
​
  (function move(){
    // window.requestAnimationFrame(move)
    //每一帧中清空画布
    ctx.clearRect(0,0,W,H)
    //每一帧中不断的加上vx
    ball.x+=vx;
    vx+=ax
    ball.render(ctx)
  })()
​
​
复制代码

任意方向上的加速度

image.png

2222_.gif

  //绘制小球及其位置
  const ball = new Ball({
    x:50,
    y:50,
    r:30
  }).render(ctx);
  
​
  let angle = C.toRad(30);
  let a = 0.1;
  let vx = 0;
  let vy = 0;
​
  (function move(){
    // window.requestAnimationFrame(move)
    //每一帧中清空画布
    ctx.clearRect(0,0,W,H)
    let ax = Math.cos(angle)*a;
    let ay = Math.sin(angle)*a;
​
    ball.x+=vx;
    ball.y+=vy;
    vx+=ax;
    vy+=ay;
    
    ball.render(ctx)
  })()
复制代码

重力加速度

8_.gif

 //绘制小球及其位置
  const ball = new Ball({
    x:W/2,
    y:100,
    r:30
  }).render(ctx);
  
  let g = 0.2;
  let vy = 0;
​
  (function move(){
    // window.requestAnimationFrame(move)
    //每一帧中清空画布
    ctx.clearRect(0,0,W,H)
    //每一帧中不断的加上vx
    ball.y+=vy;
    vy+=g
​
    if(ball.y+ball.r>=H){
      //给小球一个相反的作用力
      ball.y=H-ball.r;
      vy*=-0.8
    }
​
    ball.render(ctx)
  })()
​
复制代码

猜你喜欢

转载自juejin.im/post/7104976385013186597