Как мне изменить размер моего холста с моими элементами в нем? - PullRequest
1 голос
/ 06 февраля 2020

Справочная информация

В настоящее время я работаю над проектом, и я хочу, чтобы фон был чистым холстом с шарами, отскакивающими от стен.

До сих пор я справился, но Я столкнулся с проблемой. Всякий раз, когда я изменяю размер своего окна браузера, ни мой холст, ни шары в нем не проходят. Понятия не имею, что я делаю не так.

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>

Ответы [ 3 ]

2 голосов
/ 07 февраля 2020

вы можете использовать ctx.scale(x,y); для масштабирования всего на холсте по заданному коэффициенту, X и Y смещаются по оси X и Y соответственно.

Возможно, вы захотите сбросить холст с помощью ctx.resetTransform() или ctx.setTransform(1,0,0,1,0,0); Затем нарисуйте свой фон от 0,0 до canvas.width и canvas.height. Затем нарисуйте все (все круги) и, наконец, установите желаемое значение для scalling.

1 голос
/ 07 февраля 2020

Если вы хотите изменить ширину и высоту, вам просто нужно добавить ширину и высоту в той же области CSS, в которой находится `{border-box}.

, но если вы хотите чтобы изображения внутри него не растягивались, вам необходимо получить доступ к высоте конкретно на холсте, используя методы вышеупомянутых комментариев.

Предпочтительно использовать:

width:() => document.documentElement.clientWidth
0 голосов
/ 07 февраля 2020

Сначала вы должны исправить логику столкновений c, затем вы можете смело менять размер холста по мере необходимости.

Всегда разрешать коллизии полностью.

Неразрешенное столкновение означает, что ваш сим находится в невозможном состоянии (например, мяч внутри стены). С этого момента следующие состояния будут иметь меньшее значение.

Fix

Причина, по которой шары выходят из холста, заключается в том, что вы не перемещаете их обратно на холст, если они находятся снаружи.

Также, когда вы изменяете направление мяча, когда он ударяет в сторону, вы не гарантируете, что направление является правильным знаком.

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

Будьте систематизированы c в тесте на столкновение, чтобы когда игровое поле (холст) меняет размер, вы не получите неожиданных результатов

При обновлении сначала переместите, а затем проверьте наличие столкновений. Если произошло столкновение, переместите шар еще раз, чтобы не происходило никакого магического c шара в стене.

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

Изменение размера холста

После решения проблемы столкновения вы можете изменить размер холста, когда вам нужно. Один из способов - сделать это внутри функции анимации.

Если размер холста не соответствует размеру страницы, измените размер холста, чтобы он соответствовал.

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.

Демонстрация копирования и вставки

В коде было много плохих моментов. Вы можете скопировать вставленный ниже фрагмент в кодовое перо или использовать приведенную выше информацию, чтобы изменить свой код в своем стиле.

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