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.