drawing ink

This commit is contained in:
“chrisshank” 2024-09-24 21:33:45 -07:00
parent 9420e9e7bd
commit 487090cf58
3 changed files with 67 additions and 64 deletions

View File

@ -13,15 +13,14 @@
min-height: 100%;
position: relative;
margin: 0;
}
spatial-ink {
position: absolute;
inset: 0 0 0 0;
padding: 0;
}
</style>
</head>
<body>
<nav>
<button on:click="DRAW">Draw</button>
</nav>
<script type="module">
import { SpatialGeometry } from '../src/canvas/spatial-geometry.ts';
import { SpatialInk } from '../src/canvas/spatial-ink.ts';
@ -29,15 +28,50 @@
SpatialGeometry.register();
SpatialInk.register();
function trace() {
const drawButton = document.querySelector('button');
async function draw(e) {
if (e.target === drawButton) return;
const ink = document.createElement('spatial-ink');
document.body.append(ink);
ink.trace();
ink.style.cursor = 'var(--tracing-cursor, crosshair)';
ink.style.position = 'fixed';
ink.style.inset = '0 0 0 0';
ink.style.zIndex = 'calc(infinity)';
document.body.appendChild(ink);
const rect = await ink.draw(e);
const geometry = document.createElement('spatial-geometry');
geometry.x = rect.x;
geometry.y = rect.y;
geometry.height = rect.height;
geometry.width = rect.width;
ink.points = ink.points.map(([x, y, p]) => [x - rect.x, y - rect.y, p]);
ink.style.cursor = '';
ink.style.position = '';
ink.style.inset = '';
ink.style.zIndex = '';
geometry.appendChild(ink);
document.body.appendChild(geometry);
}
document.addEventListener('pointerdown', trace);
let isDrawing = false;
document.addEventListener('click', (e) => {
if (e.target !== drawButton) return;
trace();
isDrawing = !isDrawing;
if (isDrawing) {
document.addEventListener('pointerdown', draw);
drawButton.textContent = 'Drawing';
} else {
document.removeEventListener('pointerdown', draw);
drawButton.textContent = 'Draw';
}
});
</script>
</body>
</html>

View File

@ -29,32 +29,18 @@ styles.replaceSync(`
:host {
display: block;
position: absolute;
padding: 20px 10px 10px;
cursor: var(--fc-move, move);
content-visibility: auto;
}
::slotted(*) {
cursor: default;
}
:host > div {
position: relative;
width: 100%;
height: 100%;
}
:host > div > div {
width: 100%;
height: 100%;
overflow: hidden;
}
:host(:focus-within) > div {
:host(:focus-within) {
outline: solid 1px hsl(214, 84%, 56%);
}
:host(:hover) > div {
:host(:hover) {
outline: solid 2px hsl(214, 84%, 56%);
}
@ -155,17 +141,15 @@ export class SpatialGeometry extends HTMLElement {
// Maybe can add the first resize handler here, and lazily instantiate the rest when needed?
// I can see it becoming important at scale
shadowRoot.innerHTML = `
<div>
<button part="rotate"></button>
<button part="resize-nw"></button>
<button part="resize-ne"></button>
<button part="resize-se"></button>
<button part="resize-sw"></button>
<div><slot></slot></div>
</div>`;
<slot></slot>`;
}
#type: Shape = 'rectangle';
#type = (this.getAttribute('type') || 'rectangle') as Shape;
get type(): Shape {
return this.#type;
}
@ -175,7 +159,7 @@ export class SpatialGeometry extends HTMLElement {
}
#previousX = 0;
#x = 0;
#x = Number(this.getAttribute('x')) || 0;
get x(): number {
return this.#x;
}
@ -186,7 +170,7 @@ export class SpatialGeometry extends HTMLElement {
}
#previousY = 0;
#y = 0;
#y = Number(this.getAttribute('y')) || 0;
get y(): number {
return this.#y;
}
@ -197,7 +181,7 @@ export class SpatialGeometry extends HTMLElement {
}
#previousWidth = 0;
#width = 1;
#width = Number(this.getAttribute('width')) || 1;
get width(): number {
return this.#width;
}
@ -208,7 +192,7 @@ export class SpatialGeometry extends HTMLElement {
}
#previousHeight = 0;
#height = 1;
#height = Number(this.getAttribute('height')) || 1;
get height(): number {
return this.#height;
}
@ -219,7 +203,7 @@ export class SpatialGeometry extends HTMLElement {
}
#previousRotate = 0;
#rotate = 0;
#rotate = Number(this.getAttribute('rotate')) || 0;
get rotate(): number {
return this.#rotate;
}
@ -229,15 +213,6 @@ export class SpatialGeometry extends HTMLElement {
this.#requestUpdate('rotate');
}
connectedCallback() {
this.type = (this.getAttribute('type') || 'rectangle') as Shape;
this.x = Number(this.getAttribute('x')) || 0;
this.y = Number(this.getAttribute('y')) || 0;
this.height = Number(this.getAttribute('height')) || 0;
this.width = Number(this.getAttribute('width')) || 0;
this.rotate = Number(this.getAttribute('rotate')) || 0;
}
disconnectedCallback() {
cancelAnimationFrame(this.#rAFId);
}

View File

@ -8,18 +8,12 @@ export type Stroke = number[][];
// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/any-pointer
const styles = new CSSStyleSheet();
styles.replaceSync(`
svg {
:host, svg {
display: block;
height: 100%;
width: 100%;
touch-action: none;
}
:host(:state(tracing)) {
cursor: var(--tracing-cursor, crosshair);
position: fixed;
inset: 0 0 0 0;
z-index: calc(infinity);
}
`);
export class SpatialInk extends HTMLElement {
@ -117,12 +111,19 @@ export class SpatialInk extends HTMLElement {
this.#update();
}
#tracingPromise: PromiseWithResolvers<DOMRectReadOnly | undefined> | null = null;
// TODO: cancel trace?
trace() {
async draw(event: PointerEvent): Promise<DOMRectReadOnly | undefined> {
if (event.button !== 0 || event.ctrlKey) return;
this.points = [];
this.#internals.states.add('tracing');
this.addEventListener('pointerdown', this);
this.addPoint([event.pageX, event.pageY, event.pressure]);
this.addEventListener('lostpointercapture', this);
this.addEventListener('pointermove', this);
this.setPointerCapture(event.pointerId);
this.#tracingPromise = Promise.withResolvers<DOMRectReadOnly | undefined>();
return this.#tracingPromise.promise;
}
addPoint(point: Point) {
@ -132,23 +133,16 @@ export class SpatialInk extends HTMLElement {
handleEvent(event: PointerEvent) {
switch (event.type) {
case 'pointerdown': {
if (event.button !== 0 || event.ctrlKey) return;
this.addEventListener('pointermove', this);
this.setPointerCapture(event.pointerId);
this.addPoint([event.pageX, event.pageY, event.pressure]);
return;
}
case 'pointermove': {
this.addPoint([event.pageX, event.pageY, event.pressure]);
return;
}
case 'lostpointercapture': {
this.#internals.states.delete('tracing');
this.removeEventListener('pointermove', this);
this.removeEventListener('pointerdown', this);
this.removeEventListener('lostpointercapture', this);
this.#tracingPromise?.resolve(this.#path.getBoundingClientRect());
this.#tracingPromise = null;
return;
}
}