Dot絵エディタ6 : ViewPort
ViewPort
最も単純なマウス座標->グリッド座標の変換
// handleMouseDown(event) {
// const rect = this.canvas.getBoundingClientRect();
// const x = event.clientX - rect.left;
// const y = event.clientY - rect.top;
// const cell = this.getGridIndex(x, y);
// this.isDrawing = true;
// this.lastCell = cell;
// this.toolManager.getCurrentTool().handleMouseDown(this, cell);
// this.draw();
// }
// handleMouseMove(event) {
// if (!this.isDrawing) return;
// const rect = this.canvas.getBoundingClientRect();
// const x = event.clientX - rect.left;
// const y = event.clientY - rect.top;
// const cell = this.getGridIndex(x, y);
// if (cell.row !== this.lastCell.row || cell.col !== this.lastCell.col) {
// this.toolManager.getCurrentTool().handleMouseMove(this, cell);
// this.lastCell = cell;
// this.draw();
// }
// }
// handleMouseUp(event) {
// if (!this.isDrawing) return;
// const rect = this.canvas.getBoundingClientRect();
// const x = event.clientX - rect.left;
// const y = event.clientY - rect.top;
// const cell = this.getGridIndex(x, y);
// this.isDrawing = false;
// this.toolManager.getCurrentTool().handleMouseUp(this, cell);
// this.draw();
// }
グリッド座標への変換
getGridIndex(x, y) {
const col = Math.floor(x / this.cellWidth);
const row = Math.floor(y / this.cellHeight);
return { row, col };
}
ViewPortを考慮する
マウス座標も必要になったのでhandleに渡す引数が(e, grid, cell)に。
cellはグリッド座標。
// handleMouseDown(event) {
// const viewManager = ViewManager.getInstance(this);
// const rect = this.canvas.getBoundingClientRect();
// const x = (event.clientX - rect.left) / viewManager.viewport.scale + viewManager.viewport.x;
// const y = (event.clientY - rect.top) / viewManager.viewport.scale + viewManager.viewport.y;
// const cell = this.getGridIndex(x, y);
// this.isDrawing = true;
// this.lastCell = cell;
// this.toolManager.getCurrentTool().handleMouseDown(event, this, cell);
// this.draw();
// }
// handleMouseMove(event) {
// if (!this.isDrawing) return;
// const viewManager = ViewManager.getInstance(this);
// const rect = this.canvas.getBoundingClientRect();
// const x = (event.clientX - rect.left) / viewManager.viewport.scale + viewManager.viewport.x;
// const y = (event.clientY - rect.top) / viewManager.viewport.scale + viewManager.viewport.y;
// const cell = this.getGridIndex(x, y);
// if (cell.row !== this.lastCell.row || cell.col !== this.lastCell.col) {
// this.toolManager.getCurrentTool().handleMouseMove(event, this, cell);
// this.lastCell = cell;
// this.draw();
// }
// }
// handleMouseUp(event) {
// if (!this.isDrawing) return;
// const viewManager = ViewManager.getInstance(this);
// const rect = this.canvas.getBoundingClientRect();
// const x = (event.clientX - rect.left) / viewManager.viewport.scale + viewManager.viewport.x;
// const y = (event.clientY - rect.top) / viewManager.viewport.scale + viewManager.viewport.y;
// const cell = this.getGridIndex(x, y);
// this.isDrawing = false;
// this.toolManager.getCurrentTool().handleMouseUp(event, this, cell);
// this.draw();
// }
ViewPortはCanvasのtransformを用いた変形を管理する。
class ViewManager {
constructor(grid) {
if (ViewManager.instance) {
return ViewManager.instance;
}
ViewManager.instance = this;
this.grid = grid;
this.viewport = {
x: 0,
y: 0,
scale: 1,
rotation: 0
};
this.originalState = {
x: 0,
y: 0,
scale: 1,
rotation: 0
};
}
static getInstance(grid) {
if (!ViewManager.instance) {
ViewManager.instance = new ViewManager(grid);
}
return ViewManager.instance;
}
rotate(angle) {
this.viewport.rotation += angle;
this.grid.draw();
}
zoom(factor, centerX, centerY) {
const oldScale = this.viewport.scale;
this.viewport.scale *= factor;
// Adjust pan to keep the center point stationary
this.viewport.x += (centerX / oldScale - centerX / this.viewport.scale);
this.viewport.y += (centerY / oldScale - centerY / this.viewport.scale);
this.grid.draw();
}
pan(dx, dy) {
this.viewport.x += dx / this.viewport.scale;
this.viewport.y += dy / this.viewport.scale;
this.grid.draw();
}
reset() {
this.viewport = {...this.originalState};
this.grid.draw();
}
// applyTransform(ctx) {
// ctx.save();
// ctx.scale(this.viewport.scale, this.viewport.scale);
// ctx.translate(-this.viewport.x, -this.viewport.y);
// }
applyTransform(ctx) {
ctx.save();
ctx.translate(this.grid.width / 2, this.grid.height / 2);
ctx.rotate(this.viewport.rotation);
ctx.scale(this.viewport.scale, this.viewport.scale);
ctx.translate(-this.grid.width / 2 - this.viewport.x, -this.grid.height / 2 - this.viewport.y);
}
restoreTransform(ctx) {
ctx.restore();
}
getViewportParams() {
return {
x: this.viewport.x.toFixed(2),
y: this.viewport.y.toFixed(2),
scale: this.viewport.scale.toFixed(2),
rotation: (this.viewport.rotation * 180 / Math.PI).toFixed(2) // Convert to degrees
};
}
updateViewportInfo() {
const params = this.getViewportParams();
document.getElementById('viewportX').textContent = params.x;
document.getElementById('viewportY').textContent = params.y;
document.getElementById('viewportScale').textContent = params.scale;
document.getElementById('viewportRotation').textContent = params.rotation;
}
}
回転考慮
本来不要な機能だが、秒で実装してくれたのでつけた。
->バグった。
回転には対応してるがスケールと平行移動でずれる。
handleMouseDown(event) {
const viewManager = ViewManager.getInstance(this);
const rect = this.canvas.getBoundingClientRect();
const [x, y] = this.getRotatedCoordinates(event, rect, viewManager);
const cell = this.getGridIndex(x, y);
this.isDrawing = true;
this.lastCell = cell;
this.toolManager.getCurrentTool().handleMouseDown(event, this, cell);
this.draw();
}
handleMouseMove(event) {
if (!this.isDrawing) return;
const viewManager = ViewManager.getInstance(this);
const rect = this.canvas.getBoundingClientRect();
const [x, y] = this.getRotatedCoordinates(event, rect, viewManager);
const cell = this.getGridIndex(x, y);
if (cell.row !== this.lastCell.row || cell.col !== this.lastCell.col) {
this.toolManager.getCurrentTool().handleMouseMove(event, this, cell);
this.lastCell = cell;
this.draw();
}
}
handleMouseUp(event) {
if (!this.isDrawing) return;
const viewManager = ViewManager.getInstance(this);
const rect = this.canvas.getBoundingClientRect();
const [x, y] = this.getRotatedCoordinates(event, rect, viewManager);
const cell = this.getGridIndex(x, y);
this.isDrawing = false;
this.toolManager.getCurrentTool().handleMouseUp(event, this, cell);
this.draw();
}
回転後マウス座標->非回転マウス座標へ
getRotatedCoordinates(event, rect, viewManager) {
// Calculate the center of the canvas
const centerX = rect.width / 2;
const centerY = rect.height / 2;
// Translate mouse coordinates to be relative to the center
let mouseX = event.clientX - rect.left - centerX;
let mouseY = event.clientY - rect.top - centerY;
// Apply inverse rotation
const rotation = -viewManager.viewport.rotation;
const rotatedX = mouseX * Math.cos(rotation) - mouseY * Math.sin(rotation);
const rotatedY = mouseX * Math.sin(rotation) + mouseY * Math.cos(rotation);
// Translate back and apply scale and pan
const x = (rotatedX + centerX) / viewManager.viewport.scale + viewManager.viewport.x;
const y = (rotatedY + centerY) / viewManager.viewport.scale + viewManager.viewport.y;
return [x, y];
}
バグり果てた末
いまんとこ一番調子いい。
ハンドラは変わらず。
applyTransform(ctx) {
ctx.save();
ctx.scale(this.viewport.scale, this.viewport.scale);
ctx.translate(-this.viewport.x, -this.viewport.y);
ctx.translate(this.grid.width / 2, this.grid.height / 2);
ctx.rotate(this.viewport.rotation);
ctx.translate(-this.grid.width / 2, -this.grid.height / 2);
}
getRotatedCoordinates(event, rect, viewManager) {
// Calculate the center of the canvas
const centerX = rect.width / 2;
const centerY = rect.height / 2;
// Translate mouse coordinates to be relative to the center
let mouseX = event.clientX - rect.left - centerX;
let mouseY = event.clientY - rect.top - centerY;
// Apply inverse scale
mouseX /= viewManager.viewport.scale;
mouseY /= viewManager.viewport.scale;
// Apply inverse rotation
const rotation = -viewManager.viewport.rotation;
const cos = Math.cos(rotation);
const sin = Math.sin(rotation);
const rotatedX = mouseX * cos - mouseY * sin;
const rotatedY = mouseX * sin + mouseY * cos;
// Translate back to the canvas coordinate system and apply pan
const x = rotatedX + centerX / viewManager.viewport.scale + viewManager.viewport.x;
const y = rotatedY + centerY / viewManager.viewport.scale + viewManager.viewport.y;
return [x, y];
}
この記事が気に入ったらサポートをしてみませんか?