Перемещение фигур JS
У меня есть проблемы с перемещением фигур на канвасе. При первом нажатии на прямоугольник все нормально - я кликаю внутри него и перемещаю. После первого перемещения начинаются какие-то проблемы с координатами и прямоугольник уже не нажимается там, где он отрисован. Нажимается чуть ниже. Я где-то ошибся с рассчетом координат, но не могу понять, где.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Diagram Editor</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="geMenubarContainer">
<a class="geIcon"></a>
<div class="geTextDiv">
<a class="geItem"></a>
</div>
<div class="geMenubar">
<a class="geItem">Файл</a>
<a class="geItem">Правка</a>
<a class="geItem">Вид</a>
<a class="geItem">Положение</a>
<a class="geItem">Дополнительно</a>
<a class="geItem">Помощь</a>
</div>
</div>
<div class="geToolbarContainer">
<div class="geToolbar"></div>
<a class="geLabel" title="Масштаб (Alt+Mousewheel)">
100%
<img src="icons/arrow.gif"></img>
</a>
<div class="geSeparator"></div>
<a class="geLabel" title="Увеличить">
<img src="icons/glass1.png"></img>
</a>
<a class="geLabel" title="Уменьшить">
<img src="icons/glass2.png"></img>
</a>
<div class="geSeparator"></div>
<a class="geLabel" title="Отменить (Ctrl+Z)">
<img src="icons/undo.png"></img>
</a>
<a class="geLabel" title="Вернуть (Ctrl+Y)">
<img src="icons/revert.png"></img>
</a>
<div class="geSeparator"></div>
<a class="geLabel" title="Удалить (Delete)">
<img src="icons/delete.png"></img>
</a>
<div class="geSeparator"></div>
<a class="geLabel" title="Цвет линии">
<img src="icons/pencil.png"></img>
</a>
<a class="geLabel" title="Цвет заливки">
<img src="icons/filling.png"></img>
</a>
<input type="color" id="color-pick-input" title="Цвет линии">
<input type="range" id="line-width-input" min="1" max="40">
<div class="geSeparator"></div>
<a class="geLabel" title="Тип соединения">
<img src="icons/lines.png"></img>
</a>
<a id="text" class="geLabel" title="Добавить текст">
<img src="icons/text.png"></img>
</a>
<div class="geSeparator"></div>
<a id="clear-button" class="geLabel" title="Добавить текст">
<img src="icons/clear.png"></img>
</a>
<a id="save-button" class="geLabel" title="Добавить текст">
<img src="icons/save.png"></img>
</a>
<div class="geSeparator"></div>
</div>
<div id="diagram-container">
<canvas id="canvas" width="1300" height="800" ></canvas>
</div>
<div id="sidebar">
<img id="btn-toggle-menu" src="icons/next.png"></img>
<h3>Фигуры</h3>
<a class="geTitle" >Избранное</a>
<a class="geTitle" >Общие</a>
<div class="geSidebar">
<a class="geItem" title="прямая линия" data-shape="line">
<img src="mainFigures/line.png" ></img>
</a>
<a class="geItem" title="прямоугольник" data-shape="rectangle">
<img src="mainFigures/rectangle.png" ></img>
</a>
<a class="geItem" title="круг" data-shape="circle">
<img src="mainFigures/circle.png" ></img>
</a>
<a class="geItem" title="эллипс">
<img src="mainFigures/ellipse.png" ></img>
</a>
<a class="geItem" title="треугольник">
<img src="mainFigures/triangle.png" ></img>
</a>
<a class="geItem" title="трапеция">
<img src="mainFigures/trapezium.png" ></img>
</a>
<a class="geItem" title="ромб">
<img src="mainFigures/rhomb.png" ></img>
</a>
<a class="geItem" title="параллелограмм">
<img src="mainFigures/parallelogram.png" ></img>
</a>
<a class="geItem" title="пятиугольник">
<img src="mainFigures/pentagon.png" ></img>
</a>
<a class="geItem" title="шестиугольник">
<img src="mainFigures/hexagon.png" ></img>
</a>
<a class="geItem" title="односторонняя стрелка">
<img src="mainFigures/arrows.png" ></img>
</a>
<a class="geItem" title="двусторонняя стрелка">
<img src="mainFigures/double-arrow.png" ></img>
</a>
<a class="geItem" title="криволинейная стрелка" >
<img src="mainFigures/curve-arrow.png" ></img>
</a>
</div>
<a class="geTitle" >Блок-схемы</a>
<a class="geTitle" >UML</a>
</div>
<script src="script.js"></script>
<script src="draw.js"></script>
</body>
</html>
style.css:
body {
font-family: Arial, sans-serif;
display:flex;
flex-direction: column;
height: 100vh;
margin: 0;
padding: 0;
background-color: #fbfbfb;
align-items: center;
justify-content: center;
}
a:hover {
color: #FFFF00;
}
a {
text-decoration: none;
cursor: pointer;
}
.geMenubarContainer{
display: inline-flex;
align-items: center;
overflow: hidden;
position: absolute;
cursor: default;
top: 0px;
left: 0px;
right: 0px;
height: 64px;
background-color: #f1f3f4;
}
.geMenubarContainer .geTextDiv{
position: absolute;
right: 120px;
left: 60px;
top: 9px;
height: 26px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
visibility: visible;
}
.geTextDiv .geItem{
padding: 2px 8px;
display: inline;
font-size: 18px;
transition: all 0.1s ease-in-out;
cursor: pointer;
}
.geMenubar{
display: inline-flex;
white-space: nowrap;
align-items: center;
position: absolute;
padding-left: 59px;
box-sizing: border-box;
top: 34px;
}
.geMenubarContainer .geItem{
padding: 6px 6px 6px 9px;
transition: all 0.1s ease-in-out;
}
.geMenubarContainer .geIcon{
display: block;
position: absolute;
top: 12px;
width: 36px;
height: 36px;
margin: 8px 0px 8px 16px;
opacity: 0.85;
border-radius: 3px;
background-position: center center;
background-size: 90% 90%;
background-repeat: no-repeat;
background-image: url('../icons/icon.png')
}
.geToolbarContainer{
border-width: 1px;
border-style: none none solid none;
box-shadow: none;
overflow: hidden;
position: absolute;
cursor: default;
z-index: 1;
left: 0px;
right: 0px;
top: 64px;
height: 38px;
background-color: #f1f3f4;
}
.geToolbar{
padding-left: 16px;
border-top: 1px solid #dadce0;
box-shadow: inset 0 1px 0 0 #fff;
padding-bottom: 3px;
}
.geSeparator{
float: left;
width: 1px;
height: 20px;
background: #e5e5e5;
margin-left: 8px;
margin-right: 6px;
margin-top: 4px;
}
.geLabel{
white-space: nowrap;
position: relative;
overflow: hidden;
width: 50px;
float: left;
margin: 2px;
padding: 3px 5px 3px 5px;
border: 1px solid transparent;
transition: all 0.1s ease-in-out;
}
.geToolbar a {
color: #000000;
text-decoration: none;
}
.geToolbarContainer img{
width:20px;
height:20px;
}
.geButton {
float: left;
width: 20px;
height: 20px;
padding: 0px 2px 4px 2px;
margin: 2px;
border: 1px solid transparent;
cursor: pointer;
opacity: 0.6;
transition: all 0.1s ease-in-out;
}
#diagram-container {
position: absolute;
width: calc(100% - 215px); /* Ширина сайдбара */
height: calc(100% - 64px - 38.8px); /* Высота экрана за вычетом высоты тулбара и сайдбара */
top: 110px; /* Высота тулбара */
left: 59px; /* Ширина тулбара */
}
#canvas {
width: 1300px;
height: 600px;
border: 1px solid #ccc;
background-color: #FFFFFF;
}
#sidebar {
position: fixed;
overflow-y: scroll;
z-index: 1;
top: 100px;
bottom: 0px;
right: 0;
width: 215px;
height: 100vh;
background-color: #f1f3f4;
padding: 20px;
box-shadow: -5px 0px 5px rgba(0, 0, 0, 0.1);
transition: right 0.3s ease;
}
#sidebar.open {
right: -230px;
}
/* Стили для кнопки */
#sidebar h3 {
position: relative;
padding-left: 30px; /* Добавляем отступ слева для размещения кнопки */
text-align: center;
}
#btn-toggle-menu {
position: absolute;
width: 20px; /* Размер изображения */
height: 20px;
left: 0; /* Положение слева */
top: 0; /* Положение сверху */
background-color: transparent;
border: none;
padding: 0;
margin: 0;
cursor: pointer;
}
#btn-toggle-menu img {
width: 20px; /* Размер изображения */
height: 20px;
}
.geTitle{
display: block;
background-image: url('../icons/arrow.gif');
background-repeat: no-repeat;
background-position: 4px 50%;
position: relative;
padding-left: 72px;
border-bottom: 1px solid #e5e5e5;
border-top: 1px solid #e5e5e5;
font-weight: 500;
font-size: 13px;
border-color: #e5e5e5;
}
.geSidebar{
display: block;
transform-origin: right top 0px;
border-bottom: 1px solid #e5e5e5;
padding: 6px;
overflow: hidden;
}
.geSidebar .geItem{
display: inline-block;
background-repeat: no-repeat;
background-position: 50% 50%;
border-radius: 8px;
overflow: hidden;
width: 34px;
height: 32px;
padding: 1px;
}
.geItem img{
right: 1px;
top: 1px;
width: 32px;
height: 30px;
display: block;
position: relative;
overflow: hidden;
pointer-events: none;
}
.rectangle {
border: 3px solid #FF0000;
position: absolute;
}
draw.js:
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
const mouse = {
x: 0,
y: 0,
left: false,
right: false,
over: false,
dragging: false, // флаг для перемещения фигуры
offset: { x: 0, y: 0 } // смещение относительно координат фигуры
}
canvas.addEventListener("mouseenter", mouseEnterHandler);
canvas.addEventListener("mousemove", mouseMoveHandler);
canvas.addEventListener("mouseleave", mouseLeaveHandler);
canvas.addEventListener("mousedown", mouseDownHandler);
canvas.addEventListener("mouseup", mouseUpHandler);
// Добавляем слушатели событий на ссылки в sidebar
const sidebarLinks = document.querySelectorAll('#sidebar .geItem');
sidebarLinks.forEach(function(link) {
link.addEventListener('click', function(event) {
// Получаем значение атрибута 'data-shape' ссылки
const shape = event.target.getAttribute('data-shape');
// Рисуем выбранную фигуру сразу по клику на кнопку
drawShape(shape);
});
});
function mouseEnterHandler(event) {
mouse.over = true;
}
function mouseMoveHandler(event) {
const rect = canvas.getBoundingClientRect();
mouse.x = event.clientX - rect.left;
mouse.y = event.clientY - rect.top;
if (mouse.dragging) { // если мышь зажата и перемещение активно
// перемещаем фигуру
moveShape();
}
}
function mouseLeaveHandler(event) {
mouse.over = false;
}
function mouseDownHandler(event) {
// если кликнули внутрь фигуры, начинаем перемещение
if (isInsideShape()) {
mouse.dragging = true;
// считаем смещение относительно координат фигуры
mouse.offset.x = mouse.x - shape.x;
mouse.offset.y = mouse.y - shape.y;
}
}
function mouseUpHandler(event) {
mouse.dragging = false; // завершаем перемещение
}
function isInsideShape() {
return (
mouse.x >= shape.x &&
mouse.x <= shape.x + shape.width &&
mouse.y >= shape.y &&
mouse.y <= shape.y + shape.height
);
}
// Перемещаем фигуру на новое место
function moveShape() {
// вычисляем новые координаты фигуры с учетом смещения
shape.x = mouse.x - mouse.offset.x;
shape.y = mouse.y - mouse.offset.y;
// очищаем холст и рисуем фигуру с новыми координатами
clearCanvas();
drawRectangle(shape.x, shape.y);
}
// Функция для рисования выбранной фигуры
function drawShape(shape) {
switch (shape) {
case 'line':
drawLine();
break;
case 'rectangle':
drawRectangle(50,50);
break;
case 'circle':
drawCircle();
break;
// Добавьте здесь обработку других фигур, если необходимо
default:
console.error('Неизвестная фигура:', shape);
break;
}
}
// Функции для рисования различных фигур
function drawLine() {
// Реализуйте рисование прямой линии
context.beginPath();
context.moveTo(50, 50);
context.lineTo(200, 200);
context.stroke();
}
function drawRectangle(x,y) {
// Реализуйте рисование прямоугольника
context.beginPath();
context.rect(x, y, shape.width, shape.height);
context.stroke();
}
function drawCircle() {
// Реализуйте рисование круга
context.beginPath();
context.arc(100, 100, 50, 0, Math.PI * 2);
context.stroke();
}
// Очищаем холст
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
// Начальные координаты и размеры прямоугольника
const shape = {
x: 50,
y: 50,
width: 100,
height: 80
};
Ответы (1 шт):
Ваша математика расчета координат абсолютно верна! Проблема здесь в самом способе отображения canvas. Обратите внимание на 3 вещи (когда вы выполняете ваш код):
- при перемещении прямоугольника вдоль оси Y (по вертикали) вниз - курсор убегает быстрее, чем сам прямоугольник, причем по оси Х такого эффекта не наблюдается. Здесь можно подумать, что есть ошибка в расчетах переноса фигуры, однако это не так, потому что следующий захват, как вы и описали, происходит чуть ниже изображения прямоугольника, т.е. все координаты рассчитаны верно - математически прямоугольник находится ниже, чем отображается. Здесь можно подумать, что прямоугольник отрисовывается с неверными координатами, однако это тоже не так - методы контекста canvas используются верно и с правильными координатами;
- а теперь обратите внимание на то, что прямоугольник с начальными координатами 50, 50 по горизонтали чуть дальше от края холста canvas, чем по вертикали, хотя и там, и там по 50px.
- затем я изменил начальные размеры прямоугольника на 100, 100 и сделал его квадратом. Сделайте это тоже ради интереса и вы увидите, что на холсте ваш квадрат - вовсе не квадрат, а прямоугольник.
В общем все это - искажение размеров внутренней системы координат canvas (размеров контекста, если это будет удобнее для понимания) относительно самого html-элемента canvas. Можете открыть любые источники и убедиться, что canvas имеет два размера. В то же время система координат курсора работает "над" canvas-ом, в обычной системе координат окна браузера и если масштаб внутренней системы координат canvas отличается от единичной (какая и используется в браузере), то как раз и возникают такие искажения.
А теперь к решению проблемы. Варианта 2:
- вводить поправочный коэффициент при расчете координат;
- выставлять размеры canvas таким образом, чтобы масштаб внутренней системы координат соответствовал браузерной, т.е. был единичным.
Первый вариант даже рассматривать не буду - сложная математика и не понятно зачем так извращаться.
Второй вариант - лично я для себя взял за правило следующий алгоритм работы с canvas:
- помещаем canvas в контейнер, допустим div;
- в стилях для canvas обязательно указываю: dispaly: block; width: 100%: height: 100%. Теперь на странице canvas будет занимать родителя и управлять размерами на странице будем только через родителя. Так можно получить абсолютно адаптивный canvas;
- в JS canvas должен подстроить размеры своей внутренней системы координат под размеры родительского контейнера, а т.к. сам canvas занимает 100% ширины и высоты родителя, то и получается единичный масштаб внутренней системы координат canvas относительно html-элемента canvas. Это легко сделать: canvas.height = canvasContainer.offsetHeight; canvas.width = canvasContainer.offsetWidth;
- !!!ВАЖНО: если сделать это единожды (как и написано в моем решении), то это сработает только при первичной инициализации и будет работать до тех пор, пока вы не измените размеры страницы. Как только контейнер, в котором расположен canvas, изменит свои размеры - все сломается. Вам остается решить эту проблему и тут есть 2 варианта: отслеживать изменения размеров контейнера и соответствующим образом обрабатывать их, обновляя значение размеров внутренней системы координат canvas, либо использовать что-то типа requestAnimationFrame(). Когда я делал проект на canvas, использовал второй вариант.
PS: кстати, если вы запустите мой код здесь, то в маленьком окошке он будет работать корректно. А если вы развернете на весь экран, то все сломается. Это как раз то, о чем я писал чуть выше в п.4.
PPS: добавил отслеживание изменения размеров родительского контейнера canvas-элемента.
const canvasContainer = document.querySelector('#diagram-container')
const canvas = document.querySelector("canvas");
const context = canvas.getContext("2d");
const mouse = {
x: 0,
y: 0,
left: false,
right: false,
over: false,
dragging: false, // флаг для перемещения фигуры
offset: { x: 0, y: 0 } // смещение относительно координат фигуры
}
canvas.addEventListener("mouseenter", mouseEnterHandler);
canvas.addEventListener("mousemove", mouseMoveHandler);
canvas.addEventListener("mouseleave", mouseLeaveHandler);
canvas.addEventListener("mousedown", mouseDownHandler);
canvas.addEventListener("mouseup", mouseUpHandler);
//установка размеров внутреннего размера canvas в соответствии с размерами родительского контейнера
function canvasInitSize() {
canvas.height = canvasContainer.offsetHeight;
canvas.width = canvasContainer.offsetWidth;
}
//прослушиваем изменение размеров контейнера, в котором хранится canvas
new ResizeObserver(() => canvasInitSize()).observe(canvasContainer);
// Добавляем слушатели событий на ссылки в sidebar
const sidebarLinks = document.querySelectorAll('#sidebar .geItem');
sidebarLinks.forEach(function(link) {
link.addEventListener('click', function(event) {
// Получаем значение атрибута 'data-shape' ссылки
const shape = event.target.getAttribute('data-shape');
// Рисуем выбранную фигуру сразу по клику на кнопку
drawShape(shape);
});
});
function mouseEnterHandler(event) {
mouse.over = true;
}
function mouseMoveHandler(event) {
const rect = canvas.getBoundingClientRect();
mouse.x = event.clientX - rect.left;
mouse.y = event.clientY - rect.top;
if (mouse.dragging) { // если мышь зажата и перемещение активно
// перемещаем фигуру
moveShape();
}
}
function mouseLeaveHandler(event) {
mouse.over = false;
}
function mouseDownHandler(event) {
// если кликнули внутрь фигуры, начинаем перемещение
if (isInsideShape()) {
mouse.dragging = true;
// считаем смещение относительно координат фигуры
mouse.offset.x = mouse.x - shape.x;
mouse.offset.y = mouse.y - shape.y;
}
}
function mouseUpHandler(event) {
mouse.dragging = false; // завершаем перемещение
}
function isInsideShape() {
return (
(mouse.x >= shape.x) &&
(mouse.x <= shape.x + shape.width) &&
(mouse.y >= shape.y) &&
(mouse.y <= shape.y + shape.height)
);
}
// Перемещаем фигуру на новое место
function moveShape() {
// вычисляем новые координаты фигуры с учетом смещения
shape.x = mouse.x - mouse.offset.x;
shape.y = mouse.y - mouse.offset.y;
// очищаем холст и рисуем фигуру с новыми координатами
clearCanvas();
drawRectangle(shape.x, shape.y);
}
// Функция для рисования выбранной фигуры
function drawShape(shape) {
switch (shape) {
case 'line':
drawLine();
break;
case 'rectangle':
drawRectangle(50,50);
break;
case 'circle':
drawCircle();
break;
// Добавьте здесь обработку других фигур, если необходимо
default:
console.error('Неизвестная фигура:', shape);
break;
}
}
// Функции для рисования различных фигур
function drawLine() {
// Реализуйте рисование прямой линии
context.beginPath();
context.moveTo(50, 50);
context.lineTo(200, 200);
context.stroke();
}
function drawRectangle(x,y) {
// Реализуйте рисование прямоугольника
context.beginPath();
context.rect(x, y, shape.width, shape.height);
context.stroke();
}
function drawCircle() {
// Реализуйте рисование круга
context.beginPath();
context.arc(100, 100, 50, 0, Math.PI * 2);
context.stroke();
}
// Очищаем холст
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
// Начальные координаты и размеры прямоугольника
const shape = {
x: 50,
y: 50,
width: 100,
height: 100
};
body {
font-family: Arial, sans-serif;
display:flex;
flex-direction: column;
height: 100vh;
margin: 0;
padding: 0;
background-color: #fbfbfb;
align-items: center;
justify-content: center;
}
a:hover {
color: #FFFF00;
}
a {
text-decoration: none;
cursor: pointer;
}
.geMenubarContainer{
display: inline-flex;
align-items: center;
overflow: hidden;
position: absolute;
cursor: default;
top: 0px;
left: 0px;
right: 0px;
height: 64px;
background-color: #f1f3f4;
}
.geMenubarContainer .geTextDiv{
position: absolute;
right: 120px;
left: 60px;
top: 9px;
height: 26px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
visibility: visible;
}
.geTextDiv .geItem{
padding: 2px 8px;
display: inline;
font-size: 18px;
transition: all 0.1s ease-in-out;
cursor: pointer;
}
.geMenubar{
display: inline-flex;
white-space: nowrap;
align-items: center;
position: absolute;
padding-left: 59px;
box-sizing: border-box;
top: 34px;
}
.geMenubarContainer .geItem{
padding: 6px 6px 6px 9px;
transition: all 0.1s ease-in-out;
}
.geMenubarContainer .geIcon{
display: block;
position: absolute;
top: 12px;
width: 36px;
height: 36px;
margin: 8px 0px 8px 16px;
opacity: 0.85;
border-radius: 3px;
background-position: center center;
background-size: 90% 90%;
background-repeat: no-repeat;
background-image: url('../icons/icon.png')
}
.geToolbarContainer{
border-width: 1px;
border-style: none none solid none;
box-shadow: none;
overflow: hidden;
position: absolute;
cursor: default;
z-index: 1;
left: 0px;
right: 0px;
top: 64px;
height: 38px;
background-color: #f1f3f4;
}
.geToolbar{
padding-left: 16px;
border-top: 1px solid #dadce0;
box-shadow: inset 0 1px 0 0 #fff;
padding-bottom: 3px;
}
.geSeparator{
float: left;
width: 1px;
height: 20px;
background: #e5e5e5;
margin-left: 8px;
margin-right: 6px;
margin-top: 4px;
}
.geLabel{
white-space: nowrap;
position: relative;
overflow: hidden;
width: 50px;
float: left;
margin: 2px;
padding: 3px 5px 3px 5px;
border: 1px solid transparent;
transition: all 0.1s ease-in-out;
}
.geToolbar a {
color: #000000;
text-decoration: none;
}
.geToolbarContainer img{
width:20px;
height:20px;
}
.geButton {
float: left;
width: 20px;
height: 20px;
padding: 0px 2px 4px 2px;
margin: 2px;
border: 1px solid transparent;
cursor: pointer;
opacity: 0.6;
transition: all 0.1s ease-in-out;
}
#diagram-container {
position: absolute;
width: calc(100% - 215px); /* Ширина сайдбара */
height: calc(100% - 64px - 38.8px); /* Высота экрана за вычетом высоты тулбара и сайдбара */
top: 110px; /* Высота тулбара */
left: 59px; /* Ширина тулбара */
}
#canvas {
display: block;
width: 100%;
height: 100%;
border: 1px solid #ccc;
background-color: #FFFFFF;
}
#sidebar {
position: fixed;
overflow-y: scroll;
z-index: 1;
top: 100px;
bottom: 0px;
right: 0;
width: 215px;
height: 100vh;
background-color: #f1f3f4;
padding: 20px;
box-shadow: -5px 0px 5px rgba(0, 0, 0, 0.1);
transition: right 0.3s ease;
}
#sidebar.open {
right: -230px;
}
/* Стили для кнопки */
#sidebar h3 {
position: relative;
padding-left: 30px; /* Добавляем отступ слева для размещения кнопки */
text-align: center;
}
#btn-toggle-menu {
position: absolute;
width: 20px; /* Размер изображения */
height: 20px;
left: 0; /* Положение слева */
top: 0; /* Положение сверху */
background-color: transparent;
border: none;
padding: 0;
margin: 0;
cursor: pointer;
}
#btn-toggle-menu img {
width: 20px; /* Размер изображения */
height: 20px;
}
.geTitle{
display: block;
background-image: url('../icons/arrow.gif');
background-repeat: no-repeat;
background-position: 4px 50%;
position: relative;
padding-left: 72px;
border-bottom: 1px solid #e5e5e5;
border-top: 1px solid #e5e5e5;
font-weight: 500;
font-size: 13px;
border-color: #e5e5e5;
}
.geSidebar{
display: block;
transform-origin: right top 0px;
border-bottom: 1px solid #e5e5e5;
padding: 6px;
overflow: hidden;
}
.geSidebar .geItem{
display: inline-block;
background-repeat: no-repeat;
background-position: 50% 50%;
border-radius: 8px;
overflow: hidden;
width: 34px;
height: 32px;
padding: 1px;
}
.geItem img{
right: 1px;
top: 1px;
width: 32px;
height: 30px;
display: block;
position: relative;
overflow: hidden;
pointer-events: none;
}
.rectangle {
border: 3px solid #FF0000;
position: absolute;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Diagram Editor</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="geMenubarContainer">
<a class="geIcon"></a>
<div class="geTextDiv">
<a class="geItem"></a>
</div>
<div class="geMenubar">
<a class="geItem">Файл</a>
<a class="geItem">Правка</a>
<a class="geItem">Вид</a>
<a class="geItem">Положение</a>
<a class="geItem">Дополнительно</a>
<a class="geItem">Помощь</a>
</div>
</div>
<div class="geToolbarContainer">
<div class="geToolbar"></div>
<a class="geLabel" title="Масштаб (Alt+Mousewheel)">
100%
<img src="icons/arrow.gif"></img>
</a>
<div class="geSeparator"></div>
<a class="geLabel" title="Увеличить">
<img src="icons/glass1.png"></img>
</a>
<a class="geLabel" title="Уменьшить">
<img src="icons/glass2.png"></img>
</a>
<div class="geSeparator"></div>
<a class="geLabel" title="Отменить (Ctrl+Z)">
<img src="icons/undo.png"></img>
</a>
<a class="geLabel" title="Вернуть (Ctrl+Y)">
<img src="icons/revert.png"></img>
</a>
<div class="geSeparator"></div>
<a class="geLabel" title="Удалить (Delete)">
<img src="icons/delete.png"></img>
</a>
<div class="geSeparator"></div>
<a class="geLabel" title="Цвет линии">
<img src="icons/pencil.png"></img>
</a>
<a class="geLabel" title="Цвет заливки">
<img src="icons/filling.png"></img>
</a>
<input type="color" id="color-pick-input" title="Цвет линии">
<input type="range" id="line-width-input" min="1" max="40">
<div class="geSeparator"></div>
<a class="geLabel" title="Тип соединения">
<img src="icons/lines.png"></img>
</a>
<a id="text" class="geLabel" title="Добавить текст">
<img src="icons/text.png"></img>
</a>
<div class="geSeparator"></div>
<a id="clear-button" class="geLabel" title="Добавить текст">
<img src="icons/clear.png"></img>
</a>
<a id="save-button" class="geLabel" title="Добавить текст">
<img src="icons/save.png"></img>
</a>
<div class="geSeparator"></div>
</div>
<div id="diagram-container">
<canvas id="canvas"></canvas>
</div>
<div id="sidebar">
<img id="btn-toggle-menu" src="icons/next.png"></img>
<h3>Фигуры</h3>
<a class="geTitle">Избранное</a>
<a class="geTitle">Общие</a>
<div class="geSidebar">
<a class="geItem" title="прямая линия" data-shape="line">
<img src="mainFigures/line.png"></img>
</a>
<a class="geItem" title="прямоугольник" data-shape="rectangle">
<img src="mainFigures/rectangle.png"></img>
</a>
<a class="geItem" title="круг" data-shape="circle">
<img src="mainFigures/circle.png"></img>
</a>
<a class="geItem" title="эллипс">
<img src="mainFigures/ellipse.png"></img>
</a>
<a class="geItem" title="треугольник">
<img src="mainFigures/triangle.png"></img>
</a>
<a class="geItem" title="трапеция">
<img src="mainFigures/trapezium.png"></img>
</a>
<a class="geItem" title="ромб">
<img src="mainFigures/rhomb.png"></img>
</a>
<a class="geItem" title="параллелограмм">
<img src="mainFigures/parallelogram.png"></img>
</a>
<a class="geItem" title="пятиугольник">
<img src="mainFigures/pentagon.png"></img>
</a>
<a class="geItem" title="шестиугольник">
<img src="mainFigures/hexagon.png"></img>
</a>
<a class="geItem" title="односторонняя стрелка">
<img src="mainFigures/arrows.png"></img>
</a>
<a class="geItem" title="двусторонняя стрелка">
<img src="mainFigures/double-arrow.png"></img>
</a>
<a class="geItem" title="криволинейная стрелка">
<img src="mainFigures/curve-arrow.png"></img>
</a>
</div>
<a class="geTitle">Блок-схемы</a>
<a class="geTitle">UML</a>
</div>
<script src="script.js"></script>
<script src="draw.js"></script>
</body>
</html>