-
-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
--- | ||
title: HTML, CSS, and JavaScript | ||
slug: /html | ||
section: User Interface | ||
--- | ||
|
||
HTML is super great at building UI's for the web, there are many popular front end frameworks and tool kits that will deliver highly polished UIs, so we recommend that first. | ||
|
||
Please consider html based first, but if the UI makes more sense in-canvas look to [screen elements](/docs/screen-elements). | ||
|
||
Advantages of HTML | ||
* Accessibility | ||
* Built in keyboard/pointer events | ||
* Powerful layout engine | ||
* CSS styling | ||
|
||
## Using HTML with Excalibur's Canvas | ||
|
||
A common technique for building UI wit canvas based games is overlaying HTML elements over the canvas. | ||
|
||
Check out this full demo [excaliburjs/sample-html](https://github.com/excaliburjs/sample-html) | ||
|
||
```html | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<style> | ||
html,body { | ||
padding: 0; | ||
margin: 0; | ||
background-color: black; | ||
} | ||
/* optionally center the game */ | ||
#game { | ||
position: absolute; | ||
left: 50%; | ||
top: 50%; | ||
transform: translate(-50%, -50%); | ||
} | ||
.menu { | ||
position: absolute; | ||
top: 10px; | ||
left: 10px; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<main> | ||
<canvas id="game"></canvas> | ||
<div class="menu"> | ||
<button>Add Unit</button> | ||
</div> | ||
</main> | ||
</body> | ||
</html> | ||
``` | ||
|
||
When positioning elements over the Excalibur canvas, it is important to set `pointer-events: none` on elements you want to pass through to Excalibur. For elements you want to receive clicks/taps be sure to set `pointer-events: all`. | ||
|
||
|
||
## Positioning HTML Elements with Excalibur | ||
|
||
Excalibur provides an API for converting game positions in world/screen space to page coordinates. | ||
|
||
```typescript | ||
const engine = new ex.Engine({...}); | ||
|
||
// Excalibur camera is centered at (100, 100) | ||
engine.currentScene.camera.pos = ex.vec(100, 100); | ||
|
||
// Finds the absolution page position that corresponds to the excalibur position (100, 100) | ||
const pagePositionFromWorld = engine.screen.worldToPageCoordinates(ex.vec(100, 100)); | ||
|
||
// Finds the absolute page position that corresponds to the top left of the excalibur canvas, screen (0, 0) | ||
const pagePositionFromScreen = engine.screen.screenToPageCoordinates(ex.vec(0, 0)); | ||
|
||
// Use pagePositions in page coordinates to position HTML elements | ||
// Setting CSS variables is a convenient way to do this | ||
document.documentElement.style.setProperty('--pointer-x', evt.pagePos.x.toString() + 'px'); | ||
document.documentElement.style.setProperty('--pointer-y', evt.pagePos.y.toString() + 'px'); | ||
``` | ||
|
||
Then to position any elements reference the CSS variables in your CSS | ||
|
||
```css | ||
.menu { | ||
position: absolute; | ||
/* position menu on click */ | ||
left: var(--pointer-x); | ||
top: var(--pointer-y); | ||
} | ||
``` | ||
|
||
## Scaling between CSS & Excalibur Pixels | ||
|
||
It is useful in CSS to scale your elements to match the Excalibur game canvas, this is done by calculating the ratio between between Excalibur pixels and browser pixels. | ||
|
||
```typescript | ||
const calculateExPixelConversion = (screen: ex.Screen) => { | ||
const origin = screen.worldToPageCoordinates(Vector.Zero); | ||
const singlePixel = screen.worldToPageCoordinates(vec(1, 0)).sub(origin); | ||
const pixelConversion = singlePixel.x; | ||
document.documentElement.style.setProperty('--pixel-conversion', pixelConversion.toString()); | ||
} | ||
|
||
// Update pixel conversion on resize | ||
game.screen.events.on('resize', () => calculateExPixelConversion(game.screen)); | ||
|
||
// Set initial conversion | ||
game.start().then(() => { | ||
calculateExPixelConversion(game.screen); | ||
}); | ||
``` | ||
|
||
|
||
One of the lowest effort options is to apply a CSS transform to the container of your HTML UI based on the excalibur pixel conversion. | ||
|
||
```css | ||
.excalibur-scale { | ||
/* transform from the top left of the element */ | ||
transform-origin: 0 0; | ||
/* scale the ui */ | ||
transform: scale(var(--pixel-conversion), var(--pixel-conversion)); | ||
} | ||
``` | ||
|
||
Another option is to manually apply conversion to specific CSS properties. | ||
|
||
```css | ||
.text { | ||
/* Convert to excalibur 24px from page 24px */ | ||
font-size: calc(24px * var(--pixel-conversion)); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
--- | ||
title: Screen Elements | ||
slug: /screen-elements | ||
section: User Interface | ||
--- | ||
|
||
Please consider [html based first](#html-based-ui) but if the UI makes more sense in-canvas look to [screen elements](#screen-elements). | ||
|
||
## Screen Elements | ||
|
||
In Excalibur, if you want to display something like a HUD element or UI element inside the Excalibur canvas, you can create an instance of [[ScreenElement]]. A screen element has the following semantics that differ from a regular [actor](/docs/actors): | ||
|
||
- They automatically [capture pointer events](/docs/input#actor-pointer-events) | ||
- They do not participate in collisions | ||
- They appear above all "normal" actors in a [scene](/docs/scenes) | ||
- Invoking [[ScreenElement.contains]] will check against [screen coordinates](/docs/engine#screen-coordinates) by default. | ||
|
||
Other than that, they are the same as normal actors where you can assign drawings, perform actions, etc. | ||
|
||
```ts | ||
import * as ex from 'excalibur' | ||
import Resources from './resources' | ||
|
||
class StartButton extends ex.ScreenElement { | ||
constructor() { | ||
super({ | ||
x: 50, | ||
y: 50, | ||
}) | ||
} | ||
|
||
onInitialize() { | ||
this.graphics.add('idle', Resources.StartButtonBackground) | ||
this.graphics.add('hover', Resources.StartButtonHovered) | ||
|
||
this.on('pointerup', () => { | ||
alert("I've been clicked") | ||
}) | ||
|
||
this.on('pointerenter', () => { | ||
this.graphics.show('hover') | ||
}) | ||
|
||
this.on('pointerleave', () => { | ||
this.graphics.show('idle') | ||
}) | ||
} | ||
} | ||
|
||
game.add(new StartButton()) | ||
game.start() | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
--- | ||
title: Web Fonts | ||
slug: /web-fonts | ||
section: User Interface | ||
--- | ||
|
||
When building UIs in order to prevent a flash of unstyled content (a moment of default font/styling before the font loads) Excalibur has a built in resource type to help with that. | ||
|
||
## Using FontSource | ||
|
||
Using a [[FontSource]] will ensure the font is loaded and the font face is ready before the game starts! | ||
|
||
```typescript | ||
const fontSource = new ex.FontSource('/my-font.ttf', 'My Font') | ||
loader.addResource(fontSource) | ||
|
||
game.start(loader).then(() => { | ||
const font = fontSource.toFont() // returns ex.Font | ||
}) | ||
``` | ||
|
||
Font options can be defined either at the source or at the `toFont()` call. If defined in both, `toFont(options)` will | ||
override the options in the [[FontSource]]. | ||
|
||
```typescript | ||
const fontSource = new ex.FontSource('/my-font.ttf', 'My Font', { | ||
filtering: ex.ImageFiltering.Pixel, | ||
size: 16, // set a default size | ||
}) | ||
const font = fontSource.toFont({ | ||
// override just the size | ||
size: 20, | ||
}) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"label": "User Interface", | ||
"position": 5, | ||
"link": { | ||
"type": "generated-index", | ||
"description": "5 minutes to learn the most important Excalibur.js concepts." | ||
} | ||
} |