Проблема в 1 или в двух вещах
Ваш холст отображается с разрешением 400x300, но имеет только 300x150 пикселей. Холсты имеют 2 размера. Один размер - это размер, который они показывают, установленный с CSS. Другой - это количество пикселей, которое обычно устанавливается в коде путем установки canvas.width
и canvas.height
. Количество пикселей по умолчанию составляет 300x150
Если вы действительно хотите, чтобы они были разных размеров, то вам необходимо учесть это в коде мыши. Правильный код:
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = (e.clientX - rect.left) * canvas.width / rect.width;
let y = (e.clientY - rect.top) * canvas.height / rect.height;
return new Point(x, y);
}
const TOOL_LINE = 'line';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Paint {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = canvas.getContext("2d");
}
set activeTool(tool) {
this.tool = tool;
}
init() {
this.canvas.onmousedown = e => this.onMouseDown(e);
}
onMouseDown(e) {
this.saveData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
this.canvas.onmousemove = e => this.onMouseMove(e);
document.onmouseup = e => this.onMouseUp(e);
this.startPos = this.getMouseCoordinatesCanvas(e, this.canvas);
}
onMouseMove(e) {
this.currentPos = this.getMouseCoordinatesCanvas(e, this.canvas);
switch (this.tool) {
case TOOL_LINE:
this.drawShape();
break;
default:
break;
}
}
onMouseUp(e) {
this.canvas.onmousemove = null;
document.onmouseup = null;
}
drawShape() {
this.context.putImageData(this.saveData, 0, 0);
this.context.beginPath();
this.context.moveTo(this.startPos.x, this.startPos.y);
this.context.lineTo(this.currentPos.x, this.currentPos.y);
this.context.stroke();
}
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = (e.clientX - rect.left) * canvas.width / rect.width;
let y = (e.clientY - rect.top) * canvas.height / rect.height;
return new Point(x, y);
}
}
var paint = new Paint("canvas");
paint.activeTool = TOOL_LINE;
paint.init();
document.querySelectorAll("[data-tools]").forEach(
item => {
item.addEventListener("click", e => {
let selectedTool = item.getAttribute("data-tools");
paint.activeTool = selectedTool;
});
}
);
#Container {
background-color: lime;
height: 310px;
}
.toolbox,
#canvas {
display: inline-block;
}
.toolbox {
background-color: gray;
padding: 0px 15px 15px 15px;
left: 10px;
top: 11px;
}
.group {
margin: 5px 2px;
}
#line {
transform: rotate(-90deg);
}
.ico {
margin: 3px;
font-size: 23px;
}
.item:hover,
.item.active {
background-color: rgba(160, 160, 160, 0.5);
color: white;
}
#canvas {
background-color: white;
margin: 5px;
float: right;
width: 400px;
height: 300px;
}
<script src="https://kit.fontawesome.com/c1d28c00bc.js" crossorigin="anonymous"></script>
<div class="container">
<div id="Container">
<div class="toolbox">
<center>
<div class="group tools">
<div class="item active" data-tools="line">
<i class="ico far fa-window-minimize" id="line" title="Line"></i>
</div>
</div>
</center>
</div>
<canvas id="canvas"></canvas>
</div>
</div>
Если вы не хотите, чтобы они были разных размеров, вам нужно, чтобы размеры соответствовали. Я всегда устанавливаю размер с помощью CSS, а затем использую код, чтобы холст соответствовал этому размеру.
Примерно так:
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
Обратите внимание, что при каждом изменении размера холста он получит очищено, но в любом случае.
const TOOL_LINE = 'line';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
class Paint {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = canvas.getContext("2d");
resizeCanvasToDisplaySize(canvas);
}
set activeTool(tool) {
this.tool = tool;
}
init() {
this.canvas.onmousedown = e => this.onMouseDown(e);
}
onMouseDown(e) {
this.saveData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
this.canvas.onmousemove = e => this.onMouseMove(e);
document.onmouseup = e => this.onMouseUp(e);
this.startPos = this.getMouseCoordinatesCanvas(e, this.canvas);
}
onMouseMove(e) {
this.currentPos = this.getMouseCoordinatesCanvas(e, this.canvas);
switch (this.tool) {
case TOOL_LINE:
this.drawShape();
break;
default:
break;
}
}
onMouseUp(e) {
this.canvas.onmousemove = null;
document.onmouseup = null;
}
drawShape() {
this.context.putImageData(this.saveData, 0, 0);
this.context.beginPath();
this.context.moveTo(this.startPos.x, this.startPos.y);
this.context.lineTo(this.currentPos.x, this.currentPos.y);
this.context.stroke();
}
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = (e.clientX - rect.left) * canvas.width / rect.width;
let y = (e.clientY - rect.top) * canvas.height / rect.height;
return new Point(x, y);
}
}
var paint = new Paint("canvas");
paint.activeTool = TOOL_LINE;
paint.init();
document.querySelectorAll("[data-tools]").forEach(
item => {
item.addEventListener("click", e => {
let selectedTool = item.getAttribute("data-tools");
paint.activeTool = selectedTool;
});
}
);
#Container {
background-color: lime;
height: 310px;
}
.toolbox,
#canvas {
display: inline-block;
}
.toolbox {
background-color: gray;
padding: 0px 15px 15px 15px;
left: 10px;
top: 11px;
}
.group {
margin: 5px 2px;
}
#line {
transform: rotate(-90deg);
}
.ico {
margin: 3px;
font-size: 23px;
}
.item:hover,
.item.active {
background-color: rgba(160, 160, 160, 0.5);
color: white;
}
#canvas {
background-color: white;
margin: 5px;
float: right;
width: 400px;
height: 300px;
}
<script src="https://kit.fontawesome.com/c1d28c00bc.js" crossorigin="anonymous"></script>
<div class="container">
<div id="Container">
<div class="toolbox">
<center>
<div class="group tools">
<div class="item active" data-tools="line">
<i class="ico far fa-window-minimize" id="line" title="Line"></i>
</div>
</div>
</center>
</div>
<canvas id="canvas"></canvas>
</div>
</div>
Распространенная причина делать их разных размеров - поддержка дисплеев HI-DPI. В этом случае, хотя код мыши может go вернуться к тому, как это было, если вы используете преобразование canvas.
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth * devicePixelRatio | 0;
const height = canvas.clientHeight * devicePixelRatio | 0;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
и затем установите преобразование перед рисованием
ctx.scale(devicePixelRatio, devicePixelRatio);
const TOOL_LINE = 'line';
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth * devicePixelRatio | 0;
const height = canvas.clientHeight * devicePixelRatio | 0;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
canvas.width = width;
canvas.height = height;
}
return needResize;
}
class Paint {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = canvas.getContext("2d");
resizeCanvasToDisplaySize(canvas);
}
set activeTool(tool) {
this.tool = tool;
}
init() {
this.canvas.onmousedown = e => this.onMouseDown(e);
}
onMouseDown(e) {
this.saveData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
this.canvas.onmousemove = e => this.onMouseMove(e);
document.onmouseup = e => this.onMouseUp(e);
this.startPos = this.getMouseCoordinatesCanvas(e, this.canvas);
}
onMouseMove(e) {
this.currentPos = this.getMouseCoordinatesCanvas(e, this.canvas);
switch (this.tool) {
case TOOL_LINE:
this.drawShape();
break;
default:
break;
}
}
onMouseUp(e) {
this.canvas.onmousemove = null;
document.onmouseup = null;
}
drawShape() {
this.context.setTransform(1, 0, 0, 1, 0, 0); // the default
this.context.putImageData(this.saveData, 0, 0);
this.context.scale(devicePixelRatio, devicePixelRatio);
this.context.beginPath();
this.context.moveTo(this.startPos.x, this.startPos.y);
this.context.lineTo(this.currentPos.x, this.currentPos.y);
this.context.stroke();
}
getMouseCoordinatesCanvas(e, canvas) {
let rect = canvas.getBoundingClientRect();
let x = (e.clientX - rect.left);
let y = (e.clientY - rect.top);
return new Point(x, y);
}
}
var paint = new Paint("canvas");
paint.activeTool = TOOL_LINE;
paint.init();
document.querySelectorAll("[data-tools]").forEach(
item => {
item.addEventListener("click", e => {
let selectedTool = item.getAttribute("data-tools");
paint.activeTool = selectedTool;
});
}
);
#Container {
background-color: lime;
height: 310px;
}
.toolbox,
#canvas {
display: inline-block;
}
.toolbox {
background-color: gray;
padding: 0px 15px 15px 15px;
left: 10px;
top: 11px;
}
.group {
margin: 5px 2px;
}
#line {
transform: rotate(-90deg);
}
.ico {
margin: 3px;
font-size: 23px;
}
.item:hover,
.item.active {
background-color: rgba(160, 160, 160, 0.5);
color: white;
}
#canvas {
background-color: white;
margin: 5px;
float: right;
width: 400px;
height: 300px;
}
<script src="https://kit.fontawesome.com/c1d28c00bc.js" crossorigin="anonymous"></script>
<div class="container">
<div id="Container">
<div class="toolbox">
<center>
<div class="group tools">
<div class="item active" data-tools="line">
<i class="ico far fa-window-minimize" id="line" title="Line"></i>
</div>
</div>
</center>
</div>
<canvas id="canvas"></canvas>
</div>
</div>
обратите внимание, что эта строка
this.saveData = this.context.getImageData(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);
тоже неверна. Он должен быть
this.saveData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
clientWidth
и clientHeight
- размер экрана. width
и height
- это разрешение (количество пикселей на холсте)