diff --git a/docs/script.ts b/docs/script.ts index 39a3a05..42f2e7d 100644 --- a/docs/script.ts +++ b/docs/script.ts @@ -486,7 +486,7 @@ function renderDemo( video.source = "https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4"; video.width = canvas.width; - video.anchor = WebSpinner.Vector2D.zero; + video.anchor = WebSpinner.Vector2D.zero(); canvas.listen.click(() => video.play()); } diff --git a/src/classes/color.ts b/src/classes/color.ts index 55818b1..3de276d 100644 --- a/src/classes/color.ts +++ b/src/classes/color.ts @@ -57,14 +57,6 @@ export class Color { return this.#str; } - static get random() { - return Color.rgb( - Math.random() * 255, - Math.random() * 255, - Math.random() * 255 - ); - } - static rgb(red: number, green: number, blue: number, alpha?: number) { return new Color(red, green, blue, alpha); } diff --git a/src/classes/gradient.ts b/src/classes/gradient.ts index 312ff38..247412c 100644 --- a/src/classes/gradient.ts +++ b/src/classes/gradient.ts @@ -205,7 +205,7 @@ export class ConicalGradient extends Gradient { constructor( startAngle: Angle = Angle.zero(), - offset: Vector2D = Vector2D.zero + offset: Vector2D = Vector2D.zero() ) { super(); diff --git a/src/classes/mouse.ts b/src/classes/mouse.ts index 39f16d9..d739a64 100644 --- a/src/classes/mouse.ts +++ b/src/classes/mouse.ts @@ -34,26 +34,34 @@ export class MouseTracker extends MouseData { this.#previous = new MouseData(); - (target as HTMLElement).addEventListener("mousedown", (event) => { + const updatePosition = (event: MouseEvent) => { + super.x = event.x; + super.y = event.y; + }; + + const targetElement = target as HTMLElement; + + targetElement.addEventListener("mousedown", (event) => { super.buttonStates[event.button] = true; + + updatePosition(event); }); - (target as HTMLElement).addEventListener("mouseup", (event) => { + targetElement.addEventListener("mouseup", (event) => { super.buttonStates[event.button] = false; + + updatePosition(event); }); - target.addEventListener("mouseenter", (event) => { + targetElement.addEventListener("mouseenter", (event) => { super.over = true; }); - target.addEventListener("mouseleave", (event) => { + targetElement.addEventListener("mouseleave", (event) => { super.over = false; }); - window.addEventListener("mousemove", (event) => { - super.x = event.x; - super.y = event.y; - }); + window.addEventListener("mousemove", updatePosition); } advanceFrame() { diff --git a/src/classes/shadow.ts b/src/classes/shadow.ts index 284309c..03ea946 100644 --- a/src/classes/shadow.ts +++ b/src/classes/shadow.ts @@ -5,7 +5,7 @@ import { Vector2D } from "./vector2d"; export class Shadow { #blur = createState(0); #color = Color.gray(0); - #offset = Vector2D.zero; + #offset = Vector2D.zero(); #changeListenerMap = new Map, () => void>(); constructor(options: Partial) { diff --git a/src/classes/state.ts b/src/classes/state.ts index 621ed42..3b16c9f 100644 --- a/src/classes/state.ts +++ b/src/classes/state.ts @@ -59,10 +59,7 @@ export class State { * @returns */ - replace( - other: O, - changeListener: ChangeListener - ): State { + replace(other: O, changeListener: ChangeListener): O { if (this.equals(other)) { if (this === other) return other; diff --git a/src/classes/vector2d.ts b/src/classes/vector2d.ts index 859ab19..6c1f0fe 100644 --- a/src/classes/vector2d.ts +++ b/src/classes/vector2d.ts @@ -46,7 +46,7 @@ export class Vector2D extends State { return Vector2D.xy(-this.x, -this.y); } - static get one() { + static one() { return new Vector2D(1); } @@ -105,7 +105,7 @@ export class Vector2D extends State { this.handleChange(); } - static get zero() { + static zero() { return new Vector2D(); } } diff --git a/src/elements/mixable.ts b/src/elements/mixable.ts index 0a736ae..296fff4 100644 --- a/src/elements/mixable.ts +++ b/src/elements/mixable.ts @@ -31,6 +31,7 @@ export class CustomHTMLElement extends HTMLElement { /** * @private */ + registerChange

>( propertyName: P, newValue: this[P] diff --git a/src/elements/visual/bezier.ts b/src/elements/visual/bezier.ts index 9718dec..54ff64b 100644 --- a/src/elements/visual/bezier.ts +++ b/src/elements/visual/bezier.ts @@ -18,8 +18,8 @@ function hasControlPoints(Base: B) { "control-b", ]; - #controlA = Vector2D.zero; - #controlB = Vector2D.zero; + #controlA = Vector2D.zero(); + #controlB = Vector2D.zero(); /** * Controls the shape at the beginning of the curve. diff --git a/src/elements/visual/canvas.ts b/src/elements/visual/canvas.ts index bfa46ab..e9696fb 100644 --- a/src/elements/visual/canvas.ts +++ b/src/elements/visual/canvas.ts @@ -7,6 +7,7 @@ import { attributeParser } from "../../utlities/attributeParser"; import { C2DBase } from "./c2dBase"; import { Canvas2DBaseRenderable } from "./renderable"; import { DrawStyle } from "../../classes/gradient"; +import { CustomHTMLElement } from "../mixable"; export class Canvas2DCanvasElement extends c2dStandaloneChildren(C2DBase) { static observedAttributes: string[] = [ @@ -177,6 +178,17 @@ export class Canvas2DCanvasElement extends c2dStandaloneChildren(C2DBase) { this.alpha = this.alpha; } + createChild( + ElementClass: E, + options?: Partial>> | undefined + ): InstanceType { + const child = super.createChild(ElementClass, options); + + this.queueRender(); + + return child; + } + get keyDown() { return this.#keyboardTracker.down; } diff --git a/src/elements/visual/renderable.ts b/src/elements/visual/renderable.ts index 45f5ab1..4e20e3f 100644 --- a/src/elements/visual/renderable.ts +++ b/src/elements/visual/renderable.ts @@ -10,6 +10,7 @@ import { c2dShapeChildren, c2dStandaloneChildren } from "../../mixins/children"; import { Canvas2DCanvasElement } from "./canvas"; import { C2DBase } from "./c2dBase"; import { Canvas2DShape } from "./shape"; +import { CustomHTMLElement } from "../mixable"; export const changedEvent = new Event("change", { bubbles: true }); @@ -56,6 +57,17 @@ export class Canvas2DBaseRenderable extends C2DBase { super.addEventListener(type, listener, options); } + createChild( + ElementClass: E, + options?: Partial>> | undefined + ): InstanceType { + const child = super.createChild(ElementClass, options); + + this.dispatchEvent(changedEvent); + + return child; + } + /** * @private */ diff --git a/src/mixins/fromTo.ts b/src/mixins/fromTo.ts index 6c0db7b..1af0cd1 100644 --- a/src/mixins/fromTo.ts +++ b/src/mixins/fromTo.ts @@ -41,7 +41,7 @@ export function hasFrom(Base: B) { return class extends Base { static observedAttributes = [...super.observedAttributes, "from"]; - #from = Vector2D.zero; + #from = Vector2D.zero(); /** * Starting point of the element relative to its anchor. diff --git a/src/mixins/transform.ts b/src/mixins/transform.ts index 8594c8c..cf0cea0 100644 --- a/src/mixins/transform.ts +++ b/src/mixins/transform.ts @@ -22,11 +22,11 @@ export function baseTransform(Base: B) { "velocity", ]; - #anchor = Vector2D.zero; + #anchor = Vector2D.zero(); #angle = Angle.radians(0); #angularVelocity = Angle.radians(0); - #scale = Vector2D.one; - #velocity = Vector2D.zero; + #scale = Vector2D.one(); + #velocity = Vector2D.zero(); constructor(...args: any[]) { super(...args); @@ -34,6 +34,14 @@ export function baseTransform(Base: B) { this.#anchor.addChangeListener(this.#anchorChangeListener); this.#angle.addChangeListener(this.#angleChangeListener); + + this.#angularVelocity.addChangeListener( + this.#angularVelocityChangeListener + ); + + this.#scale.addChangeListener(this.#scaleChangeListener); + + this.#velocity.addChangeListener(this.#velocityChangeListener); } #angleChangeListener: ChangeListener = () => { @@ -51,12 +59,16 @@ export function baseTransform(Base: B) { } set angle(value) { - const replace = this.#angle.replace.bind(this.#angle); - - replace((this.#angle = value), this.#angleChangeListener); + this.#angle.replace((this.#angle = value), this.#angleChangeListener); } #angularVelocityChangedTime = -1; + + #angularVelocityChangeListener: ChangeListener = () => { + this.#angularVelocityChangedTime = performance.now(); + + this.registerChange("angularVelocity", this.angularVelocity); + }; /** * Clockwise rotation per second. * @@ -68,13 +80,10 @@ export function baseTransform(Base: B) { } set angularVelocity(value) { - if (this.#angularVelocity.equals(value)) { - return; - } - - this.#angularVelocityChangedTime = performance.now(); - - this.registerChange("angularVelocity", (this.#angularVelocity = value)); + this.#angularVelocity.replace( + (this.#angularVelocity = value), + this.#angularVelocityChangeListener + ); } /** @@ -92,9 +101,7 @@ export function baseTransform(Base: B) { }; set anchor(value) { - const replace = this.#anchor.replace.bind(this.#anchor); - - replace((this.#anchor = value), this.#anchorChangeListener); + this.#anchor.replace((this.#anchor = value), this.#anchorChangeListener); } _applyMovement(deltaTime: number) { @@ -179,6 +186,10 @@ export function baseTransform(Base: B) { this.angle = Angle.radians(this.#angle.radians - angle.radians); } + #scaleChangeListener: ChangeListener = () => { + this.registerChange("scale", this.#scale); + }; + /** * Multiplies the size of the element in the x and y direction. This also affects * line width. Setting scale to a number will set both the x and y scale to that @@ -198,15 +209,21 @@ export function baseTransform(Base: B) { if (this.#scale.equals(vectorValue)) return; this.registerChange("scale", (this.#scale = vectorValue)); - } else if (this.#scale.equals(value)) { + return; - } else { - this.registerChange("scale", (this.#scale = value)); } + + this.#scale.replace((this.#scale = value), this.#scaleChangeListener); } #velocityChangedTime = -1; + #velocityChangeListener: ChangeListener = () => { + this.#velocityChangedTime = performance.now(); + + this.registerChange("velocity", this.#velocity); + }; + /** * Anchor movement per second. * @@ -218,11 +235,10 @@ export function baseTransform(Base: B) { } set velocity(value) { - if (this.#velocity.equals(value)) return; - - this.registerChange("velocity", (this.#velocity = value)); - - this.#velocityChangedTime = performance.now(); + this.#velocity.replace( + (this.#velocity = value), + this.#velocityChangeListener + ); } }; }