From 1ace6879fc19807bccde73bf494076883df04888 Mon Sep 17 00:00:00 2001 From: Manuel Barzi Date: Thu, 28 Dec 2023 17:30:51 +0100 Subject: [PATCH] update doc and demos; add mouse enter and leave events support --- README.md | 104 ++++++++++------ dist/wings.cjs | 24 +++- dist/wings.mjs | 24 +++- index.html | 232 ++++++++++++++++++++++++++++-------- package.json | 7 +- scripts/counter-up.js | 61 ++++++++++ scripts/toggling-saluter.js | 84 +++++++++++++ style.css | 6 +- wings.js | 24 +++- 9 files changed, 462 insertions(+), 104 deletions(-) create mode 100644 scripts/counter-up.js create mode 100644 scripts/toggling-saluter.js diff --git a/README.md b/README.md index 2b20445..6662090 100644 --- a/README.md +++ b/README.md @@ -4,68 +4,98 @@ Object-oriented Component-based UI JavaScript library for HTML Canvas (inspired ##### Usage -- link wings - -```html - -``` - -- add a canvas - ```html + ``` -- add `View` and `Component` classes - ```js -const { View, Component } = Wings -``` - -- construct a view with a reference to the canvas +// import wings in javascript (esm) +import Wings from '@b00tc4mp/wings' +// or require it (cjs) +// const Wings = require('@b00tc4mp/wings') -```js -const view = new View(document.getElementById("view")) -``` +// destructure it +const { View, Component } = Wings -- create a component class (example) +// instantiate a view linked to a canvas +const view = new View(document.getElementById('toggling-saluter')) -```js -class Box extends Component { - constructor() { +// create a saluter abstraction +class TogglingSaluter extends Component { + constructor(hello, bye) { super() // set the dimensions - this.width = this.height = 50 + this.width = 150 + this.height = 50 // set colors this.backgroundColor = 'magenta' this.borderColor = 'cyan' this.borderWidth = 5 + + // set the text height for the salutation + this.textHeight = 16 + + // init the salutation value + this.hello = hello + this.bye = bye + this.salutation = this.hello + + // add behavior to toggle the salutation on click + this.on('MouseClick', () => { + if (this.salutation === this.hello) + this.salutation = this.bye + else + this.salutation = this.hello + }) + } + + render(context) { + // render the base layer + super.render(context) + + // render the saluter value centered in the component + context.fillStyle = 'white' + context.font = this.textHeight + 'px verdana' + const width = context.measureText(this.salutation).width + context.fillText( + this.salutation, + (this.width - width) / 2, + (this.height + this.textHeight) / 2 + ) } } -``` -- create an instance and add it to the view +// instantiate a saluter and center it at the top of the view +const saluter = new TogglingSaluter('Hello, World!', 'Bye, World!') -```js -const box = new Box +saluter.x = (view.width - saluter.width) / 2 +saluter.y = (view.height / 3 - saluter.height) / 2 -view.add(box) -``` +// add the saluter to the view +view.add(saluter) -- locate it inside the view +// instantiate a saluter and center it in the view +const saluter2 = new TogglingSaluter('Hola Mundo!', 'Adiós, Mundo!') -```js -box.x = box.y = 100 -``` +saluter2.x = (view.width - saluter2.width) / 2 +saluter2.y = (view.height - saluter2.height) / 2 -- add a mouse reaction to it (example) +// add the saluter to the view +view.add(saluter2) -```js -box.on('MouseClick', () => alert('Hello, World!')) -``` +// instantiate a saluter and center it at the bottom of the view +const saluter3 = new TogglingSaluter('Ciao Mondo!', 'Addio, Mondo!') -- and that's it +saluter3.x = (view.width - saluter3.width) / 2 +saluter3.y = (view.height * 5 / 3 - saluter3.height) / 2 + +// add the saluter to the view +view.add(saluter3) + +// and that's it +``` See more examples at https://b00tc4mp.github.io/wings \ No newline at end of file diff --git a/dist/wings.cjs b/dist/wings.cjs index 873e140..78cfff0 100644 --- a/dist/wings.cjs +++ b/dist/wings.cjs @@ -4,13 +4,17 @@ * An Object-Oriented Component-based UI Library for Canvas built in JavaScript (inspired by Java Swing). * * @author manuelbarzi - * @version 1.0.2 + * @version 1.1.0 */ const Wings = (() => { class Component { constructor() { this.x = this.y = this.width = this.height = 0 - this.mouse = {} + this.mouse = { + entered: false, + pressed: false, + dragging: false + } this.behaviors = [] this.parent = null this.children = [] @@ -58,14 +62,26 @@ const Wings = (() => { mouseMove(event) { if (this.visible) { if (this.isPointed(event.x, event.y)) { + if (!this.mouse.entered) { + this.mouse.entered = true + this.fireEvent('MouseEnter', event) + } + this.fireEvent('MouseMove', event) if (this.mouse.pressed) { this.mouse.dragging = true this.fireEvent('MouseDrag', event) } - } else if (this.mouse.dragging) - this.fireEvent('MouseDrag', event) + } else { + if (this.mouse.entered) { + this.mouse.entered = false + this.fireEvent('MouseLeave', event) + } + + if (this.mouse.dragging) + this.fireEvent('MouseDrag', event) + } if (this.children.length > 0) for (const child of this.children) diff --git a/dist/wings.mjs b/dist/wings.mjs index b87318a..2f81b50 100644 --- a/dist/wings.mjs +++ b/dist/wings.mjs @@ -4,13 +4,17 @@ * An Object-Oriented Component-based UI Library for Canvas built in JavaScript (inspired by Java Swing). * * @author manuelbarzi - * @version 1.0.2 + * @version 1.1.0 */ const Wings = (() => { class Component { constructor() { this.x = this.y = this.width = this.height = 0 - this.mouse = {} + this.mouse = { + entered: false, + pressed: false, + dragging: false + } this.behaviors = [] this.parent = null this.children = [] @@ -58,14 +62,26 @@ const Wings = (() => { mouseMove(event) { if (this.visible) { if (this.isPointed(event.x, event.y)) { + if (!this.mouse.entered) { + this.mouse.entered = true + this.fireEvent('MouseEnter', event) + } + this.fireEvent('MouseMove', event) if (this.mouse.pressed) { this.mouse.dragging = true this.fireEvent('MouseDrag', event) } - } else if (this.mouse.dragging) - this.fireEvent('MouseDrag', event) + } else { + if (this.mouse.entered) { + this.mouse.entered = false + this.fireEvent('MouseLeave', event) + } + + if (this.mouse.dragging) + this.fireEvent('MouseDrag', event) + } if (this.children.length > 0) for (const child of this.children) diff --git a/index.html b/index.html index e6fba3c..cbfb4d1 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,14 @@ + Object-oriented Component-based UI JavaScript library for HTML Canvas (inspired by Java Swing) + + + @@ -18,87 +22,209 @@

Wings

Object-oriented Component-based UI JavaScript library for HTML Canvas (inspired by Java Swing)

- 1.0.2 + 1.1.0 -

Demos

+
+

Intallation

+ +
$ npm i @b00tc4mp/wings --hello
+ +

Usage (example)

+ +
+

Toggling Saluter

+ +

A saluter that toggles from Hello to Bye, World, and viceversa when clicking on it

+ + + + + +
+ +

Steps

+ +
<!-- create a canvas in html -->
+<canvas id="toggling-saluter" width="300px" height="300px">
+ +
// import wings in javascript (esm)
+import Wings from '@b00tc4mp/wings'
+// or require it (cjs)
+// const Wings = require('@b00tc4mp/wings')
+
+// destructure it
+const { View, Component } = Wings
+
+// instantiate a view linked to a canvas
+const view = new View(document.getElementById('toggling-saluter'))
+
+// create a saluter abstraction
+class TogglingSaluter extends Component {
+	constructor(hello, bye) {
+		super()
+
+		// set the dimensions
+		this.width = 150
+		this.height = 50
+
+		// set colors
+		this.backgroundColor = 'magenta'
+		this.borderColor = 'cyan'
+		this.borderWidth = 5
+
+		// set the text height for the salutation
+		this.textHeight = 16
+
+		// init the salutation value 
+		this.hello = hello
+		this.bye = bye
+		this.salutation = this.hello
 
-	
-

Draggable Body

+ // add behavior to toggle the salutation on click + this.on('MouseClick', () => { + if (this.salutation === this.hello) + this.salutation = this.bye + else + this.salutation = this.hello + }) + } -

A body with draggable parts

+ render(context) { + // render the base layer + super.render(context) - - + // render the saluter value centered in the component + context.fillStyle = 'white' + context.font = this.textHeight + 'px verdana' + const width = context.measureText(this.salutation).width + context.fillText( + this.salutation, + (this.width - width) / 2, + (this.height + this.textHeight) / 2 + ) + } +} -

code

-
+// instantiate a saluter and center it at the top of the view +const saluter = new TogglingSaluter('Hello, World!', 'Bye, World!') -
-

Rotating Image

+saluter.x = (view.width - saluter.width) / 2 +saluter.y = (view.height / 3 - saluter.height) / 2 -

An infinitely rotating image

+// add the saluter to the view +view.add(saluter) - - +// instantiate a saluter and center it in the view +const saluter2 = new TogglingSaluter('Hola Mundo!', 'Adiós, Mundo!') -

code

-
+saluter2.x = (view.width - saluter2.width) / 2 +saluter2.y = (view.height - saluter2.height) / 2 -
-

Reactive Box

+// add the saluter to the view +view.add(saluter2) -

A box that reacts on click

+// instantiate a saluter and center it at the bottom of the view +const saluter3 = new TogglingSaluter('Ciao Mondo!', 'Addio, Mondo!') - - +saluter3.x = (view.width - saluter3.width) / 2 +saluter3.y = (view.height * 5 / 3 - saluter3.height) / 2 -

code

-
+// add the saluter to the view +view.add(saluter3) -
-

Vertical Slider

+// and that's it
-

A slider that updates the knob value when dragging it

+

More examples

- - +
+

Draggable Body

-

code

-
+

A body with draggable parts

-
-

Movable Box

+ + -

A box that moves with the keyboard: 'W' moves UP, 'S' moves DOWN, 'A' moves LEFT, and 'D' moves RIGHT

+

code

+
- - +
+

Rotating Image

-

code

-
+

An infinitely rotating image

-
-

Rotating Cube

+ + -

An infinitely rotating cube

+

code

+
- - +
+

Reactive Box

-

code

-
+

A box that reacts on click

-
-

Mouth Frames

+ + -

Mouth frames rendered sequentially to produce an animation

+

code

+
- - +
+

Vertical Slider

-

code

-
+

A slider that updates the knob value when dragging it

+ + + + +

code

+ + +
+

Movable Box

+ +

A box that moves with the keyboard: 'W' moves UP, 'S' moves DOWN, 'A' moves LEFT, and 'D' moves RIGHT

+ + + + +

code

+
+ +
+

Rotating Cube

+ +

An infinitely rotating cube

+ + + + +

code

+
+ +
+

Mouth Frames

+ +

Mouth frames rendered sequentially to produce an animation

+ + + + +

code

+
+ +
+

Counter Up

+ +

A box that counts up on click and changes the cursor on mouse over

+ + + + +

code

+
+

@@ -109,6 +235,8 @@

Mouth Frames

+ + @@ -119,6 +247,8 @@

Mouth Frames

+ + \ No newline at end of file diff --git a/package.json b/package.json index dddd437..9591df9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@b00tc4mp/wings", "description": "Object-oriented Component-based UI JavaScript library for HTML Canvas (inspired by Java Swing)", - "version": "1.0.2", + "version": "1.1.0", "homepage": "https://b00tc4mp.github.io/wings", "repository": { "type": "git", @@ -18,6 +18,7 @@ } }, "scripts": { - "build": "node build" + "build": "node build", + "publish": "npm publish --access public" } -} +} \ No newline at end of file diff --git a/scripts/counter-up.js b/scripts/counter-up.js new file mode 100644 index 0000000..0be892f --- /dev/null +++ b/scripts/counter-up.js @@ -0,0 +1,61 @@ +/** + * A counter up + */ +{ + const { View, Component } = Wings + + const view = new View(document.getElementById('counter-up')) + + class Counter extends Component { + constructor() { + super() + + // set the dimensions + this.width = this.height = 50 + + // set colors + this.backgroundColor = 'magenta' + this.borderColor = 'cyan' + this.borderWidth = 5 + + this.count = 0 + this.textHeight = 16 + + this.on('MouseClick', () => this.count++) + this.on('MouseEnter', () => utils.showPointer()) + this.on('MouseLeave', () => utils.hidePointer()) + } + + render(context) { + super.render(context) + + context.fillStyle = 'white' + context.font = this.textHeight + 'px verdana' + + const width = context.measureText(this.count).width + + context.fillText(this.count, (this.width - width) / 2, (this.height + this.textHeight) / 2) + } + } + + const counter = new Counter + + counter.x = (view.width - counter.width) / 2 + counter.y = (view.height - counter.height) / 2 + + view.add(counter) + + // utils + + const utils = { + showPointer() { + const cursorStyle = document.createElement('style') + cursorStyle.innerHTML = '* {cursor: pointer!important}' + cursorStyle.id = 'cursor-style' + document.head.appendChild(cursorStyle) + }, + hidePointer() { + document.getElementById('cursor-style').remove() + } + } +} \ No newline at end of file diff --git a/scripts/toggling-saluter.js b/scripts/toggling-saluter.js new file mode 100644 index 0000000..f0c2b6f --- /dev/null +++ b/scripts/toggling-saluter.js @@ -0,0 +1,84 @@ +/** + * A saluter that toggles from Hello to Bye, World, and viceversa + */ +{ + // destructure it + const { View, Component } = Wings + + // instantiate a view linked to a canvas + const view = new View(document.getElementById('toggling-saluter')) + + // create a saluter abstraction + class TogglingSaluter extends Component { + constructor(hello, bye) { + super() + + // set the dimensions + this.width = 150 + this.height = 50 + + // set colors + this.backgroundColor = 'magenta' + this.borderColor = 'cyan' + this.borderWidth = 5 + + // set the text height for the salutation + this.textHeight = 16 + + // init the salutation value + this.hello = hello + this.bye = bye + this.salutation = this.hello + + // add behavior to toggle the salutation on click + this.on('MouseClick', () => { + if (this.salutation === this.hello) + this.salutation = this.bye + else + this.salutation = this.hello + }) + } + + render(context) { + // render the base layer + super.render(context) + + // render the saluter value centered in the component + context.fillStyle = 'white' + context.font = this.textHeight + 'px verdana' + const width = context.measureText(this.salutation).width + context.fillText( + this.salutation, + (this.width - width) / 2, + (this.height + this.textHeight) / 2 + ) + } + } + + // instantiate a saluter and center it at the top of the view + const saluter = new TogglingSaluter('Hello, World!', 'Bye, World!') + + saluter.x = (view.width - saluter.width) / 2 + saluter.y = (view.height / 3 - saluter.height) / 2 + + // add the saluter to the view + view.add(saluter) + + // instantiate a saluter and center it in the view + const saluter2 = new TogglingSaluter('Hola Mundo!', 'Adiós, Mundo!') + + saluter2.x = (view.width - saluter2.width) / 2 + saluter2.y = (view.height - saluter2.height) / 2 + + // add the saluter to the view + view.add(saluter2) + + // instantiate a saluter and center it at the bottom of the view + const saluter3 = new TogglingSaluter('Ciao Mondo!', 'Addio, Mondo!') + + saluter3.x = (view.width - saluter3.width) / 2 + saluter3.y = (view.height * 5 / 3 - saluter3.height) / 2 + + // add the saluter to the view + view.add(saluter3) +} \ No newline at end of file diff --git a/style.css b/style.css index ca835f5..fff1944 100644 --- a/style.css +++ b/style.css @@ -1,10 +1,13 @@ +@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap'); + :root { --width: 300px; --max-width: 500px; } * { - font-family: 'Courier New', Courier, monospace; + font-family: 'Source Code Pro', 'Courier New', Courier, monospace; + font-size: 1rem; } html { @@ -19,6 +22,7 @@ footer { flex-direction: column; justify-content: center; align-items: center; + margin: auto; } h1 { diff --git a/wings.js b/wings.js index aaf96ae..0336dd7 100644 --- a/wings.js +++ b/wings.js @@ -4,13 +4,17 @@ * An Object-Oriented Component-based UI Library for Canvas built in JavaScript (inspired by Java Swing). * * @author manuelbarzi - * @version 1.0.2 + * @version 1.1.0 */ const Wings = (() => { class Component { constructor() { this.x = this.y = this.width = this.height = 0 - this.mouse = {} + this.mouse = { + entered: false, + pressed: false, + dragging: false + } this.behaviors = [] this.parent = null this.children = [] @@ -58,14 +62,26 @@ const Wings = (() => { mouseMove(event) { if (this.visible) { if (this.isPointed(event.x, event.y)) { + if (!this.mouse.entered) { + this.mouse.entered = true + this.fireEvent('MouseEnter', event) + } + this.fireEvent('MouseMove', event) if (this.mouse.pressed) { this.mouse.dragging = true this.fireEvent('MouseDrag', event) } - } else if (this.mouse.dragging) - this.fireEvent('MouseDrag', event) + } else { + if (this.mouse.entered) { + this.mouse.entered = false + this.fireEvent('MouseLeave', event) + } + + if (this.mouse.dragging) + this.fireEvent('MouseDrag', event) + } if (this.children.length > 0) for (const child of this.children)