【问题标题】:How do I resize my canvas with my elements in it?如何使用其中的元素调整画布的大小?
【发布时间】:2020-02-06 20:04:55
【问题描述】:

背景资料

我目前正在做一个项目,我希望背景是一张空白画布,上面有从墙上弹起的球。

到目前为止,我已经做到了,但我遇到了一个问题。 每当我调整浏览器窗口的大小时,我的画布或其中的球都不会跟随。我不知道我做错了什么。

codepen

代码

const canvas = document.querySelector('#responsive-canvas')

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

let c = canvas.getContext('2d');

function Circle(x, y, dx, dy, radius, colour) {
  this.x = x;
  this.y = y;
  this.dx = dx;
  this.dy = dy;
  this.radius = radius;
  this.colour = colour;


  this.draw = function() {

    this.getNewColour = function() {
      let symbols, colour;

      symbols = "0123456789ABCDEF";

      colour = "#";

      for (let i = 0; i < 6; i++) {
        colour += symbols[Math.floor(Math.random() * 16)];
      }
      c.strokeStyle = colour;
      c.fillStyle = colour;
    }
    this.getNewColour();


    this.x = x;
    this.y = y;
    this.radius = radius;
    //this.getNewColour().colour = colour;

    c.beginPath();
    c.arc(x, y, radius, 0, Math.PI * 2, false);
    //  c.strokeStyle = 'blue';
    c.stroke();
    //c.fill();
  }

  this.update = function() {
    this.x = x;
    this.y = y;
    this.dx = dx;
    this.dy = dy;
    this.radius = radius;

    if (x + radius > innerWidth || x - radius < 0) {
      dx = -dx;
    }

    if (y + radius > innerHeight || y - radius < 0) {
      dy = -dy;
    }
    x += dx;
    y += dy;

    this.draw();
  }
}

let circleArr = [];

for (let i = 0; i < 23; i++) {

  let radius = 50;
  let x = Math.random() * (innerWidth - radius * 2) + radius;
  let y = Math.random() * (innerHeight - radius * 2) + radius;
  let dx = (Math.random() - 0.5);
  let dy = (Math.random() - 0.5);

  circleArr.push(new Circle(x, y, dx, dy, radius));
};




function animate() {
  requestAnimationFrame(animate);
  c.clearRect(0, 0, innerWidth, innerHeight)

  for (var i = 0; i < circleArr.length; i++) {
    circleArr[i].update();
  }
};



animate();
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}


/* BODY
*/

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
}
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
  <meta name="description" content="">
  <link rel="stylesheet" href="../dist/css/style.css">

  <body>
    <canvas id="responsive-canvas"></canvas>
  </body>
  <script src="js/canvas.js"></script>

</html>

【问题讨论】:

    标签: javascript html css canvas resize


    【解决方案1】:

    你可以使用 ctx.scale(x,y); 要按给定因子缩放画布上的所有内容,X 和 Y 分别在 X 和 Y 轴上缩放。

    您可能希望使用重置画布 ctx.resetTransform()ctx.setTransform(1,0,0,1,0,0); 然后将背景从 0,0 绘制到 canvas.width 和 canvas.height 然后绘制所有内容(所有圆圈),最后将缩放设置为您想要的值。

    【讨论】:

    • 警告resetTransform 是实验性技术,并非所有浏览器都完全支持。使用替代方法安全地重置转换,如本答案中所示,setTransform(1,0,0,1,0,0);
    【解决方案2】:

    如果你想改变宽度和高度,你只需要在`{border-box}所在的同一个CSS区域添加宽度和高度。

    但如果您想让其中的图像不被拉伸,则需要使用上述 cmets 方法专门访问画布中的高度。

    最好使用:

    width:() => document.documentElement.clientWidth
    

    【讨论】:

      【解决方案3】:

      首先您必须修复碰撞逻辑,然后您可以根据需要安全地更改画布大小。

      始终完全解决冲突。

      未解决的碰撞意味着您的模拟市民处于不可能的状态(例如球在墙内)。从那时起,以下状态的意义将减少。

      修复

      球移出画布的原因是,如果它们在外面,您不会将它们移回画布上。

      此外,当您改变球击中一侧的方向时,您并不能确保方向是正确的标志。

          // if the dx = 1 and x > innerWidth + 1000 then dx becomes -1
          // Next tine x is still > innerWidth + 1000 and you flip the sign of dx again to 1,
          // Then dx to -1 and so on. You never move the ball 
          if(x + radius > innerWidth || x - radius < 0) {
              dx = -dx;  
          } 
      

      在碰撞测试中要系统化,这样当运动场(画布)改变大小时,你不会得到意外的结果

      更新时,先移动然后检查碰撞。如果发生碰撞,再次移动球,以确保不会发生墙上的魔法球。

      update() {
          this.x += this.dx;
          this.y += this.dy;
          if (this.x < this.radius) {
              this.x = this.radius;
              this.dx = Math.abs(this.dx);
          }
          if (this.y < this.radius) {
              this.y = this.radius;
              this.dy = Math.abs(this.dy);
          }
          if (this.x > ctx.canvas.width - this.radius) {
              this.x = ctx.canvas.width - this.radius;
              this.dx = -Math.abs(this.dx);
          }
          if (this.y > ctx.canvas.height - this.radius) {
              this.y = ctx.canvas.height - this.radius;
              this.dy = -Math.abs(this.dy);
          }
      }
      

      调整画布大小

      解决了碰撞问题后,您现在可以随时更改画布大小。一种方法是在 animate 函数中进行。

      如果画布大小与页面大小不匹配,请更改画布大小以匹配。

      function animate () {
          if (ctx.canvas.width !== innerWidth || ctx.canvas.height !== innerHeight) {
              ctx.canvas.width = innerWidth;
              ctx.canvas.height = innerHeight;
          } else {
              ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
          }
      
          // ... do stuff
      
          requestAnimationFrame(animate);
      } 
      

      注意,更改画布大小也会清除画布,这就是为什么在 else 子句中清除。

      复制粘贴演示

      代码中有很多不好的地方。您可以将下面的 sn-p 复制粘贴到代码笔中或使用上面的信息以您的风格修改代码。

      const canvas = document.querySelector('#responsive-canvas')
      
      canvas.width = innerWidth;
      canvas.height = innerHeight;
      
      // Set constants in one place so you can make changes quickly and easily
      const DEFAULT_RADIUS = 50;
      const MAX_SPEED = 5;
      const CIRCLE_COUNT = 100;
      Math.TAU = Math.PI * 2
      
      // Use function to do repetitive code
      Math.rand = (min, max) => Math.random() * (max - min) + min;  // 
      function randomHexColor() { 
          return "#" + ((Math.random() * 0xFFFFFF | 0).toString(16).padStart(6,"0"));
      }
      
      // pulral names for arrays and variables that do not change should be constants
      const circles = [];
      const ctx = canvas.getContext('2d');
      
      requestAnimationFrame(animate); // start frame renderer with a request, dont call it directly
      
      
      
      function Circle( // using default params to set random values
              radius = Math.rand(DEFAULT_RADIUS/4, DEFAULT_RADIUS), // radius must be first argument as its used to set random x, and y
              x = Math.rand(radius, ctx.canvas.width - radius), 
              y = Math.rand(radius, ctx.canvas.height - radius),
              dx = Math.rand(-MAX_SPEED, MAX_SPEED),
              dy = Math.rand(-MAX_SPEED, MAX_SPEED),
              colour = randomHexColor()
          ) {
      
          this.x = x;
          this.y = y;
          this.dx = dx;
          this.dy = dy;
          this.radius = radius;
          this.colour = colour;
      }
      // Define Circle functions as prototype outside the function Circle (runs faster)
      Circle.prototype = {
          draw() {
              ctx.strokeStyle = ctx.fillStyle = this.colour; 
              ctx.beginPath();
              ctx.arc(this.x, this.y, this.radius, 0, Math.TAU);
              ctx.stroke();
          },
          update() {
              this.x += this.dx;
              this.y += this.dy;
              if (this.x < this.radius) {
                  this.x = this.radius;
                  this.dx = Math.abs(this.dx);
              }
              if (this.y < this.radius) {
                  this.y = this.radius;
                  this.dy = Math.abs(this.dy);
              }
              if (this.x > ctx.canvas.width - this.radius) {
                  this.x = ctx.canvas.width - this.radius;
                  this.dx = -Math.abs(this.dx);
              }
              if (this.y > ctx.canvas.height - this.radius) {
                  this.y = ctx.canvas.height - this.radius;
                  this.dy = -Math.abs(this.dy);
              }
          }
      };
      for (let i = 0; i < CIRCLE_COUNT; i++) { circles.push(new Circle()) } 
      function animate () {
          if (ctx.canvas.width !== innerWidth || ctx.canvas.height !== innerHeight) {
              ctx.canvas.width = innerWidth;
              ctx.canvas.height = innerHeight;
          } else {
              ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
          }
      
          // use for of loop (saves having to mess with index)
          for (const circle of circles) {
                  circle.update(); // seperate update and draw
                  circle.draw()
          }
          requestAnimationFrame(animate);
      } 
      

      【讨论】:

      • 感谢您的反馈,我知道我的代码很乱。我在之前链接的同一个 codepen 上检查了你的代码 sn-p,我很好奇为什么你的 randomHexColour 函数不起作用?我是 ES6 的新手,我不太了解语法。此外,我尝试使用自己的 getNewColour 函数,而不是将其实现到您的代码中。它工作得相当好,除了在我调整窗口大小后所有的球都变黑了。我该如何解决这个问题?代码笔已更新。同样,我对 web 开发还很陌生,这是我的第一个画布。我很感激帮助。干杯
      • @Kemura 哦,很抱歉颜色函数我忘了对随机值进行四舍五入,最重要的是使用Number.toString(16) 将值转换为十六进制,正确的版本是。 return "#" + (Math.random() * 0xFFFFFF | 0).toString(16).padStart(6,"0");。我会将其添加到答案中。当您调整画布大小时,它会重置其状态(样式默认为黑色)。确保在渲染圆圈之前将ctx.strokeStyle 和/或ctx.fillStyle 设置为您想要的颜色。
      猜你喜欢
      • 2010-09-24
      • 2012-01-31
      • 1970-01-01
      • 2017-12-23
      • 2019-04-29
      • 2019-09-23
      • 2016-06-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多