new version
This commit is contained in:
parent
86c0f1419a
commit
5c0d6e4a2f
@ -1,5 +1,5 @@
|
|||||||
import { convertHex } from '../common/convert';
|
import { convertHex } from '../common/convert';
|
||||||
import { clamp } from '../common/helper';
|
import { clamp, debounce } from '../common/helper';
|
||||||
import { Placement } from '../common/types/canvas';
|
import { Placement } from '../common/types/canvas';
|
||||||
import { IcyNetUser } from '../server/types/user';
|
import { IcyNetUser } from '../server/types/user';
|
||||||
import { Picker } from './picker';
|
import { Picker } from './picker';
|
||||||
@ -15,6 +15,7 @@ export class ViewCanvas {
|
|||||||
private _wrapper = $('<div class="canvas__wrapper">');
|
private _wrapper = $('<div class="canvas__wrapper">');
|
||||||
private _container = $('<div class="canvas__container">');
|
private _container = $('<div class="canvas__container">');
|
||||||
private _cursor = $('<div class="canvas__cursor">');
|
private _cursor = $('<div class="canvas__cursor">');
|
||||||
|
private _coods = $('<div class="canvas__coordinates">');
|
||||||
private _size = 1000;
|
private _size = 1000;
|
||||||
|
|
||||||
private _viewWidth = 0;
|
private _viewWidth = 0;
|
||||||
@ -22,7 +23,7 @@ export class ViewCanvas {
|
|||||||
|
|
||||||
private _posx = 0;
|
private _posx = 0;
|
||||||
private _posy = 0;
|
private _posy = 0;
|
||||||
private _zoom = this._size;
|
private _zoom = 1;
|
||||||
|
|
||||||
private _mousex = 0;
|
private _mousex = 0;
|
||||||
private _mousey = 0;
|
private _mousey = 0;
|
||||||
@ -42,10 +43,8 @@ export class ViewCanvas {
|
|||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
public moveCanvas(): void {
|
public moveCanvas(): void {
|
||||||
const pixelSize = this._size / this._zoom;
|
this._canvas.style.transform = `scale(${this._zoom})`;
|
||||||
this._canvas.style.top = `${this._posy}px`;
|
this._container.style.transform = `translate(${this._posx}px, ${this._posy}px)`;
|
||||||
this._canvas.style.left = `${this._posx}px`;
|
|
||||||
this._canvas.style.transform = `scale(${pixelSize})`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public center(): void {
|
public center(): void {
|
||||||
@ -60,13 +59,11 @@ export class ViewCanvas {
|
|||||||
|
|
||||||
public moveCursor(): void {
|
public moveCursor(): void {
|
||||||
// Zoom multiplier
|
// Zoom multiplier
|
||||||
const pixelSize = this._size / this._zoom;
|
|
||||||
// Apparent size of the canvas after scaling it
|
// Apparent size of the canvas after scaling it
|
||||||
const realSize = pixelSize * this._size;
|
const realSize = this._zoom * this._size;
|
||||||
// The difference between the real canvas size and apparent size
|
// The difference between the real canvas size and apparent size
|
||||||
const scale = this._size / 2 - realSize / 2;
|
const screenX = this._posx;
|
||||||
const screenX = this._posx + scale;
|
const screenY = this._posy;
|
||||||
const screenY = this._posy + scale;
|
|
||||||
|
|
||||||
// Position of the on-screen cursor, snapped
|
// Position of the on-screen cursor, snapped
|
||||||
// Relative to top left of screen
|
// Relative to top left of screen
|
||||||
@ -78,27 +75,34 @@ export class ViewCanvas {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Position of the cursor on the canvas
|
// Position of the cursor on the canvas
|
||||||
this._relcursorx = Math.floor((this._cursorx - screenX) / pixelSize);
|
this._relcursorx = Math.floor((this._cursorx - screenX) / this._zoom);
|
||||||
this._relcursory = Math.floor((this._cursory - screenY) / pixelSize);
|
this._relcursory = Math.floor((this._cursory - screenY) / this._zoom);
|
||||||
|
|
||||||
this._screencursorx = this._relcursorx * pixelSize + screenX;
|
this._screencursorx = this._relcursorx * this._zoom + screenX;
|
||||||
this._screencursory = this._relcursory * pixelSize + screenY;
|
this._screencursory = this._relcursory * this._zoom + screenY;
|
||||||
|
|
||||||
this._cursor.style.top = `${this._screencursory + pixelSize / 2}px`;
|
this._cursor.style.top = `${this._screencursory + this._zoom / 2}px`;
|
||||||
this._cursor.style.left = `${this._screencursorx + pixelSize / 2}px`;
|
this._cursor.style.left = `${this._screencursorx + this._zoom / 2}px`;
|
||||||
this._cursor.style.transform = `scale(${pixelSize})`;
|
this._cursor.style.transform = `scale(${this._zoom})`;
|
||||||
|
|
||||||
|
this._coods.innerText = `(${this._relcursorx}, ${
|
||||||
|
this._relcursory
|
||||||
|
}) ${this._zoom.toFixed(2)}x`;
|
||||||
|
|
||||||
|
this._updateURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
public initialize(): void {
|
public initialize(): void {
|
||||||
this.picker.initialize();
|
this.picker.initialize();
|
||||||
|
|
||||||
|
this._wrapper.append(this._coods);
|
||||||
this._container.append(this._canvas);
|
this._container.append(this._canvas);
|
||||||
this._container.append(this._cursor);
|
this._wrapper.append(this._cursor);
|
||||||
this._wrapper.append(this._container);
|
this._wrapper.append(this._container);
|
||||||
this._wrapper.append(this.picker.element);
|
this._wrapper.append(this.picker.element);
|
||||||
document.body.append(this._wrapper);
|
document.body.append(this._wrapper);
|
||||||
|
|
||||||
this._container.addEventListener('pointermove', (ev: MouseEvent) => {
|
this._wrapper.addEventListener('pointermove', (ev: MouseEvent) => {
|
||||||
const currentX = this._mousex;
|
const currentX = this._mousex;
|
||||||
const currentY = this._mousey;
|
const currentY = this._mousey;
|
||||||
|
|
||||||
@ -109,39 +113,69 @@ export class ViewCanvas {
|
|||||||
const offsetY = currentY - this._mousey;
|
const offsetY = currentY - this._mousey;
|
||||||
|
|
||||||
if (this._dragging) {
|
if (this._dragging) {
|
||||||
this._posx -= offsetX;
|
const realSize = this._zoom * this._size;
|
||||||
this._posy -= offsetY;
|
this._posx = clamp(
|
||||||
|
this._posx - offsetX,
|
||||||
|
this._cursorx - realSize,
|
||||||
|
this._cursorx,
|
||||||
|
);
|
||||||
|
this._posy = clamp(
|
||||||
|
this._posy - offsetY,
|
||||||
|
this._cursory - realSize,
|
||||||
|
this._cursory,
|
||||||
|
);
|
||||||
|
|
||||||
this.moveCanvas();
|
this.moveCanvas();
|
||||||
this.moveCursor();
|
this.moveCursor();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this._container.addEventListener('pointerdown', (ev: MouseEvent) => {
|
this._wrapper.addEventListener('mousedown', (ev: MouseEvent) => {
|
||||||
this._mousex = ev.clientX;
|
this._mousex = ev.clientX;
|
||||||
this._mousey = ev.clientY;
|
this._mousey = ev.clientY;
|
||||||
this._dragging = true;
|
this._dragging = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
this._container.addEventListener('pointerup', (ev: MouseEvent) => {
|
this._wrapper.addEventListener('mouseup', (ev: MouseEvent) => {
|
||||||
this._dragging = false;
|
this._dragging = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this._container.addEventListener('pointerleave', (ev: MouseEvent) => {
|
this._wrapper.addEventListener('touchstart', (ev: TouchEvent) => {
|
||||||
|
const touch = ev.touches[0] || ev.changedTouches[0];
|
||||||
|
this._mousex = touch.pageX;
|
||||||
|
this._mousey = touch.pageY;
|
||||||
|
this._dragging = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._wrapper.addEventListener('touchend', (ev: TouchEvent) => {
|
||||||
this._dragging = false;
|
this._dragging = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this._container.addEventListener('wheel', (ev: WheelEvent) => {
|
this._wrapper.addEventListener('pointerleave', (ev: MouseEvent) => {
|
||||||
|
this._dragging = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
this._wrapper.addEventListener('wheel', (ev: WheelEvent) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
this._mousex = ev.clientX;
|
this._mousex = ev.clientX;
|
||||||
this._mousey = ev.clientY;
|
this._mousey = ev.clientY;
|
||||||
|
|
||||||
const delta = Math.ceil(ev.deltaY / 2) * 2;
|
const scaleX = (ev.clientX - this._posx) / this._zoom;
|
||||||
this._zoom = clamp((this._zoom += delta), 8, this._size);
|
const scaleY = (ev.clientY - this._posy) / this._zoom;
|
||||||
|
|
||||||
|
ev.deltaY < 0 ? (this._zoom *= 1.2) : (this._zoom /= 1.2);
|
||||||
|
this._zoom = clamp(this._zoom, 1, 100);
|
||||||
|
|
||||||
|
this._posx = ev.clientX - scaleX * this._zoom;
|
||||||
|
this._posy = ev.clientY - scaleY * this._zoom;
|
||||||
|
|
||||||
|
const realSize = this._zoom * this._size;
|
||||||
|
this._posx = clamp(this._posx, this._cursorx - realSize, this._cursorx);
|
||||||
|
this._posy = clamp(this._posy, this._cursory - realSize, this._cursory);
|
||||||
|
|
||||||
this.moveCanvas();
|
|
||||||
this.moveCursor();
|
this.moveCursor();
|
||||||
|
this.moveCanvas();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.picker.registerOnPlace((color) => {
|
this.picker.registerOnPlace((color) => {
|
||||||
@ -155,8 +189,6 @@ export class ViewCanvas {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setView();
|
|
||||||
|
|
||||||
window.addEventListener('resize', () => this.setView());
|
window.addEventListener('resize', () => this.setView());
|
||||||
window.addEventListener('keyup', (ev: KeyboardEvent) => {
|
window.addEventListener('keyup', (ev: KeyboardEvent) => {
|
||||||
const numeral = parseInt(ev.key, 10);
|
const numeral = parseInt(ev.key, 10);
|
||||||
@ -198,7 +230,7 @@ export class ViewCanvas {
|
|||||||
public setView() {
|
public setView() {
|
||||||
this._viewWidth = document.body.clientWidth;
|
this._viewWidth = document.body.clientWidth;
|
||||||
this._viewHeight = document.body.clientHeight;
|
this._viewHeight = document.body.clientHeight;
|
||||||
this.center();
|
this._fromURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
public fill(size: number, canvas: number[]) {
|
public fill(size: number, canvas: number[]) {
|
||||||
@ -219,6 +251,8 @@ export class ViewCanvas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._ctx!.putImageData(data, 0, 0);
|
this._ctx!.putImageData(data, 0, 0);
|
||||||
|
|
||||||
|
this.setView();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPixel(x: number, y: number, pixel: number) {
|
public setPixel(x: number, y: number, pixel: number) {
|
||||||
@ -235,4 +269,53 @@ export class ViewCanvas {
|
|||||||
this._user = user;
|
this._user = user;
|
||||||
this.picker.setLoggedIn(user);
|
this.picker.setLoggedIn(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _updateURL = debounce(() => {
|
||||||
|
const urlelements = new URLSearchParams({
|
||||||
|
px: this._relcursorx.toString(),
|
||||||
|
py: this._relcursory.toString(),
|
||||||
|
z: this._zoom.toFixed(2),
|
||||||
|
});
|
||||||
|
window.history.replaceState(
|
||||||
|
null,
|
||||||
|
document.title,
|
||||||
|
`/?${urlelements.toString()}`,
|
||||||
|
);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
private _fromURL(): void {
|
||||||
|
const search = window.location.search.substring(1);
|
||||||
|
if (!search?.length) {
|
||||||
|
this.center();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const obj = new URLSearchParams(search);
|
||||||
|
const move = !!obj.get('px') || !!obj.get('py');
|
||||||
|
|
||||||
|
if (!move) {
|
||||||
|
return this.center();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.get('z')) {
|
||||||
|
this._zoom = clamp(parseFloat(obj.get('z')), 1, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.get('px')) {
|
||||||
|
this._relcursorx = clamp(parseInt(obj.get('px'), 10), 0, this._size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.get('py')) {
|
||||||
|
this._relcursory = clamp(parseInt(obj.get('py'), 10), 0, this._size);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cursorx = this._viewWidth / 2;
|
||||||
|
this._cursory = this._viewHeight / 2;
|
||||||
|
|
||||||
|
this._posx = -Math.ceil(this._relcursorx * this._zoom - this._cursorx) - 1;
|
||||||
|
this._posy = -Math.ceil(this._relcursory * this._zoom - this._cursory);
|
||||||
|
|
||||||
|
this.moveCanvas();
|
||||||
|
this.moveCursor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,21 +21,31 @@ body {
|
|||||||
image-rendering: pixelated; /* Awesome future-browsers */
|
image-rendering: pixelated; /* Awesome future-browsers */
|
||||||
-ms-interpolation-mode: nearest-neighbor; /* IE */
|
-ms-interpolation-mode: nearest-neighbor; /* IE */
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
transform-origin: center center;
|
transform-origin: left top;
|
||||||
|
|
||||||
&__wrapper {
|
&__wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: rgb(143, 143, 143);
|
background-color: rgb(143, 143, 143);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-grow: 1;
|
width: 0;
|
||||||
overflow: hidden;
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__coordinates {
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
left: 1rem;
|
||||||
|
background-color: rgba(221, 221, 221, 0.404);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 5px;
|
||||||
|
z-index: 1008;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__cursor {
|
&__cursor {
|
||||||
|
@ -1,3 +1,13 @@
|
|||||||
export function clamp(x: number, min: number, max: number): number {
|
export function clamp(x: number, min: number, max: number): number {
|
||||||
return Math.min(Math.max(x, min), max);
|
return Math.min(Math.max(x, min), max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function debounce(func: Function, timeout = 300) {
|
||||||
|
let timer: any;
|
||||||
|
return (...args: any[]) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
func.apply(this, args);
|
||||||
|
}, timeout);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user