Skip to content

Use Viewport component

Common example:

html
<Viewport worldWidth="2000" worldHeight="2000" clamp={ {direction: 'all'} } />

Properties

You can use all properties from Display Object

Viewport Options

The Viewport component supports several options inherited from pixi-viewport:

OptionTypeDescription
dragboolean or objectEnable dragging the viewport
wheelboolean or objectEnable mouse wheel scrolling
pinchboolean or objectEnable pinch to zoom
decelerateboolean or objectEnable deceleration (momentum) after dragging
clampobjectRestrict viewport movement

Example with options:

html
<Viewport 
  worldWidth="2000" 
  worldHeight="2000" 
  drag={true}
  wheel={true}
  pinch={true}
  decelerate={true}
  clamp={ {direction: 'all'} } 
/>

Viewport Events

The Viewport component supports all events from pixi-viewport:

EventDescription
bounce-x-endFired when bounce on the x-axis ends
bounce-x-startFired when bounce on the x-axis starts
bounce-y-endFired when bounce on the y-axis ends
bounce-y-startFired when bounce on the y-axis starts
clickedFired when viewport is clicked
drag-endFired when drag ends
drag-startFired when drag starts
frame-endFired when frame ends
mouse-edge-endFired when mouse-edge ends
mouse-edge-startFired when mouse-edge starts
movedFired when viewport moves
moved-endFired when viewport stops moving
pinch-endFired when pinch ends
pinch-startFired when pinch starts
snap-endFired when snap ends
snap-startFired when snap starts
snap-zoom-endFired when snap-zoom ends
snap-zoom-startFired when snap-zoom starts
wheel-scrollFired when mouse wheel is scrolled
zoomedFired when viewport is zoomed
zoomed-endFired when viewport stops zooming

Example with event:

html
<Viewport 
  worldWidth="2000" 
  worldHeight="2000"
/>

Viewport Follow Directive

The viewportFollow directive allows an element to be followed by the viewport. When applied, the viewport will automatically center on the element as it moves.

This directive must be used within a Viewport component context.

Usage

html
<Viewport worldWidth="2000" worldHeight="2000" clamp={ {direction: 'all'} }>
    <Rect viewportFollow x={0} y={0} width={100} height={100} color="red" />
</Viewport>

<script>
    const viewportFollow = true // null to disable
</script>

In this example, the red rectangle will be followed by the viewport, keeping it centered in the view as it moves around within the 2000x2000 world space.

Usage with options

html
<Viewport worldWidth="2000" worldHeight="2000" clamp={ {direction: 'all'} }>
    <Rect viewportFollow x={0} y={0} width={100} height={100} color="red" />
</Viewport>

<script>
    const viewportFollow = {
        speed: 0.1,
        acceleration: 0.1,
        radius: 100
    }
</script>
  • speed number 0 optional to follow in pixels/frame (0=teleport to location)
  • acceleration number optional set acceleration to accelerate and decelerate at this rate; speed cannot be 0 to use acceleration
  • radius number optional radius (in world coordinates) of center circle where movement is allowed without moving the viewport * @returns {Viewport} this

Requirements

  • Must be used on an element that is a child of a Viewport component
  • The parent Viewport component must have defined dimensions (worldWidth and worldHeight)

WARNING

Important: When an element has viewportFollow set to true, it will automatically disable the dragging functionality of the parent viewport. This is by design as viewport dragging would conflict with the following behavior.

Performance Optimization

When rendering large numbers of elements (1000+) inside a Viewport, consider these optimization strategies:

1. Viewport Culling

The viewportCull directive automatically hides elements outside the visible area, significantly reducing render overhead:

html
<Viewport worldWidth="5000" worldHeight="5000" drag={true}>
  <Container viewportCull={true}>
    @for (item of items) {
      <Sprite image={@item.image} x={@item.x} y={@item.y} />
    }
  </Container>
</Viewport>

TIP

Culling is most effective when only a small portion of the world is visible at once. If most elements are always on screen, culling overhead may not be worth it.

2. Reduce Signal Granularity

Instead of creating signals for every animated property, use direct Pixi manipulation in tick():

html
<script>
  import { tick, mount } from 'canvasengine';
  
  // BAD: 5 signals per element = expensive with 1000+ elements
  const items = signal(data.map(d => ({
    x: signal(d.x),
    y: signal(d.y),
    rotation: signal(0),    // Don't do this for animations!
    alpha: signal(1),       // Don't do this for animations!
    scale: signal(1),       // Don't do this for animations!
  })));
  
  // GOOD: Only position signals, animate imperatively
  const items = signal(data.map(d => ({
    x: signal(d.x),
    y: signal(d.y),
    rotationSpeed: Math.random() * 0.1,
  })));
  
  tick((tickValue, element) => {
    const viewport = element.componentInstance.children[0];
    viewport.children.forEach((sprite, i) => {
      sprite.rotation += items()[i].rotationSpeed;
    });
  });
</script>

3. Throttle Updates

For non-critical animations, update every N frames instead of every frame:

html
<script>
  tick((tickValue) => {
    if (tickValue.frame % 2 !== 0) return; // Skip every other frame
    
    // Animation logic here
  });
</script>

4. Level of Detail (LOD)

Reduce animation complexity based on element count:

html
<script>
  tick((tickValue, element) => {
    const count = items().length;
    
    sprites.forEach(sprite => {
      sprite.rotation += 0.01; // Always animate rotation
      
      if (count < 3000) {
        sprite.alpha = Math.sin(tickValue.frame * 0.05); // Alpha only below 3k
      }
      if (count < 1000) {
        sprite.scale.set(1 + Math.sin(tickValue.frame * 0.02) * 0.1); // Scale only below 1k
      }
    });
  });
</script>

Performance Summary

Element CountRecommended Strategy
< 500Full reactivity (signals for all props)
500 - 2000Imperative animations + position signals
2000 - 5000+ Viewport culling + throttled updates
> 5000+ LOD + consider chunked rendering

Common Properties

PropertyTypeDescription
xnumberX-coordinate position of the display object.
ynumberY-coordinate position of the display object.
widthnumberWidth of the display object.
heightnumberHeight of the display object.
scaleobjectScale of the display object.
anchorobjectAnchor point of the display object.
skewobjectSkew of the display object.
tintnumberTint color of the display object.
rotationnumberRotation of the display object in radians.
anglenumberRotation of the display object in degrees.
zIndexnumberZ-index of the display object.
roundPixelsbooleanWhether to round pixel values.
cursorstringCursor style when hovering over the display object.
visiblebooleanVisibility of the display object.
alphanumberAlpha transparency of the display object.
pivotobjectPivot point of the display object.
filtersarrayFilters applied to the display object.
maskOfElementElement that this display object masks.
blendModestringBlend mode for rendering.
filterAreaobjectFilter area for rendering.

Layout Properties

Pour obtenir la documentation complète et détaillée sur toutes les propriétés de mise en page, consultez la documentation officielle de PixiJS Layout.

Sizing and Dimensions

PropertyTypeDescription
widthnumber/stringWidth of the display object. Accepts pixels or percentage (e.g. '50%').
heightnumber/stringHeight of the display object. Accepts pixels or percentage (e.g. '50%').
minWidthnumber/stringMinimum width the object can shrink to.
minHeightnumber/stringMinimum height the object can shrink to.
maxWidthnumber/stringMaximum width the object can expand to.
maxHeightnumber/stringMaximum height the object can expand to.
aspectRationumberRatio between width and height (e.g. 1.5 for 3:2 ratio).

Flex Layout

PropertyTypeDescription
flexDirectionstringDirection of flex items. Values: 'row', 'column', 'row-reverse', 'column-reverse'.
flexWrapstringWhether items wrap. Values: 'wrap', 'nowrap', 'wrap-reverse'.
justifyContentstringAlignment along main axis. Values: 'flex-start', 'flex-end', 'center', 'space-between', 'space-around'.
alignItemsstringAlignment along cross axis. Values: 'flex-start', 'flex-end', 'center', 'stretch', 'baseline'.
alignContentstringAlignment of lines with multiple items. Values: 'flex-start', 'flex-end', 'center', 'stretch', 'space-between', 'space-around'.
alignSelfstringOverride of parent's alignItems for specific item.
flexGrownumberGrow factor of item relative to other items.
flexShrinknumberShrink factor of item relative to other items.
flexBasisnumber/stringInitial size of item before flex growing/shrinking.
gapnumber/objectGap between items.
rowGapnumberGap between rows.
columnGapnumberGap between columns.

Positioning

PropertyTypeDescription
positionTypestringType of positioning. Values: 'relative', 'absolute', 'static'.
topnumber/stringDistance from the top edge.
rightnumber/stringDistance from the right edge.
bottomnumber/stringDistance from the bottom edge.
leftnumber/stringDistance from the left edge.

Spacing, Margins and Borders

PropertyTypeDescription
marginnumber/arraySpace outside border box. Can be single value or array for different sides.
paddingnumber/arraySpace inside border box. Can be single value or array for different sides.
bordernumber/arrayBorder width. Can be single value or array for different sides.

Object Fitting and Alignment

PropertyTypeDescription
objectFitstringHow object is resized to fit layout box. Values: 'contain', 'cover', 'fill', 'none', 'scale-down'.
objectPositionstringAnchor point of object inside layout box. E.g. 'center', 'top left'.
transformOriginstringPivot point for rotation and scaling of layout box.

Shadow

PropertyTypeDescription
blurnumberBlur strength.
colornumberColor of the shadow.
offsetobjectOffset of the shadow.
qualitynumberQuality of the shadow.

Hook before destroy

html
<script>
  import {
    signal,
    animatedSignal,
    effect,
    animatedSequence,
  } from "canvasengine";
  import MyViewport from "./viewport.ce";
  
  let bool = signal(true)
  const opacity = animatedSignal(1, { duration: 500 });

  const click = async () => {
    bool.set(!bool())
  }

  const beforeDestroy = async () => {
    await animatedSequence([
      () => opacity.set(0),
    ])
    console.log("before destroy")
  }
</script>


<Canvas antialias={true}>
     <Container onBeforeDestroy={beforeDestroy}>
        @if (bool) {
            <Rect width={300} height={300} color="red" alpha={opacity} click />
        }
    </Container>
</Canvas>