pts 0.12.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -80
- package/dist/index.d.mts +6208 -1254
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +6208 -1254
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9314 -10680
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +9266 -10611
- package/dist/index.mjs.map +1 -0
- package/dist/pts.js +9446 -10777
- package/dist/pts.js.map +1 -0
- package/dist/pts.min.js +3 -5
- package/dist/pts.min.js.map +1 -0
- package/package.json +91 -31
- package/src/Canvas.ts +1642 -0
- package/src/Color.ts +1109 -0
- package/src/Create.ts +1547 -0
- package/src/Dom.ts +940 -0
- package/src/Form.ts +312 -0
- package/src/Image.ts +722 -0
- package/src/LinearAlgebra.ts +530 -0
- package/src/Num.ts +1091 -0
- package/src/Op.ts +2127 -0
- package/src/Physics.ts +1233 -0
- package/src/Play.ts +861 -0
- package/src/Pt.ts +1303 -0
- package/src/Space.ts +898 -0
- package/src/Svg.ts +1573 -0
- package/src/Types.ts +301 -0
- package/src/Typography.ts +228 -0
- package/src/UI.ts +757 -0
- package/src/Util.ts +454 -0
- package/src/_module.ts +18 -0
- package/src/_script.ts +94 -0
- package/src/_triangulate.ts +884 -0
- package/src/uheprng.ts +153 -0
package/src/Dom.ts
ADDED
|
@@ -0,0 +1,940 @@
|
|
|
1
|
+
/*! Pts.js is licensed under Apache License 2.0. Copyright © 2017-current William Ngan and contributors. (https://github.com/williamngan/pts) */
|
|
2
|
+
|
|
3
|
+
import { MultiTouchSpace } from "./Space";
|
|
4
|
+
import { type Form, VisualForm, Font } from "./Form";
|
|
5
|
+
import { Util } from "./Util";
|
|
6
|
+
import { Pt, Bound } from "./Pt";
|
|
7
|
+
import {
|
|
8
|
+
type PtLike,
|
|
9
|
+
type GroupLike,
|
|
10
|
+
type IPlayer,
|
|
11
|
+
type DOMFormContext,
|
|
12
|
+
type PtLikeIterable,
|
|
13
|
+
} from "./Types";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* DOMSpace hosts a Space in a DOM element. It is the subclassing entry point for building
|
|
17
|
+
* custom element-based spaces; usually its subclass [`SVGSpace`](#link) should be used instead.
|
|
18
|
+
* Learn more about spaces in [this guide](../guide/Space-0500.html).
|
|
19
|
+
*
|
|
20
|
+
* When using a Space inside a component framework, create it on mount and call
|
|
21
|
+
* [`DOMSpace.dispose`](#link) on unmount so listeners and the animation loop are released.
|
|
22
|
+
* For example, in React:
|
|
23
|
+
* ```
|
|
24
|
+
* useEffect(() => {
|
|
25
|
+
* const space = new SVGSpace(ref.current).setup({ resize: true });
|
|
26
|
+
* space.add(...).play();
|
|
27
|
+
* return () => { space.dispose(); };
|
|
28
|
+
* }, []);
|
|
29
|
+
* ```
|
|
30
|
+
* Dispose is idempotent, and a new Space can be mounted on the same element afterwards
|
|
31
|
+
* (as happens under React's StrictMode).
|
|
32
|
+
*/
|
|
33
|
+
export class DOMSpace extends MultiTouchSpace {
|
|
34
|
+
protected _canvas: HTMLElement | SVGElement;
|
|
35
|
+
protected _container: Element;
|
|
36
|
+
|
|
37
|
+
id: string = "domspace";
|
|
38
|
+
protected _autoResize = true;
|
|
39
|
+
protected _bgcolor = "#e1e9f0";
|
|
40
|
+
protected _css = {};
|
|
41
|
+
private _domDisposed = false;
|
|
42
|
+
private _readyTimer: ReturnType<typeof setTimeout>;
|
|
43
|
+
// elements this space created for a missing target, removed on dispose
|
|
44
|
+
private _ownsContainer = false;
|
|
45
|
+
|
|
46
|
+
// one stable bound reference, so removeEventListener actually removes the
|
|
47
|
+
// listener that addEventListener added (and double-adds dedupe)
|
|
48
|
+
private readonly _resizeHandlerBound = this._resizeHandler.bind(this);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a DOMSpace for HTML DOM elements
|
|
52
|
+
* @param elem Specify an element by its "id" attribute as string, or by the element object itself. If left empty, a `<div id="pt_container"><div id="pt" /></div>` will be added to DOM; a missing id is created the same way. Use css to customize its appearance if needed.
|
|
53
|
+
* @param callback an optional callback `function(boundingBox, spaceElement)` to be called when element is appended and ready. Alternatively, a "ready" event will also be fired from the element when it's appended, which can be traced with `spaceInstance.element.addEventListener("ready")`
|
|
54
|
+
* @example `new DOMSpace( "#myElementID" )`
|
|
55
|
+
*/
|
|
56
|
+
constructor(
|
|
57
|
+
elem: string | Element | null = "pt",
|
|
58
|
+
callback?: (bound: Bound, elem: Element) => void,
|
|
59
|
+
) {
|
|
60
|
+
super();
|
|
61
|
+
this.refresh(false); // DOM elements persist unless a renderer opts into refreshing.
|
|
62
|
+
|
|
63
|
+
let _selector: Element | null = null;
|
|
64
|
+
this.id = "pts";
|
|
65
|
+
|
|
66
|
+
// check element or element id string
|
|
67
|
+
if (elem instanceof Element) {
|
|
68
|
+
_selector = elem;
|
|
69
|
+
this.id = "pts_existing_space";
|
|
70
|
+
} else {
|
|
71
|
+
const target = elem || "pt";
|
|
72
|
+
const id = target[0] === "#" || target[0] === "." ? target : "#" + target;
|
|
73
|
+
_selector = document.querySelector(id);
|
|
74
|
+
this.id = id.substr(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// if selector is not defined, create the elements
|
|
78
|
+
if (!_selector) {
|
|
79
|
+
this._container = DOMSpace.createElement("div", this.id + "_container");
|
|
80
|
+
this._canvas = this._createDefaultElement(this._container, this.id);
|
|
81
|
+
document.body.appendChild(this._container);
|
|
82
|
+
this._ownsContainer = true;
|
|
83
|
+
} else {
|
|
84
|
+
this._canvas = _selector as HTMLElement;
|
|
85
|
+
this._container = _selector.parentElement!;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// no mutation observer, so we set a timeout for ready event
|
|
89
|
+
this._readyTimer = setTimeout(this._ready.bind(this, callback), 50);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create the drawing element for a target that does not exist yet, inside the created container.
|
|
94
|
+
* Subclasses that draw into a specific element type override this.
|
|
95
|
+
* @param container the created container
|
|
96
|
+
* @param id the id for the new element
|
|
97
|
+
*/
|
|
98
|
+
protected _createDefaultElement(
|
|
99
|
+
container: Element,
|
|
100
|
+
id: string,
|
|
101
|
+
): HTMLElement | SVGElement {
|
|
102
|
+
return DOMSpace.createElement("div", id, container) as HTMLElement;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Helper function to create a DOM element.
|
|
107
|
+
* @param elem element tag name
|
|
108
|
+
* @param id element id attribute
|
|
109
|
+
* @param appendTo Optional, if specified, the created element will be appended to this element
|
|
110
|
+
*/
|
|
111
|
+
static createElement(
|
|
112
|
+
elem: string = "div",
|
|
113
|
+
id: string,
|
|
114
|
+
appendTo?: Element,
|
|
115
|
+
): Element {
|
|
116
|
+
let d = document.createElement(elem);
|
|
117
|
+
if (id) d.setAttribute("id", id);
|
|
118
|
+
if (appendTo && appendTo.appendChild) appendTo.appendChild(d);
|
|
119
|
+
return d;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Handle callbacks after element is mounted in DOM.
|
|
124
|
+
* @param callback
|
|
125
|
+
*/
|
|
126
|
+
private _ready(callback?: (bound: Bound, elem: Element) => void) {
|
|
127
|
+
if (this._domDisposed) return;
|
|
128
|
+
if (!this._container)
|
|
129
|
+
throw new Error(`Cannot initiate #${this.id} element`);
|
|
130
|
+
|
|
131
|
+
this._isReady = true;
|
|
132
|
+
|
|
133
|
+
this._resizeHandler(null);
|
|
134
|
+
this.clear(this._bgcolor);
|
|
135
|
+
this._canvas.dispatchEvent(new Event("ready"));
|
|
136
|
+
|
|
137
|
+
for (let k in this.players) {
|
|
138
|
+
if (this.players.hasOwnProperty(k)) {
|
|
139
|
+
if (this.players[k].start)
|
|
140
|
+
this.players[k].start(this.bound.clone(), this);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this._pointer = this.center;
|
|
145
|
+
|
|
146
|
+
if (callback) callback(this.bound, this._canvas);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Set up various options for DOMSpace. This is usually set during instantiation, eg `new DOMSpace(...).setup( {opt} )`.
|
|
151
|
+
* @param opt an object with these optional properties: **bgcolor** is a hex or rgba string to set initial background color of the canvas, or use `false` or "transparent" to set a transparent background; **resize** a boolean to set whether `<canvas>` size should auto resize to match its container's size, which can also be set using `autoSize()`.
|
|
152
|
+
* @example `space.setup({ bgcolor: "#f00", resize: true })`
|
|
153
|
+
*/
|
|
154
|
+
setup(opt: { bgcolor?: string; resize?: boolean }): this {
|
|
155
|
+
if (opt.bgcolor) {
|
|
156
|
+
this._bgcolor = opt.bgcolor;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.autoResize = opt.resize != undefined ? opt.resize : false;
|
|
160
|
+
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Not implemented. See SVGSpace and HTMLSpace for implementation.
|
|
166
|
+
*/
|
|
167
|
+
getForm(): Form {
|
|
168
|
+
return null as unknown as Form;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Set whether the canvas element should resize when its container is resized.
|
|
173
|
+
* @param auto a boolean value indicating if auto size is set
|
|
174
|
+
*/
|
|
175
|
+
set autoResize(auto: boolean) {
|
|
176
|
+
this._autoResize = auto;
|
|
177
|
+
if (auto) {
|
|
178
|
+
window.addEventListener("resize", this._resizeHandlerBound);
|
|
179
|
+
} else {
|
|
180
|
+
delete (this._css as any)["width"];
|
|
181
|
+
delete (this._css as any)["height"];
|
|
182
|
+
window.removeEventListener("resize", this._resizeHandlerBound);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
get autoResize(): boolean {
|
|
186
|
+
return this._autoResize;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* This overrides Space's `resize` function. It's used as a callback function for window's resize event and not usually called directly. You can keep track of resize events with `resize: (bound, evt)` callback in your player objects (See [`Space.add`](#link) function).
|
|
191
|
+
* @param b a Bound object to resize to
|
|
192
|
+
* @param evt Optionally pass a resize event
|
|
193
|
+
*/
|
|
194
|
+
resize(b: Bound, evt?: Event | null): this {
|
|
195
|
+
this.bound = b;
|
|
196
|
+
this.styles({ width: `${b.width}px`, height: `${b.height}px` }, true);
|
|
197
|
+
|
|
198
|
+
for (let k in this.players) {
|
|
199
|
+
if (this.players.hasOwnProperty(k)) {
|
|
200
|
+
let p = this.players[k];
|
|
201
|
+
if (p.resize) p.resize(this.bound, evt);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Window resize handling.
|
|
210
|
+
* @param evt
|
|
211
|
+
*/
|
|
212
|
+
protected _resizeHandler(evt: Event | null) {
|
|
213
|
+
let b = Bound.fromBoundingRect(this._container.getBoundingClientRect());
|
|
214
|
+
|
|
215
|
+
if (this._autoResize) {
|
|
216
|
+
this.styles({ width: "100%", height: "100%" }, true);
|
|
217
|
+
} else {
|
|
218
|
+
this.styles({ width: `${b.width}px`, height: `${b.height}px` }, true);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.resize(b, evt);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get this DOM element.
|
|
226
|
+
*/
|
|
227
|
+
get element(): Element {
|
|
228
|
+
return this._canvas;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get the parent DOM element that contains this DOM element.
|
|
233
|
+
*/
|
|
234
|
+
get parent(): Element {
|
|
235
|
+
return this._container;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* A property to indicate if the Space is ready.
|
|
240
|
+
*/
|
|
241
|
+
get ready(): boolean {
|
|
242
|
+
return this._isReady;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Clear the element's contents, and optionally set a new background color. This overrides Space's `clear` function.
|
|
247
|
+
* @param bg Optionally specify a custom background color in hex or rgba string, or "transparent". If not defined, it will use its `bgcolor` property as background color to clear the canvas.
|
|
248
|
+
*/
|
|
249
|
+
clear(bg?: string): this {
|
|
250
|
+
if (bg) this.background = bg;
|
|
251
|
+
this._canvas.innerHTML = "";
|
|
252
|
+
return this;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Set a background color on the container element.
|
|
257
|
+
@param bg background color as hex or rgba string
|
|
258
|
+
*/
|
|
259
|
+
set background(bg: string) {
|
|
260
|
+
this._bgcolor = bg;
|
|
261
|
+
(this._container as HTMLElement).style.backgroundColor = this._bgcolor;
|
|
262
|
+
}
|
|
263
|
+
get background(): string {
|
|
264
|
+
return this._bgcolor;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Add or update a style definition, and optionally update that style in the Element.
|
|
269
|
+
* @param key style name
|
|
270
|
+
* @param val style value
|
|
271
|
+
* @param update a boolean to update the element's style immediately if set to `true`. Default is `false`.
|
|
272
|
+
*/
|
|
273
|
+
style(key: string, val: string, update: boolean = false): this {
|
|
274
|
+
(this._css as any)[key] = val;
|
|
275
|
+
if (update) (this._canvas.style as any)[key] = val;
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Add of update a list of style definitions, and optionally update those styles in the Element.
|
|
281
|
+
* @param styles a key-value objects of style definitions
|
|
282
|
+
* @param update a boolean to update the element's style immediately if set to `true`. Default is `false`.
|
|
283
|
+
* @return this
|
|
284
|
+
*/
|
|
285
|
+
styles(styles: Record<string, string>, update: boolean = false): this {
|
|
286
|
+
for (let k in styles) {
|
|
287
|
+
if (styles.hasOwnProperty(k)) this.style(k, (styles as any)[k], update);
|
|
288
|
+
}
|
|
289
|
+
return this;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* A static helper function to add or update Element attributes.
|
|
294
|
+
* @param elem Element to update
|
|
295
|
+
* @param data an object with key-value pairs
|
|
296
|
+
* @returns this DOM element
|
|
297
|
+
*/
|
|
298
|
+
static setAttr(elem: Element, data: Record<string, any>): Element {
|
|
299
|
+
for (let k in data) {
|
|
300
|
+
if (data.hasOwnProperty(k)) {
|
|
301
|
+
elem.setAttribute(k, (data as any)[k]);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return elem;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* A static helper function to compose an inline style string from a object of styles.
|
|
309
|
+
* @param data an object with key-value pairs
|
|
310
|
+
* @example `DOMSpace.getInlineStyles( {width: "100px", "font-size": "10px"} )`
|
|
311
|
+
*/
|
|
312
|
+
static getInlineStyles(data: Record<string, any>): string {
|
|
313
|
+
let str = "";
|
|
314
|
+
for (let k in data) {
|
|
315
|
+
if (data.hasOwnProperty(k)) {
|
|
316
|
+
if ((data as any)[k]) str += `${k}: ${(data as any)[k]}; `;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return str;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Dispose of browser resources held by this space and remove all players. Call this before
|
|
324
|
+
* unmounting the DOM, eg in a framework component's unmount/cleanup callback. Dispose is
|
|
325
|
+
* idempotent, and a new Space can be created on the same element afterwards.
|
|
326
|
+
*/
|
|
327
|
+
dispose(): this {
|
|
328
|
+
if (this._domDisposed) return this;
|
|
329
|
+
this._domDisposed = true;
|
|
330
|
+
clearTimeout(this._readyTimer);
|
|
331
|
+
|
|
332
|
+
this.autoResize = false; // removes the window resize listener
|
|
333
|
+
this._unbindAll(); // removes mouse/touch listeners on the element
|
|
334
|
+
this._cancelAnimation(); // cancels the animation frame immediately
|
|
335
|
+
|
|
336
|
+
// Remove the players without the subclass's DOM-clearing `removeAll`:
|
|
337
|
+
// disposing must release resources, never destroy a user-owned host
|
|
338
|
+
// element's contents (a re-mount on the same element must work).
|
|
339
|
+
MultiTouchSpace.prototype.removeAll.call(this);
|
|
340
|
+
this._isReady = false;
|
|
341
|
+
// elements created for a missing target are this space's own
|
|
342
|
+
if (this._ownsContainer) this._container.remove();
|
|
343
|
+
|
|
344
|
+
return this;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @deprecated HTML rendering is deprecated and will be removed in a future major version. Use [`SVGSpace`](#link) for DOM-based output instead — it shares the supported subset of the [`CanvasForm`](#link) drawing API.
|
|
350
|
+
* **[Experimental]** HTMLSpace is a subclass of DOMSpace that works with HTML elements. See [a demo here](https://ptsjs.org/demo/?name=htmlform.scope).
|
|
351
|
+
*/
|
|
352
|
+
export class HTMLSpace extends DOMSpace {
|
|
353
|
+
/**
|
|
354
|
+
* Get a new `HTMLForm` which provides visualization functions in html elements.
|
|
355
|
+
* @see `HTMLForm`
|
|
356
|
+
*/
|
|
357
|
+
getForm(): Form {
|
|
358
|
+
return new HTMLForm(this);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* A static function to add a DOM element inside a node. Usually you don't need to use this directly. See methods in [`HTMLForm`](#link) instead.
|
|
363
|
+
* @param parent the parent element, or `null` to use current `<svg>` as parent.
|
|
364
|
+
* @param name a string of element name, such as `rect` or `circle`
|
|
365
|
+
* @param id id attribute of the new element
|
|
366
|
+
* @param autoClass add a class based on the id (from char 0 to index of "-"). Default is true.
|
|
367
|
+
*/
|
|
368
|
+
static htmlElement(
|
|
369
|
+
parent: Element | null | undefined,
|
|
370
|
+
name: string,
|
|
371
|
+
id?: string,
|
|
372
|
+
autoClass: boolean = true,
|
|
373
|
+
): HTMLElement {
|
|
374
|
+
if (!parent || !parent.appendChild)
|
|
375
|
+
throw new Error("parent is not a valid DOM element");
|
|
376
|
+
|
|
377
|
+
// O(1) id lookup, then verify it's inside the parent so a same-id
|
|
378
|
+
// element elsewhere in the document is never silently adopted
|
|
379
|
+
let elem: Element | null = document.getElementById(id!);
|
|
380
|
+
if (elem && !parent.contains(elem)) elem = null;
|
|
381
|
+
if (!elem) {
|
|
382
|
+
elem = document.createElement(name);
|
|
383
|
+
elem.setAttribute("id", id!);
|
|
384
|
+
|
|
385
|
+
if (autoClass)
|
|
386
|
+
elem.setAttribute("class", id!.substring(0, id!.indexOf("-")));
|
|
387
|
+
parent.appendChild(elem);
|
|
388
|
+
}
|
|
389
|
+
return elem as HTMLElement;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Remove an item from this space.
|
|
394
|
+
* @param player a player item with an auto-assigned `animateID` property
|
|
395
|
+
*/
|
|
396
|
+
remove(player: IPlayer): this {
|
|
397
|
+
let temp = this._container.querySelectorAll("." + HTMLForm.scopeID(player));
|
|
398
|
+
|
|
399
|
+
temp.forEach((el: Element) => {
|
|
400
|
+
el.parentNode!.removeChild(el);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
return super.remove(player);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Remove all items from this space. This clears the contents of the space's
|
|
408
|
+
* element but never touches its container.
|
|
409
|
+
*/
|
|
410
|
+
removeAll(): this {
|
|
411
|
+
this._canvas.innerHTML = "";
|
|
412
|
+
return super.removeAll();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
let _htmlFormGroupID = 0;
|
|
417
|
+
let _htmlFormDomID = 0;
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* @deprecated HTML rendering is deprecated and will be removed in a future major version. Use [`SVGForm`](#link) for DOM-based output instead — it shares the complete [`CanvasForm`](#link) drawing API.
|
|
421
|
+
* **[Experimental]** HTMLForm is an implementation of abstract class [`VisualForm`](#link). It provide methods to express Pts on [`HTMLSpace`](#link).
|
|
422
|
+
*/
|
|
423
|
+
export class HTMLForm extends VisualForm {
|
|
424
|
+
/**
|
|
425
|
+
* store common styles so that they can be restored to canvas context when using multiple forms. See `reset()`.
|
|
426
|
+
*/
|
|
427
|
+
protected _style = {
|
|
428
|
+
filled: true,
|
|
429
|
+
stroked: true,
|
|
430
|
+
background: "#f03",
|
|
431
|
+
"border-color": "#fff",
|
|
432
|
+
color: "#000",
|
|
433
|
+
"border-width": "1px",
|
|
434
|
+
"border-radius": "0",
|
|
435
|
+
"border-style": "solid",
|
|
436
|
+
opacity: 1,
|
|
437
|
+
position: "absolute",
|
|
438
|
+
top: 0,
|
|
439
|
+
left: 0,
|
|
440
|
+
width: 0,
|
|
441
|
+
height: 0,
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
protected _ctx: DOMFormContext = {
|
|
445
|
+
group: null,
|
|
446
|
+
groupID: "pts",
|
|
447
|
+
groupCount: 0,
|
|
448
|
+
currentID: "pts0",
|
|
449
|
+
currentClass: "",
|
|
450
|
+
style: {},
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// mutable statics are stored at module level and exposed through accessors so
|
|
454
|
+
// no post-class assignment is emitted (which would defeat tree-shaking)
|
|
455
|
+
static get groupID(): number {
|
|
456
|
+
return _htmlFormGroupID;
|
|
457
|
+
}
|
|
458
|
+
static set groupID(n: number) {
|
|
459
|
+
_htmlFormGroupID = n;
|
|
460
|
+
}
|
|
461
|
+
static get domID(): number {
|
|
462
|
+
return _htmlFormDomID;
|
|
463
|
+
}
|
|
464
|
+
static set domID(n: number) {
|
|
465
|
+
_htmlFormDomID = n;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
protected _space: HTMLSpace;
|
|
469
|
+
protected _ready: boolean = false;
|
|
470
|
+
protected _formID: number = 0;
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Create a new `HTMLForm`. Alternatively, you can use [`HTMLSpace.getForm`](#link) function to get an instance of HTMLForm.
|
|
474
|
+
* @param space the space to use
|
|
475
|
+
*/
|
|
476
|
+
constructor(space: HTMLSpace) {
|
|
477
|
+
super();
|
|
478
|
+
this._space = space;
|
|
479
|
+
this._formID = HTMLForm.groupID++;
|
|
480
|
+
|
|
481
|
+
const init = () => {
|
|
482
|
+
this._ctx.group = this._space.element;
|
|
483
|
+
this._ctx.groupID = `pts_dom_${this._formID}`;
|
|
484
|
+
this._ctx.style = Object.assign({}, this._style);
|
|
485
|
+
this._ready = true;
|
|
486
|
+
};
|
|
487
|
+
// a form created after the space is ready would otherwise never
|
|
488
|
+
// initialize, since start callbacks only run at readiness
|
|
489
|
+
if (this._space.ready) init();
|
|
490
|
+
else this._space.add({ start: init });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Get the corresponding space for this form
|
|
495
|
+
*/
|
|
496
|
+
get space(): HTMLSpace {
|
|
497
|
+
return this._space;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Usually not used directly. This updates a style in `_ctx` context or throw an Error if the style doesn't exist.
|
|
502
|
+
* @param k style key
|
|
503
|
+
* @param v style value
|
|
504
|
+
* @param unit Optional unit like 'px' to append to value
|
|
505
|
+
*/
|
|
506
|
+
protected styleTo(k: string, v: any, unit: string = "") {
|
|
507
|
+
if ((this._ctx.style as any)[k] === undefined)
|
|
508
|
+
throw new Error(`${k} style property doesn't exist`);
|
|
509
|
+
this._ctx.style[k] = `${v}${unit}`;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Set current alpha value.
|
|
514
|
+
* @example `form.alpha(0.6)`
|
|
515
|
+
* @param a alpha value between 0 and 1
|
|
516
|
+
*/
|
|
517
|
+
alpha(a: number): this {
|
|
518
|
+
this.styleTo("opacity", a);
|
|
519
|
+
return this;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Set current fill style. Provide a valid color string or `false` to specify no fill color.
|
|
524
|
+
* @example `form.fill("#F90")`, `form.fill("rgba(0,0,0,.5")`, `form.fill(false)`
|
|
525
|
+
* @param c fill color
|
|
526
|
+
*/
|
|
527
|
+
fill(c: string | boolean): this {
|
|
528
|
+
if (typeof c == "boolean") {
|
|
529
|
+
this.styleTo("filled", c);
|
|
530
|
+
if (!c) this.styleTo("background", "transparent");
|
|
531
|
+
} else {
|
|
532
|
+
this.styleTo("filled", true);
|
|
533
|
+
this.styleTo("background", c);
|
|
534
|
+
}
|
|
535
|
+
return this;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Set current stroke style. Provide a valid color string or `false` to specify no stroke color.
|
|
540
|
+
* @example `form.stroke("#F90")`, `form.stroke("rgba(0,0,0,.5")`, `form.stroke(false)`, `form.stroke("#000", 0.5, 'round', 'square')`
|
|
541
|
+
* @param c stroke color which can be as color, gradient, or pattern. (See [canvas documentation](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/strokeStyle))
|
|
542
|
+
* @param width Optional value (can be floating point) to set line width
|
|
543
|
+
* @param linejoin not implemented in HTMLForm
|
|
544
|
+
* @param linecap not implemented in HTMLForm
|
|
545
|
+
*/
|
|
546
|
+
stroke(
|
|
547
|
+
c: string | boolean,
|
|
548
|
+
width?: number,
|
|
549
|
+
linejoin?: string,
|
|
550
|
+
linecap?: string,
|
|
551
|
+
): this {
|
|
552
|
+
if (typeof c == "boolean") {
|
|
553
|
+
this.styleTo("stroked", c);
|
|
554
|
+
if (!c) this.styleTo("border-width", 0);
|
|
555
|
+
} else {
|
|
556
|
+
this.styleTo("stroked", true);
|
|
557
|
+
this.styleTo("border-color", c);
|
|
558
|
+
this.styleTo("border-width", (width || 1) + "px");
|
|
559
|
+
}
|
|
560
|
+
return this;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Set current text color style. Provide a valid color string.
|
|
565
|
+
* @example `form.fill("#F90")`, `form.fill("rgba(0,0,0,.5")`, `form.fill(false)`
|
|
566
|
+
* @param c fill color
|
|
567
|
+
*/
|
|
568
|
+
fillText(c: string): this {
|
|
569
|
+
this.styleTo("color", c);
|
|
570
|
+
return this;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Add custom class to the created element.
|
|
575
|
+
* @param c custom class name or `false` to reset it
|
|
576
|
+
* @example `form.fill("#f00").cls("myClass").rects(r)` `form.cls(false).circles(c)`
|
|
577
|
+
*/
|
|
578
|
+
cls(c: string | boolean) {
|
|
579
|
+
if (typeof c == "boolean") {
|
|
580
|
+
this._ctx.currentClass = "";
|
|
581
|
+
} else {
|
|
582
|
+
this._ctx.currentClass = c;
|
|
583
|
+
}
|
|
584
|
+
return this;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Set the current font.
|
|
589
|
+
* @param sizeOrFont either a number to specify font-size, or a `Font` object to specify all font properties
|
|
590
|
+
* @param weight Optional font-weight string such as "bold"
|
|
591
|
+
* @param style Optional font-style string such as "italic"
|
|
592
|
+
* @param lineHeight Optional line-height number suchas 1.5
|
|
593
|
+
* @param family Optional font-family such as "Helvetica, sans-serif"
|
|
594
|
+
* @example `form.font( myFont )`, `form.font(14, "bold")`
|
|
595
|
+
*/
|
|
596
|
+
font(
|
|
597
|
+
sizeOrFont: number | Font,
|
|
598
|
+
weight?: string,
|
|
599
|
+
style?: string,
|
|
600
|
+
lineHeight?: number,
|
|
601
|
+
family?: string,
|
|
602
|
+
): this {
|
|
603
|
+
if (typeof sizeOrFont == "number") {
|
|
604
|
+
this._font.size = sizeOrFont;
|
|
605
|
+
if (family) this._font.face = family;
|
|
606
|
+
if (weight) this._font.weight = weight;
|
|
607
|
+
if (style) this._font.style = style;
|
|
608
|
+
if (lineHeight) this._font.lineHeight = lineHeight;
|
|
609
|
+
} else {
|
|
610
|
+
this._font = sizeOrFont;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
this._ctx.style["font"] = this._font.value;
|
|
614
|
+
|
|
615
|
+
return this;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Reset the context's common styles to this form's styles. This supports using multiple forms on the same canvas context.
|
|
620
|
+
*/
|
|
621
|
+
reset(): this {
|
|
622
|
+
this._ctx.style = Object.assign({}, this._style);
|
|
623
|
+
|
|
624
|
+
this._font = new Font(10, "sans-serif");
|
|
625
|
+
this._ctx.style["font"] = this._font.value;
|
|
626
|
+
|
|
627
|
+
return this;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Set this form's group scope by an ID, and optionally define the group's parent element. A group scope keeps track of elements by their generated IDs, and updates their properties as needed. See also `scope()`.
|
|
632
|
+
* @param group_id a string to use as prefix for the group's id. For example, group_id "hello" will create elements with id like "hello-1", "hello-2", etc
|
|
633
|
+
* @param group Optional DOM element to define this group's parent element
|
|
634
|
+
* @returns this form's context
|
|
635
|
+
*/
|
|
636
|
+
updateScope(group_id: string, group?: Element): DOMFormContext {
|
|
637
|
+
this._ctx.group = group;
|
|
638
|
+
this._ctx.groupID = group_id;
|
|
639
|
+
this._ctx.groupCount = 0;
|
|
640
|
+
this.nextID();
|
|
641
|
+
return this._ctx;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Set the current group scope to an item added into space, in order to keep track of any point, circle, etc created within it. The item must have an `animateID` property, so that elements created within the item will have generated IDs like "item-{animateID}-{count}".
|
|
646
|
+
* @param item a "player" item that's added to space (see `space.add(...)`) and has an `animateID` property
|
|
647
|
+
* @returns this form's context
|
|
648
|
+
*/
|
|
649
|
+
scope(item: IPlayer) {
|
|
650
|
+
if (!item || item.animateID == null)
|
|
651
|
+
throw new Error("item not defined or not yet added to Space");
|
|
652
|
+
// two forms scoped to the same player must not generate the same ids
|
|
653
|
+
return this.updateScope(
|
|
654
|
+
`${HTMLForm.scopeID(item)}-f${this._formID}`,
|
|
655
|
+
this.space.element,
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Get next available id in the current group.
|
|
661
|
+
* @returns an id string
|
|
662
|
+
*/
|
|
663
|
+
nextID(): string {
|
|
664
|
+
this._ctx.groupCount++;
|
|
665
|
+
this._ctx.currentID = `${this._ctx.groupID}-${this._ctx.groupCount}`;
|
|
666
|
+
return this._ctx.currentID;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* A static function to generate an ID string based on a context object.
|
|
671
|
+
* @param ctx a context object for an HTMLForm
|
|
672
|
+
*/
|
|
673
|
+
static getID(ctx: DOMFormContext): string {
|
|
674
|
+
return ctx.currentID || `p-${HTMLForm.domID++}`;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* A static function to generate an ID string for a scope, based on a "player" item in the Space.
|
|
679
|
+
* @param item a "player" item that's added to space (see `space.add(...)`) and has an `animateID` property
|
|
680
|
+
*/
|
|
681
|
+
static scopeID(item: IPlayer): string {
|
|
682
|
+
return `item-${item.animateID}`;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* A static function to help adding style object to an element. This put all styles into `style` attribute instead of individual attributes, so that the styles can be parsed by Adobe Illustrator.
|
|
687
|
+
* @param elem A DOM element to add to
|
|
688
|
+
* @param styles an object of style properties
|
|
689
|
+
* @example `HTMLForm.style(elem, {fill: "#f90", stroke: false})`
|
|
690
|
+
* @returns DOM element
|
|
691
|
+
*/
|
|
692
|
+
static style(elem: Element, styles: Record<string, any>): Element {
|
|
693
|
+
let st = [];
|
|
694
|
+
|
|
695
|
+
if (!styles["filled"]) st.push("background: none");
|
|
696
|
+
if (!styles["stroked"]) st.push("border: none");
|
|
697
|
+
|
|
698
|
+
for (let k in styles) {
|
|
699
|
+
if (styles.hasOwnProperty(k) && k != "filled" && k != "stroked") {
|
|
700
|
+
let v = styles[k];
|
|
701
|
+
if (v) {
|
|
702
|
+
if (!styles["filled"] && k.indexOf("background") === 0) {
|
|
703
|
+
continue;
|
|
704
|
+
} else if (!styles["stroked"] && k.indexOf("border-width") === 0) {
|
|
705
|
+
continue;
|
|
706
|
+
} else {
|
|
707
|
+
st.push(`${k}: ${v}`);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
return HTMLSpace.setAttr(elem, { style: st.join(";") });
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* A helper function to set top, left, width, height of DOM element.
|
|
718
|
+
* @param ctx the HTMLForm context whose style is updated
|
|
719
|
+
* @param pt left and top position
|
|
720
|
+
* @param size width and height
|
|
721
|
+
*/
|
|
722
|
+
static rectStyle(
|
|
723
|
+
ctx: DOMFormContext,
|
|
724
|
+
pt: PtLike,
|
|
725
|
+
size: PtLike,
|
|
726
|
+
): DOMFormContext {
|
|
727
|
+
ctx.style["left"] = pt[0] + "px";
|
|
728
|
+
ctx.style["top"] = pt[1] + "px";
|
|
729
|
+
ctx.style["width"] = size[0] + "px";
|
|
730
|
+
ctx.style["height"] = size[1] + "px";
|
|
731
|
+
return ctx;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* A helper function to set the top and left position styling of text DOM context.
|
|
736
|
+
* @param ctx context to add style to
|
|
737
|
+
* @param pt a Pt object or numeric array determining the top-left position of the text
|
|
738
|
+
*/
|
|
739
|
+
static textStyle(ctx: DOMFormContext, pt: PtLike): DOMFormContext {
|
|
740
|
+
ctx.style["left"] = pt[0] + "px";
|
|
741
|
+
ctx.style["top"] = pt[1] + "px";
|
|
742
|
+
return ctx;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* A static function to draws a point.
|
|
747
|
+
* @param ctx a context object of HTMLForm
|
|
748
|
+
* @param pt a Pt object or numeric array
|
|
749
|
+
* @param radius radius of the point. Default is 5.
|
|
750
|
+
* @param shape The shape of the point. Defaults to "square", but it can be "circle" or a custom shape function in your own implementation.
|
|
751
|
+
* @example `HTMLForm.point( p )`, `HTMLForm.point( p, 10, "circle" )`
|
|
752
|
+
*/
|
|
753
|
+
static point(
|
|
754
|
+
ctx: DOMFormContext,
|
|
755
|
+
pt: PtLike,
|
|
756
|
+
radius: number = 5,
|
|
757
|
+
shape: string = "square",
|
|
758
|
+
): Element {
|
|
759
|
+
if (shape === "circle") {
|
|
760
|
+
return HTMLForm.circle(ctx, pt, radius);
|
|
761
|
+
} else {
|
|
762
|
+
return HTMLForm.square(ctx, pt, radius);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Draws a point.
|
|
768
|
+
* @param pt a Pt object
|
|
769
|
+
* @param radius radius of the point. Default is 5.
|
|
770
|
+
* @param shape The shape of the point. Defaults to "square", but it can be "circle" or a custom shape function in your own implementation.
|
|
771
|
+
* @example `form.point( p )`, `form.point( p, 10, "circle" )`
|
|
772
|
+
*/
|
|
773
|
+
point(pt: PtLike, radius: number = 5, shape: string = "square"): this {
|
|
774
|
+
this.nextID();
|
|
775
|
+
// reset radius for squares, so a square after a circle isn't rounded
|
|
776
|
+
this.styleTo("border-radius", shape == "circle" ? "100%" : "0");
|
|
777
|
+
HTMLForm.point(this._ctx, pt, radius, shape);
|
|
778
|
+
return this;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* A static function to draw a circle.
|
|
783
|
+
* @param ctx a context object of HTMLForm
|
|
784
|
+
* @param pt center position of the circle
|
|
785
|
+
* @param radius radius of the circle
|
|
786
|
+
*/
|
|
787
|
+
static circle(ctx: DOMFormContext, pt: PtLike, radius: number = 10): Element {
|
|
788
|
+
let elem = HTMLSpace.htmlElement(ctx.group, "div", HTMLForm.getID(ctx));
|
|
789
|
+
HTMLSpace.setAttr(elem, {
|
|
790
|
+
class: `pts-form pts-circle ${ctx.currentClass}`,
|
|
791
|
+
});
|
|
792
|
+
HTMLForm.rectStyle(
|
|
793
|
+
ctx,
|
|
794
|
+
new Pt(pt).$subtract(radius),
|
|
795
|
+
new Pt(radius * 2, radius * 2),
|
|
796
|
+
);
|
|
797
|
+
HTMLForm.style(elem, ctx.style);
|
|
798
|
+
return elem;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Draw a circle.
|
|
803
|
+
* @param pts usually a Group of 2 Pts, but it can also take an array of two numeric arrays [ [position], [size] ]
|
|
804
|
+
* @see [`Circle.fromCenter`](#link)
|
|
805
|
+
*/
|
|
806
|
+
circle(pts: GroupLike | number[][]): this {
|
|
807
|
+
this.nextID();
|
|
808
|
+
this.styleTo("border-radius", "100%");
|
|
809
|
+
HTMLForm.circle(this._ctx, pts[0], pts[1][0]);
|
|
810
|
+
return this;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* A static function to draw a square.
|
|
815
|
+
* @param ctx a context object of HTMLForm
|
|
816
|
+
* @param pt center position of the square
|
|
817
|
+
* @param halfsize half size of the square
|
|
818
|
+
*/
|
|
819
|
+
static square(ctx: DOMFormContext, pt: PtLike, halfsize: number) {
|
|
820
|
+
let elem = HTMLSpace.htmlElement(ctx.group, "div", HTMLForm.getID(ctx));
|
|
821
|
+
HTMLSpace.setAttr(elem, {
|
|
822
|
+
class: `pts-form pts-square ${ctx.currentClass}`,
|
|
823
|
+
});
|
|
824
|
+
HTMLForm.rectStyle(
|
|
825
|
+
ctx,
|
|
826
|
+
new Pt(pt).$subtract(halfsize),
|
|
827
|
+
new Pt(halfsize * 2, halfsize * 2),
|
|
828
|
+
);
|
|
829
|
+
HTMLForm.style(elem, ctx.style);
|
|
830
|
+
return elem;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Draw a square, given a center and its half-size.
|
|
835
|
+
* @param pt center Pt
|
|
836
|
+
* @param halfsize half-size
|
|
837
|
+
*/
|
|
838
|
+
square(pt: PtLike, halfsize: number): this {
|
|
839
|
+
this.nextID();
|
|
840
|
+
this.styleTo("border-radius", "0");
|
|
841
|
+
HTMLForm.square(this._ctx, pt, halfsize);
|
|
842
|
+
return this;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* A static function to draw a rectangle.
|
|
847
|
+
* @param ctx a context object of HTMLForm
|
|
848
|
+
* @param pts a Group or an Iterable<PtLike> with 2 Pt specifying the top-left and bottom-right positions.
|
|
849
|
+
*/
|
|
850
|
+
static rect(ctx: DOMFormContext, pts: PtLikeIterable): Element | undefined {
|
|
851
|
+
let p = Util.iterToArray(pts);
|
|
852
|
+
if (!Util.arrayCheck(p)) return;
|
|
853
|
+
|
|
854
|
+
let elem = HTMLSpace.htmlElement(ctx.group, "div", HTMLForm.getID(ctx));
|
|
855
|
+
HTMLSpace.setAttr(elem, { class: `pts-form pts-rect ${ctx.currentClass}` });
|
|
856
|
+
HTMLForm.rectStyle(ctx, p[0], p[1]);
|
|
857
|
+
HTMLForm.style(elem, ctx.style);
|
|
858
|
+
return elem;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Draw a rectangle.
|
|
863
|
+
* @param pts a Group or an Iterable<PtLike> with 2 Pt specifying the top-left and bottom-right positions.
|
|
864
|
+
*/
|
|
865
|
+
rect(pts: PtLikeIterable): this {
|
|
866
|
+
this.nextID();
|
|
867
|
+
this.styleTo("border-radius", "0");
|
|
868
|
+
HTMLForm.rect(this._ctx, pts);
|
|
869
|
+
return this;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* A static function to draw text.
|
|
874
|
+
* @param ctx a context object of HTMLForm
|
|
875
|
+
* @param pt a Point object to specify the anchor point
|
|
876
|
+
* @param txt a string of text to draw
|
|
877
|
+
*/
|
|
878
|
+
static text(ctx: DOMFormContext, pt: PtLike, txt: string): Element {
|
|
879
|
+
let elem = HTMLSpace.htmlElement(ctx.group, "div", HTMLForm.getID(ctx));
|
|
880
|
+
|
|
881
|
+
HTMLSpace.setAttr(elem, { class: `pts-form pts-text ${ctx.currentClass}` });
|
|
882
|
+
|
|
883
|
+
elem.textContent = txt;
|
|
884
|
+
HTMLForm.textStyle(ctx, pt);
|
|
885
|
+
HTMLForm.style(elem, ctx.style);
|
|
886
|
+
|
|
887
|
+
return elem;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Draw text in a DOM element.
|
|
892
|
+
* @param pt a Pt or numeric array to specify the anchor point
|
|
893
|
+
* @param txt text
|
|
894
|
+
*/
|
|
895
|
+
text(pt: PtLike, txt: string): this {
|
|
896
|
+
this.nextID();
|
|
897
|
+
HTMLForm.text(this._ctx, pt, txt);
|
|
898
|
+
return this;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* A convenient way to draw some text on canvas for logging or debugging. It'll be draw on the top-left of the canvas as an overlay.
|
|
903
|
+
* @param txt text
|
|
904
|
+
*/
|
|
905
|
+
log(txt: any): this {
|
|
906
|
+
this.fill("#000").stroke("#fff", 0.5).text([10, 14], txt);
|
|
907
|
+
return this;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Arc is not implemented in HTMLForm.
|
|
912
|
+
*/
|
|
913
|
+
arc(
|
|
914
|
+
pt: PtLike,
|
|
915
|
+
radius: number,
|
|
916
|
+
startAngle: number,
|
|
917
|
+
endAngle: number,
|
|
918
|
+
cc?: boolean,
|
|
919
|
+
): this {
|
|
920
|
+
Util.warn("arc is not implemented in HTMLForm");
|
|
921
|
+
return this;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Line is not implemented in HTMLForm.
|
|
926
|
+
*/
|
|
927
|
+
line(pts: GroupLike | number[][]): this {
|
|
928
|
+
Util.warn("line is not implemented in HTMLForm");
|
|
929
|
+
return this;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Polygon is not implemented in HTMLForm.
|
|
934
|
+
* @param pts
|
|
935
|
+
*/
|
|
936
|
+
polygon(pts: GroupLike | number[][]): this {
|
|
937
|
+
Util.warn("polygon is not implemented in HTMLForm");
|
|
938
|
+
return this;
|
|
939
|
+
}
|
|
940
|
+
}
|