Движение элемента по периметру

Как выполнить движение элемента внутри блока по периметру?

var canvas = document.getElementById('perim');
var ctx = canvas.getContext('2d');
var n = 4;
var x = 10;
var y = 10;

function draw(x, y) {
  ctx.fillRect(x, y, 20, 20);
}

function move() {
  x += n;
  if (x > canvas.width || x < 0) {
    n = -n
  }
}

function game() {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  draw(x, y);
  move();
  requestAnimationFrame(game)
}
game();
canvas {
  border: 1px solid black;
}
<canvas id="perim" width="400" height="200"></canvas>


Ответы (3 шт):

Автор решения: Kirill

Думаю, не самое изящное решение, но работает.

var canvas = document.getElementById('perim');
var ctx = canvas.getContext('2d');
var n = 5;
var x = 0;
var y = 0;

function draw(x, y) {
  ctx.fillRect(x, y, 20, 20);
}

function move() {
  if (x < canvas.width - 20 && y == 0) {
    x += n;
  } else if (x == canvas.width - 20 && y < canvas.height - 20) {
    y += n;
  } else if (y == canvas.height - 20 && x > 0) {
    x -= n;
  } else if (x == 0 && y > 0) {
    y -= n;
  }
}

function game() {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  draw(x, y);
  move();
  requestAnimationFrame(game)
}
game();
canvas {
  border: 1px solid black;
}
<canvas id="perim" width="400" height="200"></canvas>

→ Ссылка
Автор решения: Sevastopol'

Первые два варианта не мои, были в заначке, достал. Автора не знаю, но ему спасибо.

Один

var div = document.querySelector('div>div');
(run = () => {
  var l = parseFloat(getComputedStyle(div).left),
    t = parseFloat(getComputedStyle(div).top);
  if (l < 279 && t == 0) div.style.left = l + 1 + 'px';
  if (l == 279 && t < 139) div.style.top = t + 1 + 'px';
  if (l > 0 && t == 139) div.style.left = l - 1 + 'px';
  if (l == 0 && t > 0) div.style.top = t - 1 + 'px';
  setTimeout(run, 4);
})();
div {
  position: relative;
  width: 300px;
  height: 160px;
  border: 1px solid black;
}

div>div {
  display: block;
  width: 20px;
  height: 20px;
  background-color: black;
}
<div>
  <div></div>
</div>

Два

var canvas = document.getElementById("perim");
var ctx = canvas.getContext("2d");
var n = 4;
var x = 0;
var y = 0;

function draw(x, y) {
  ctx.fillStyle = "black";
  ctx.fillRect(x, y, 20, 20)
}
var a = 0;

function move() {
  var b = false;
  switch (a) {
    case 0:
      b = x > canvas.width - 24;
      break;
    case 1:
      b = y > canvas.height - 24;
      break;
    case 2:
      b = x < 4;
      break;
    case 3:
      b = y < 4;
      break
  }
  if (b) {
    a = ++a % 4;
    a % 2 || (n = -n)
  }
  a % 2 ? y += n : x += n
}

function game() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  draw(x, y);
  move();
  requestAnimationFrame(game)
}
game();
canvas {
  border: 1px solid black;
}
<canvas id="perim" width="300" height="160"></canvas>

И добавлю свой вариант. Самое простое, что можно сделать для решения этой задачи. Зачем Canvas, если есть CSS.

div {
  position: relative;
  width: 300px;
  height: 160px;
  border: 1px solid black;
}

span {
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: 20px;
  height: 20px;
  background-color: black;
  animation: elem 3s linear infinite;
}

@keyframes elem {
  0% {
    top: 0;
    left: 0;
  }
  34% {
    top: 0;
    left: 280px;
  }
  50% {
    top: 140px;
    left: 280px;
  }
  84% {
    top: 140px;
    left: 0;
  }
  100% {
    top: 0;
    left: 0;
  }
}
<div><span></span></div>

→ Ссылка
Автор решения: Stranger in the Q

Например вот так, поколдовал, чтобы была продолжительность одного цикла задана переменной и постоянная скорость на разных ребрах:

var canvas = document.getElementById('perim');
var ctx = canvas.getContext('2d');

var pad = 2;
var x = 0;
var y = 0;
var s = 20;
var d = 4000;

function clamp(x) {
  return Math.max(0, Math.min(x, 1));
}

function draw(x, y) {
  ctx.fillRect(x-s/2, y-s/2, s, s);
}

function move(t) {
  let ratio = canvas.width/canvas.height
  let halfCycle = ratio + 1;
  t = t % d / d * 2 * halfCycle;
  x = clamp(t/ratio) - clamp((t - halfCycle)/ratio);
  y = clamp(t - ratio) - clamp(t - halfCycle - ratio);
  x = pad+s/2 + x * (canvas.width - s - pad*2);
  y = pad+s/2 + y * (canvas.height - s - pad*2);
}

function game(t) {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  move(t);
  draw(x, y);
  requestAnimationFrame(game);
}

requestAnimationFrame(game);
canvas {
  border: 1px solid black;
}
<canvas id="perim" width="400" height="150"></canvas>

→ Ссылка