Как нарисовать повёрнутый квадрат с помощью обычных линий?
Вот как на изображении квадрат построенный на гипотенузе:
const context = canvas.getContext('2d')
xxx(); onresize = xxx
function xxx() {
const w = canvas.width = innerWidth
const h = canvas.height = innerHeight
const cw = w/2, ch = h/2, s = (w+h) / 2
const p1 = { x: cw - s * 0.09, y: ch + s * 0.09 }
const p2 = { x: cw + s * 0.09, y: ch - s * 0.09 }
const a = Math.abs(p2.x - p1.x)
const b = Math.abs(p2.y - p1.y)
context.lineCap = 'round'
// Нижний квадрат
context.strokeStyle = 'lightgreen'
line(p1.x, p1.y, p1.x, p1.y + a) // левая линия
line(p1.x + a, p1.y, p1.x + a, p1.y + a) // правая линия
line(p1.x, p1.y + a, p1.x + a, p1.y + a) // нижняя линия
// Правый квадрат
context.strokeStyle = 'lightblue'
line(p1.x + a, p1.y, p1.x + a + b, p1.y) // нижняя линия
line(p1.x + a, p1.y - b, p1.x + a + b, p1.y - b) // верхняя линия
line(p1.x + a + b, p1.y - b, p1.x + a + b, p1.y) // правая линия
// Треугольник
context.strokeStyle = 'tomato'
line(p1.x, p1.y, p1.x + a, p1.y) // нижняя линия
line(p1.x + a, p1.y, p1.x + a, p1.y - b) // правая линия
line(p1.x, p1.y, p1.x + a, p1.y - b) // верхняя линия
function line(x1,y1, x2,y2) {
context.lineWidth = 4
context.beginPath()
context.moveTo(x1, y1)
context.lineTo(x2, y2)
context.stroke()
}
}
body {
margin: 0;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
Я так понимаю нужно как-то хитро перемножить стороны на косинусы и синусы, но я не настолько хитёр (
Ответы (3 шт):
Формула вращения на угол a относительно x_0, y_0:
X = x_0 + (x − x_0) * cos(a) − (y − y_0 ) * sin(a)
Y = y_0 + (y − y_0) * cos(a) + (x − x_0 ) * sin(a)
x, y - исходные координаты
как-то так:
let dist = Math.sqrt(a*a+b*b)
let alpha = Math.atan2(b,a) + Math.PI/2
let dx = Math.cos(alpha)*dist;
let dy = -Math.sin(alpha)*dist;
line(p1.x, p1.y, p1.x + dx, p1.y + dy);
line(p2.x, p2.y, p2.x + dx, p2.y + dy);
line(p1.x + dx, p1.y + dy, p2.x + dx, p2.y + dy);
const context = canvas.getContext('2d')
requestAnimationFrame(xxx)
function xxx(t) {
const w = canvas.width = innerWidth
const h = canvas.height = innerHeight
const cw = w/2, ch = h/2, s = (w+h) / 2
let s1 = 0.03 + Math.sin(t/1000)*0.02
let s2 = 0.07 + Math.cos(t/2000)*0.02
const p1 = { x: cw - s * s1, y: ch + s * s2 }
const p2 = { x: cw + s * s1, y: ch - s * s2 }
const a = Math.abs(p2.x - p1.x)
const b = Math.abs(p2.y - p1.y)
context.lineCap = 'round'
context.strokeStyle = 'orange'
let dist = Math.sqrt(a*a+b*b)
let alpha = Math.atan2(b,a) + Math.PI/2
let dx = Math.cos(alpha)*dist;
let dy = -Math.sin(alpha)*dist;
line(p1.x, p1.y, p1.x + dx, p1.y + dy);
line(p2.x, p2.y, p2.x + dx, p2.y + dy);
line(p1.x + dx, p1.y + dy, p2.x + dx, p2.y + dy);
// Нижний квадрат
context.strokeStyle = 'lightgreen'
line(p1.x, p1.y, p1.x, p1.y + a) // левая линия
line(p1.x + a, p1.y, p1.x + a, p1.y + a) // правая линия
line(p1.x, p1.y + a, p1.x + a, p1.y + a) // нижняя линия
// Правый квадрат
context.strokeStyle = 'lightblue'
line(p1.x + a, p1.y, p1.x + a + b, p1.y) // нижняя линия
line(p1.x + a, p1.y - b, p1.x + a + b, p1.y - b) // верхняя линия
line(p1.x + a + b, p1.y - b, p1.x + a + b, p1.y) // правая линия
// Треугольник
context.strokeStyle = 'tomato'
line(p1.x, p1.y, p1.x + a, p1.y) // нижняя линия
line(p1.x + a, p1.y, p1.x + a, p1.y - b) // правая линия
line(p1.x, p1.y, p1.x + a, p1.y - b) // верхняя линия
requestAnimationFrame(xxx)
}
function line(x1,y1, x2,y2) {
context.lineWidth = 4
context.beginPath()
context.moveTo(x1, y1)
context.lineTo(x2, y2)
context.stroke()
}
body {
margin: 0;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
Чтобы не рассчитывать координаты точек самому, можно перепоручить это элементу canvas, просто повернув его как нужно, перед началом рисования.
Для поворота можно воспользоваться методом .rotate, аргументом является угол в радинах.
При этом подходе достаточно рисовать обычные прямоугольники и просто поворачивать их на нужный угол.
Для того, чтобы настройки поворотов не затрагивали весь canvas можно сохранять настройки перед рисованием и сбрасывать их после. Для этого можно воспользоваться методами .save и .restore.
Также нужно задать точку, вокруг которой будет производиться поворот. Сделать это можно с помощью метода .translate
Чтобы облегчить работу с углами можно отразить холст относительно оси x с помощью метода .scale, после этого оси будут привычно направлены: x - влево, y - вверх.
Пример:
const context = canvas.getContext('2d')
xxx();
onresize = xxx
function xxx() {
const w = canvas.width = innerWidth
const h = canvas.height = innerHeight
const cw = w / 2,
ch = h / 2,
s = (w + h) / 2
const p1 = {
x: cw - s * 0.09,
y: ch + s * 0.09
}
const p2 = {
x: cw + s * 0.09,
y: ch - s * 0.09
}
const a = Math.abs(p2.x - p1.x)
const b = Math.abs(p2.y - p1.y)
context.lineCap = 'round'
// Нижний квадрат
rect('lightgreen', {
x: p2.x,
y: p1.y
}, b, b, Math.PI);
// Правый квадрат
rect('lightblue', p2, a, a, -Math.PI / 2);
rect('gray', p1, Math.sqrt(a * a + b * b), Math.sqrt(a * a + b * b), Math.PI / 4);
// Треугольник
context.strokeStyle = 'tomato'
line(p1.x, p1.y, p1.x + a, p1.y) // нижняя линия
line(p1.x + a, p1.y, p1.x + a, p1.y - b) // правая линия
line(p1.x, p1.y, p1.x + a, p1.y - b) // верхняя линия
function line(x1, y1, x2, y2) {
context.lineWidth = 4;
context.beginPath()
context.moveTo(x1, y1)
context.lineTo(x2, y2)
context.stroke()
}
function rect(color, point, width, height, angle = 0) {
context.save();
context.strokeStyle = color;
context.translate(point.x, point.y);
context.scale(1, -1)
context.rotate(angle);
line(0, 0, 0, height);
line(0, height, width, height);
line(width, height, width, 0);
context.restore();
}
}
body {
margin: 0;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
Для произвольного прямоугольного треугольника, можно воспользоваться тем, что отношение катетов дает тангенс угла, на который нужно повернуть, следовательно нужный угол можно узнать с помощью метода Math.atan
const context = canvas.getContext('2d')
xxx();
onresize = xxx
function xxx() {
const w = canvas.width = innerWidth
const h = canvas.height = innerHeight
const cw = w / 2,
ch = h / 2,
s = (w + h) / 2
const p1 = {
x: cw - s * 0.09,
y: ch + s * 0.09
}
const p2 = {
x: cw + s * 0.09,
y: ch
}
const a = Math.abs(p2.x - p1.x)
const b = Math.abs(p2.y - p1.y)
context.lineCap = 'round'
// Нижний квадрат
rect('lightgreen', {
x: p2.x,
y: p1.y
}, a, a, Math.PI);
// Правый квадрат
rect('lightblue', p2, b, b, -Math.PI / 2);
rect('gray', p1, Math.sqrt(a * a + b * b), Math.sqrt(a * a + b * b), Math.atan(b / a));
// Треугольник
context.strokeStyle = 'tomato'
line(p1.x, p1.y, p1.x + a, p1.y) // нижняя линия
line(p1.x + a, p1.y, p1.x + a, p1.y - b) // правая линия
line(p1.x, p1.y, p1.x + a, p1.y - b) // верхняя линия
function line(x1, y1, x2, y2) {
context.lineWidth = 4;
context.beginPath()
context.moveTo(x1, y1)
context.lineTo(x2, y2)
context.stroke()
}
function rect(color, point, width, height, angle = 0) {
context.save();
context.strokeStyle = color;
context.translate(point.x, point.y);
context.scale(1, -1)
context.rotate(angle);
line(0, 0, 0, height);
line(0, height, width, height);
line(width, height, width, 0);
context.restore();
}
}
body {
margin: 0;
}
canvas {
display: block;
}
<canvas id="canvas"></canvas>
