Appearance
Using CanvasEngine Without the Compiler
CanvasEngine provides a powerful template syntax that gets compiled to JavaScript functions. However, you can also use CanvasEngine directly without the compiler by using the core functions: h
, loop
, and cond
from the canvasengine
package.
This approach gives you more control and can be useful for:
- Dynamic template generation
- Integration with existing build systems
- Learning how the compiler works under the hood
- Performance-critical applications where you want to avoid compilation overhead
Core Functions
h(component, props?, children?)
Creates a component instance. This is the equivalent of JSX elements.
loop(iterable, callback)
Renders a list of items. This is the equivalent of @for
loops.
cond(condition, callback)
Conditionally renders content. This is the equivalent of @if
statements.
Components
Basic Component
Instead of writing:
html
<Canvas />
Use:
javascript
h('Canvas')
Component with Properties
Instead of writing:
html
<Canvas width="800" height="600" />
Use:
javascript
h('Canvas', { width: '800', height: '600' })
Dynamic Properties
Instead of writing:
html
<Canvas width={screenWidth} height={screenHeight} />
Use:
javascript
h('Canvas', { width: screenWidth, height: screenHeight })
Computed Properties
For reactive expressions, instead of writing:
html
<Canvas width={x * 2} />
Use:
javascript
h('Canvas', { width: computed(() => x() * 2) })
Complex Computed Properties
For multiple reactive variables:
html
<Canvas width={x * 2 * y} />
Use:
javascript
h('Canvas', { width: computed(() => x() * 2 * y()) })
Object Properties
Instead of writing:
html
<Sprite sheet={{
definition,
playing: "stand",
params: {
direction: "right"
},
onFinish
}} />
Use:
javascript
h('Sprite', {
sheet: {
definition,
playing: "stand",
params: {
direction: "right"
},
onFinish
}
})
Array Properties
Instead of writing:
html
<Canvas positions={[x, 20]} />
Use:
javascript
h('Canvas', { positions: computed(() => [x(), 20]) })
Event Handlers
Instead of writing:
html
<Sprite click={handleClick} />
Use:
javascript
h('Sprite', { click: handleClick })
Inline Event Handlers
Instead of writing:
html
<Sprite click={() => console.log('clicked')} />
Use:
javascript
h('Sprite', { click: () => console.log('clicked') })
Spread Operator
Instead of writing:
html
<Canvas ...props />
Use:
javascript
h('Canvas', props)
Component as Property
Instead of writing:
html
<Canvas child={<Sprite />} />
Use:
javascript
h('Canvas', { child: h('Sprite') })
Function Returning Component
Instead of writing:
html
<Canvas child={() => <Sprite />} />
Use:
javascript
h('Canvas', { child: () => h('Sprite') })
Children
Single Child
Instead of writing:
html
<Canvas>
<Sprite />
</Canvas>
Use:
javascript
h('Canvas', null, h('Sprite'))
Multiple Children
Instead of writing:
html
<Canvas>
<Sprite />
<Text />
</Canvas>
Use:
javascript
h('Canvas', null, [
h('Sprite'),
h('Text')
])
Loops
Basic Loop
Instead of writing:
html
@for (sprite of sprites) {
<Sprite />
}
Use:
javascript
loop(sprites, sprite => h('Sprite'))
Loop with Properties
Instead of writing:
html
@for (sprite of sprites) {
<Sprite x={sprite.x} y={sprite.y} />
}
Use:
javascript
loop(sprites, sprite => h('Sprite', { x: sprite.x, y: sprite.y }))
Loop with Index
Instead of writing:
html
@for ((sprite, index) of sprites) {
<Sprite key={index} />
}
Use:
javascript
loop(sprites, (sprite, index) => h('Sprite', { key: index }))
Loop with Object Property
Instead of writing:
html
@for (sprite of sprites.items) {
<Sprite />
}
Use:
javascript
loop(sprites.items, sprite => h('Sprite'))
Loop with Function Call
Instead of writing:
html
@for (sprite of getSprites()) {
<Sprite />
}
Use:
javascript
loop(getSprites(), sprite => h('Sprite'))
Nested Loops
Instead of writing:
html
@for (sprite of sprites) {
@for (other of others) {
<Sprite />
}
}
Use:
javascript
loop(sprites, sprite =>
loop(others, other => h('Sprite'))
)
Loop in Component
Instead of writing:
html
<Canvas>
@for (sprite of sprites) {
<Sprite />
}
</Canvas>
Use:
javascript
h('Canvas', null, loop(sprites, sprite => h('Sprite')))
Conditions
Basic Condition
Instead of writing:
html
@if (sprite) {
<Sprite />
}
Use:
javascript
cond(sprite, () => h('Sprite'))
Property-based Condition
Instead of writing:
html
@if (sprite.visible) {
<Sprite />
}
Use:
javascript
cond(sprite.visible, () => h('Sprite'))
Function-based Condition
Instead of writing:
html
@if (isVisible()) {
<Sprite />
}
Use:
javascript
cond(isVisible(), () => h('Sprite'))
Complex Conditions
Instead of writing:
html
@if (!sprite && other) {
<Sprite />
}
Use:
javascript
cond(computed(() => !sprite() && other()), () => h('Sprite'))
Multiple Conditions
Instead of writing:
html
@if (sprite) {
<Sprite />
}
@if (other) {
<Text />
}
Use:
javascript
[
cond(sprite, () => h('Sprite')),
cond(other, () => h('Text'))
]
Nested Conditions
Instead of writing:
html
@if (sprite.visible) {
@if (deep) {
<Sprite />
}
}
Use:
javascript
cond(sprite.visible, () =>
cond(deep, () => h('Sprite'))
)
Condition with Multiple Elements
Instead of writing:
html
@if (sprite.visible) {
<Sprite />
<Text />
}
Use:
javascript
cond(sprite.visible, () => [
h('Sprite'),
h('Text')
])
Combining Loops and Conditions
Condition in Loop
Instead of writing:
html
<Canvas>
@for (sprite of sprites) {
@if (sprite.visible) {
<Sprite />
}
}
</Canvas>
Use:
javascript
h('Canvas', null,
loop(sprites, sprite =>
cond(sprite.visible, () => h('Sprite'))
)
)
Multiple Loops
Instead of writing:
html
<Canvas>
@for (sprite of sprites) {
<Sprite />
}
@for (other of others) {
<Text />
}
</Canvas>
Use:
javascript
h('Canvas', null, [
loop(sprites, sprite => h('Sprite')),
loop(others, other => h('Text'))
])
Complete Example
Here's a complete example showing how to build a complex component without the compiler:
javascript
import { h, loop, cond, computed } from 'canvasengine';
function GameScene({ sprites, enemies, showUI }) {
return h('Canvas', { width: 800, height: 600 }, [
// Background
h('Sprite', { texture: 'background' }),
// Player sprites
loop(sprites, sprite =>
cond(sprite.visible, () =>
h('Sprite', {
x: sprite.x,
y: sprite.y,
texture: sprite.texture,
click: () => sprite.onClick()
})
)
),
// Enemies
loop(enemies, (enemy, index) =>
h('Sprite', {
key: index,
x: computed(() => enemy.x() + 10),
y: computed(() => enemy.y() + 10),
texture: enemy.texture,
tint: enemy.isHit ? 0xff0000 : 0xffffff
})
),
// UI overlay
cond(showUI, () =>
h('Container', null, [
h('Text', { text: 'Score: 100', x: 10, y: 10 }),
h('Text', { text: 'Lives: 3', x: 10, y: 30 })
])
)
]);
}
This approach gives you the full power of CanvasEngine while maintaining complete control over your component structure and logic.