From cf521f94342704ce202a7e22ef11d04cc5d30c86 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Mon, 4 Apr 2022 17:51:36 +0300 Subject: [PATCH] keep the placer tag visible when moved only a little --- src/client/canvas.ts | 103 ++++++++++++++++++++++++++----------- src/client/scss/index.scss | 1 + 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/src/client/canvas.ts b/src/client/canvas.ts index 19de416..1f19290 100644 --- a/src/client/canvas.ts +++ b/src/client/canvas.ts @@ -12,14 +12,18 @@ export class ViewCanvas { private _placeFn?: (placement: Placement) => void; private _getPlacerFn?: (x: number, y: number) => Promise; private _canvas = $('') as HTMLCanvasElement; - private _ctx = this._canvas.getContext('2d'); private _wrapper = $('
'); private _zoomWrapper = $('
'); private _eventWrapper = $('
'); private _cursor = $('
'); private _coods = $('
'); private _userInfo = $(''); + + private _ctx = this._canvas.getContext('2d'); + private _size = 1000; + private _minZoom = 1; + private _maxZoom = 100; private _viewWidth = 0; private _viewHeight = 0; @@ -31,13 +35,12 @@ export class ViewCanvas { private _mousex = 0; private _mousey = 0; - private _relmousex = 0; - private _relmousey = 0; - private _cursorx = 0; private _cursory = 0; - private _relcursorx = 0; - private _relcursory = 0; + private _highlightedTileX = 0; + private _highlightedTileY = 0; + private _previousTileX = 0; + private _previousTileY = 0; private _screencursorx = 0; private _screencursory = 0; @@ -67,8 +70,6 @@ export class ViewCanvas { } public moveCursor(): void { - this._resetPlacerTag(); - // Apparent size of the canvas after scaling it const realSize = this._zoom * this._size; // The difference between the real canvas size and apparent size @@ -84,33 +85,43 @@ export class ViewCanvas { clamp(this._viewHeight / 2, screenY, screenY + realSize), ); + // Store previous canvas position + this._previousTileX = this._highlightedTileX; + this._previousTileY = this._highlightedTileY; + // Position of the cursor on the canvas - this._relcursorx = clamp( + this._highlightedTileX = clamp( Math.floor((this._cursorx - screenX) / this._zoom), 0, this._size - 1, ); - this._relcursory = clamp( + this._highlightedTileY = clamp( Math.floor((this._cursory - screenY) / this._zoom), 0, this._size - 1, ); - this._screencursorx = this._relcursorx * this._zoom + screenX; - this._screencursory = this._relcursory * this._zoom + screenY; + // Remove placer tag if the highlighted tile position has changed + const skipRequest = this._resetPlacerTag(); + + // Position the cursor on the screen so that it is snapped to the current tile + this._screencursorx = this._highlightedTileX * this._zoom + screenX; + this._screencursory = this._highlightedTileY * this._zoom + screenY; this._cursor.style.transform = `translate(${this._screencursorx}px, ${this._screencursory}px)`; this._cursor.style.width = `${this._zoom}px`; this._cursor.style.height = `${this._zoom}px`; - this._coods.innerText = `(${this._relcursorx}, ${ - this._relcursory + // Update coordinate display + this._coods.innerText = `(${this._highlightedTileX}, ${ + this._highlightedTileY }) ${this._zoom.toFixed(2)}x`; this._updateURL(); - if (this._zoom > 20) { - this._getPlacerAt(this._relcursorx, this._relcursory); + // Get placer tag and tile color info for picker button + if (this._zoom > 20 && !skipRequest) { + this._getPlacerAt(this._highlightedTileX, this._highlightedTileY); } } @@ -232,7 +243,7 @@ export class ViewCanvas { 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._zoom = clamp(this._zoom, this._minZoom, this._maxZoom); this._posx = ev.clientX - scaleX * this._zoom; this._posy = ev.clientY - scaleY * this._zoom; @@ -248,8 +259,8 @@ export class ViewCanvas { this.picker.registerOnPlace((color) => { if (this._placeFn) { this._placeFn({ - x: this._relcursorx, - y: this._relcursory, + x: this._highlightedTileX, + y: this._highlightedTileY, c: color, t: Date.now(), }); @@ -351,8 +362,8 @@ export class ViewCanvas { private _updateURL = debounce(() => { const urlelements = new URLSearchParams({ - px: this._relcursorx.toString(), - py: this._relcursory.toString(), + px: this._highlightedTileX.toString(), + py: this._highlightedTileY.toString(), z: this._zoom.toFixed(2), }); @@ -374,7 +385,7 @@ export class ViewCanvas { }); } }, - 1000, + 500, ); private _getPlacerAt(x: number, y: number) { @@ -391,17 +402,36 @@ export class ViewCanvas { this._placerTag = $('
'); this._placerTag.innerText = placer.user; this._placerTag.style.setProperty('--base-size', `${this._zoom / 2}px`); - this._placerTag.style.setProperty('--base-scale', `${this._zoom / 100}`); + this._placerTag.style.setProperty( + '--base-scale', + `${this._zoom / this._maxZoom}`, + ); this._cursor.append(this._placerTag); } - private _resetPlacerTag(): void { + private _resetPlacerTag(): boolean { + if ( + this._previousTileX === this._highlightedTileX && + this._previousTileY === this._highlightedTileY && + this._placerTag + ) { + this._placerTag.style.setProperty('--base-size', `${this._zoom / 2}px`); + this._placerTag.style.setProperty( + '--base-scale', + `${this._zoom / this._maxZoom}`, + ); + + return true; + } + this._placerRequestTime = 0; this.picker.setPickColor(null); if (this._placerTag) { this._cursor.removeChild(this._placerTag); this._placerTag = null; } + + return false; } private _resetPositionOrCenter(): void { @@ -419,22 +449,37 @@ export class ViewCanvas { } if (obj.get('z')) { - this._zoom = clamp(parseFloat(obj.get('z')), 1, 100); + this._zoom = clamp( + parseFloat(obj.get('z')), + this._minZoom, + this._maxZoom, + ); } if (obj.get('px')) { - this._relcursorx = clamp(parseInt(obj.get('px'), 10), 0, this._size - 1); + this._highlightedTileX = clamp( + parseInt(obj.get('px'), 10), + 0, + this._size - 1, + ); } if (obj.get('py')) { - this._relcursory = clamp(parseInt(obj.get('py'), 10), 0, this._size - 1); + this._highlightedTileY = clamp( + parseInt(obj.get('py'), 10), + 0, + this._size - 1, + ); } 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._posx = + -Math.ceil(this._highlightedTileX * this._zoom - this._cursorx) - 1; + this._posy = -Math.ceil( + this._highlightedTileY * this._zoom - this._cursory, + ); this.moveCanvas(); this.moveCursor(); diff --git a/src/client/scss/index.scss b/src/client/scss/index.scss index 64a5cff..b309d12 100644 --- a/src/client/scss/index.scss +++ b/src/client/scss/index.scss @@ -86,6 +86,7 @@ body { z-index: 1002; width: 1px; height: 1px; + user-select: none; background: url('data:image/png;base64,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dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6R0lNUD0iaHR0cDovL3d3dy5naW1wLm9yZy94bXAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgIHhtcE1NOkRvY3VtZW50SUQ9ImdpbXA6ZG9jaWQ6Z2ltcDo1MGFkNWM1Mi0xMWZkLTQ2ZDktOWQzMy0wODBhZDBlZmRkOGIiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NjQxMTYxOWUtYTRhZC00OTQzLWE5MDEtMTMyODk4MjFkYjMyIgogICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6Mzc0YzA0MGItMjNlMy00ZDllLWI0ZWQtYWE5ZGJkMzVlZTBkIgogICBkYzpGb3JtYXQ9ImltYWdlL3BuZyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQbGF0Zm9ybT0iTGludXgiCiAgIEdJTVA6VGltZVN0YW1wPSIxNjQ4ODkwNjQ1MTM0NTU2IgogICBHSU1QOlZlcnNpb249IjIuMTAuMzAiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHhtcDpDcmVhdG9yVG9vbD0iR0lNUCAyLjEwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MDBiNzUzOTItOTU0NS00YzY3LTlhN2MtNGJjMmU5ZGIxM2E4IgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKExpbnV4KSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMi0wNC0wMlQxMjoxMDo0NSswMzowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz7Mc7bvAAAABmJLR0QAVACzAPRJGKQ4AAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5gQCCQot46GzBAAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAFdSURBVHja7d2xjYMwAEBROKVjC1p6CobwLB7FszAERXqXsAV1rroW54RQgvzeAAjZX0bYiWjHcXw1X+z5fLbNjYUQvnp8fxqqJgABIAAEgAAQALVpS/sA0zRdegMppbbmCYgxXrpPsG2bFQABIAAEgAAQAALgz2Pfd+/xH3R2/Er7COu6WgEQAAJAAAgAASAABIAAEIAADIEAEAACQADUpx2G4fA8OefsvP/GSvNrBfAIQAAIAAEgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADApdoQwuF35eZ59t3AGxvH0XcDEQACQAAIAAEgAN7eB+j7/vACKSX7BB8UYzycv2VZrAAIAAEgAASAABAAAkAACEAAhkAACAABIADq8zh7gdJ5dEntvyc4O35WAASAABAAAkAACIB/KP4voGRd10tvMOd8632CYRgufc/vus4KgAAQAAJAAAgAAfCuX64yPUr2kCHkAAAAAElFTkSuQmCC'); background-size: contain; background-repeat: no-repeat;