@dartbot/dartboard
Version:
Dartboard implemented as a vanilla web component
185 lines • 8.03 kB
JavaScript
var _Dartboard_canvas, _Dartboard_hits, _Dartboard_zoom, _Dartboard_fit, _Dartboard_centerPoint, _Dartboard_shadow, _Dartboard_board;
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
import { createTheme, Token } from './theme';
import { translateCoords, debounce, getPolar } from './utils';
import { createBoard, getRingIndexFromPoint, getSectorIndexFromPoint, } from './draw-board/board';
import { render } from './draw-board/render';
const RESIZE_DEBOUNCE_MS = 100;
const DEFAULT_ZOOM = 0;
export class Dartboard extends HTMLElement {
get board() {
return __classPrivateFieldGet(this, _Dartboard_board, "f");
}
set board(value) {
__classPrivateFieldSet(this, _Dartboard_board, value, "f");
this.render();
}
get zoom() {
return __classPrivateFieldGet(this, _Dartboard_zoom, "f");
}
set zoom(value) {
__classPrivateFieldSet(this, _Dartboard_zoom, value, "f");
this.render();
}
get centerPoint() {
return __classPrivateFieldGet(this, _Dartboard_centerPoint, "f");
}
set centerPoint(value) {
__classPrivateFieldSet(this, _Dartboard_centerPoint, value, "f");
this.render();
}
get hits() {
return __classPrivateFieldGet(this, _Dartboard_hits, "f");
}
set hits(value) {
__classPrivateFieldSet(this, _Dartboard_hits, value, "f");
this.render();
}
get fit() {
return __classPrivateFieldGet(this, _Dartboard_fit, "f");
}
set fit(value) {
__classPrivateFieldSet(this, _Dartboard_fit, value, "f");
this.render();
}
static get observedAttributes() {
return ['zoom', 'center-angle', 'center-radius', 'fit'];
}
constructor() {
super();
_Dartboard_canvas.set(this, void 0);
_Dartboard_hits.set(this, []);
_Dartboard_zoom.set(this, DEFAULT_ZOOM);
_Dartboard_fit.set(this, 'contain');
_Dartboard_centerPoint.set(this, { radius: 0, angle: 0 });
_Dartboard_shadow.set(this, void 0);
_Dartboard_board.set(this, void 0);
__classPrivateFieldSet(this, _Dartboard_board, createBoard(), "f");
__classPrivateFieldSet(this, _Dartboard_shadow, this.attachShadow({ mode: 'open' }), "f");
__classPrivateFieldGet(this, _Dartboard_shadow, "f").innerHTML = `
<style>
:host {
display: flex;
width: 100%;
aspect-ratio: 1 / 1;
box-sizing: border-box;
user-select: none;
}
canvas {
width: 100%;
height: 100%;
background-color: var(${Token.canvasBg}, transparent);
}
</style>
<canvas></canvas>
`;
__classPrivateFieldSet(this, _Dartboard_canvas, __classPrivateFieldGet(this, _Dartboard_shadow, "f").querySelector('canvas'), "f");
__classPrivateFieldGet(this, _Dartboard_canvas, "f").addEventListener('click', this);
const resizeObserver = new ResizeObserver(debounce((entries) => {
const entry = entries.find(e => e.target === this);
const box = entry.devicePixelContentBoxSize?.[0];
const boxC = entry.contentBoxSize[0];
const physical = (n) => Math.round(n * devicePixelRatio);
__classPrivateFieldGet(this, _Dartboard_canvas, "f").width = box?.inlineSize ?? physical(boxC.inlineSize);
__classPrivateFieldGet(this, _Dartboard_canvas, "f").height = box?.blockSize ?? physical(boxC.blockSize);
this.render();
}, RESIZE_DEBOUNCE_MS, { leading: true, trailing: true }));
resizeObserver.observe(this, { box: 'content-box' });
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue === newValue) {
return;
}
if (name === 'zoom') {
this.zoom = parseFloat(newValue);
}
else if (name === 'center-angle') {
const angle = parseFloat(newValue);
const { radius } = __classPrivateFieldGet(this, _Dartboard_centerPoint, "f");
this.centerPoint = { radius, angle };
}
else if (name === 'center-radius') {
const radius = parseFloat(newValue);
const { angle } = __classPrivateFieldGet(this, _Dartboard_centerPoint, "f");
this.centerPoint = { radius, angle };
}
else if (name === 'fit') {
this.fit = newValue;
}
}
handleEvent(event) {
switch (event.type) {
case 'click':
case 'pointerdown':
case 'pointerup': {
const { offsetX, offsetY } = event;
const { point, polar, sector, ring } = this.translatePoint(offsetX, offsetY);
const name = `dartboard-${event.type}`;
const detail = { event, point, polar, sector, ring };
const e = new CustomEvent(name, {
detail,
bubbles: true,
cancelable: true,
});
this.dispatchEvent(e);
break;
}
default:
break;
}
}
render() {
const ctx = __classPrivateFieldGet(this, _Dartboard_canvas, "f").getContext('2d');
if (ctx == null) {
return;
}
const zoom = __classPrivateFieldGet(this, _Dartboard_zoom, "f");
const center = __classPrivateFieldGet(this, _Dartboard_centerPoint, "f");
const fit = __classPrivateFieldGet(this, _Dartboard_fit, "f");
const style = getComputedStyle(this);
const theme = createTheme(style);
render(__classPrivateFieldGet(this, _Dartboard_board, "f"), zoom, center, fit, __classPrivateFieldGet(this, _Dartboard_hits, "f"), theme, ctx);
}
/**
* Translates a point from the coordinate system of the canvas.
* The point is adjusted so that 0,0 is the center of the board with
* the y-axis pointing up. The units are translated from pixels to
* millimeters relative to the board radius.
* @param x - X coordinate in canvas space
* @param y - Y coordinate in canvas space
*/
translatePoint(x, y) {
const { offsetWidth: w, offsetHeight: h } = __classPrivateFieldGet(this, _Dartboard_canvas, "f");
const { radius } = __classPrivateFieldGet(this, _Dartboard_board, "f");
const zoom = __classPrivateFieldGet(this, _Dartboard_zoom, "f");
const center = __classPrivateFieldGet(this, _Dartboard_centerPoint, "f");
const fit = __classPrivateFieldGet(this, _Dartboard_fit, "f");
const point = translateCoords(w, h, zoom, center, radius, fit, x, y);
const polar = getPolar(point.x, point.y);
const sector = getSectorIndexFromPoint(__classPrivateFieldGet(this, _Dartboard_board, "f"), polar);
const ring = getRingIndexFromPoint(__classPrivateFieldGet(this, _Dartboard_board, "f"), polar);
return { point, polar, sector, ring };
}
/**
* Return the image data encoded as a data URL.
* @param type The image format. Default is `image/png`.
* @param quality The image quality. Default is `1.0`.
*/
toDataURL(type, quality) {
return __classPrivateFieldGet(this, _Dartboard_canvas, "f").toDataURL(type, quality);
}
/**
* Return the image data as a Blob.
* @param type The image format. Default is `image/png`.
* @param quality The image quality. Default is `1.0`.
*/
toBlob(type, quality) {
return new Promise(resolve => {
__classPrivateFieldGet(this, _Dartboard_canvas, "f").toBlob(blob => {
resolve(blob);
}, type, quality);
});
}
}
_Dartboard_canvas = new WeakMap(), _Dartboard_hits = new WeakMap(), _Dartboard_zoom = new WeakMap(), _Dartboard_fit = new WeakMap(), _Dartboard_centerPoint = new WeakMap(), _Dartboard_shadow = new WeakMap(), _Dartboard_board = new WeakMap();
//# sourceMappingURL=Dartboard.js.map