界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
设计与创意 · SKILL.md

pixijs

pixijs

【设计与创意】Renders fast 2D graphics in the browser with PixiJS 8, a WebGL and WebGPU engine for sprites, text, filters, masks and particles. Use when a user asks to build a 2D game, an interactive visualization, an animated banner, a sprite animation from a spritesheet, a particle effect, or to speed up a canvas scene with thousands of objects.

独立 GitHub 快照 · 自动分类 · 以原作者文档为准

Tterminalskills / skillsApache-2.0

技能说明

GitHub

name: pixijs description: >- Renders fast 2D graphics in the browser with PixiJS 8, a WebGL and WebGPU engine for sprites, text, filters, masks and particles. Use when a user asks to build a 2D game, an interactive visualization, an animated banner, a sprite animation from a spritesheet, a particle effect, or to speed up a canvas scene with thousands of objects. license: Apache-2.0 compatibility: 'PixiJS 8.x (pixi.js on npm), any modern browser with WebGL2 or WebGPU; Node.js 20+ for the build tooling.' metadata: author: terminal-skills version: "1.1.0" category: development tags: - 2d-rendering - webgl - graphics - animation - game-development repository: https://github.com/pixijs/pixijs

PixiJS — High-Performance 2D WebGL Renderer

Overview

PixiJS is a 2D rendering library for the web. It draws a scene graph of sprites, text, vector graphics and meshes on the GPU with WebGL (default) or WebGPU, batches draw calls automatically, and adds an asset loader, a ticker, a pointer-event system and filters. It is a renderer, not a full game engine: physics, audio and game logic come from other libraries.

This skill targets PixiJS 8 (current: 8.22). The v8 API differs from v7: the application is created with new Application() then await app.init(...), shapes use rect().fill(), and ParticleContainer holds lightweight Particle objects. There is no 2D-canvas fallback renderer; the CanvasRenderer is listed as "coming soon", so a device needs WebGL or WebGPU.

Instructions

Install and set up

npm create pixi.js@latest      # scaffold a project (Vite template recommended)
npm install pixi.js            # or add to an existing project
// src/main.ts
import { Application, Assets, Sprite } from 'pixi.js'

(async () => {                  // wrap in a function: top-level await breaks some Vite production builds
  const app = new Application()
  await app.init({
    background: '#1a1a2e',
    resizeTo: window,
    antialias: false,                         // crisp pixel art
    resolution: window.devicePixelRatio,
    autoDensity: true,                        // keeps CSS size correct on retina screens
  })
  document.body.appendChild(app.canvas)       // v8: app.canvas, not app.view

  await Assets.load([
    { alias: 'hero', src: '/sprites/hero.png' },
    { alias: 'tileset', src: '/sprites/tileset.png' },
  ])

  const hero = Sprite.from('hero')            // works for an alias that is already loaded
  hero.anchor.set(0.5)
  hero.position.set(app.screen.width / 2, app.screen.height / 2)
  app.stage.addChild(hero)

  app.ticker.add((ticker) => {
    hero.rotation += 0.01 * ticker.deltaTime  // v8 passes the Ticker, not a number
  })
})()

preference: 'webgpu' in init asks for the WebGPU renderer; the docs call it experimental and recommend WebGL for production.

Containers and layers

import { Container } from 'pixi.js'

const world = new Container()
const effects = new Container()
const hud = new Container()
app.stage.addChild(world, effects, hud)       // later children draw on top

world.sortableChildren = true                 // depth sort by zIndex
app.ticker.add(() => enemies.forEach((e) => { e.zIndex = e.y }))

Filters

import { BlurFilter, ColorMatrixFilter, DisplacementFilter, Sprite } from 'pixi.js'

background.filters = [new BlurFilter({ strength: 4 })]
const grayscale = new ColorMatrixFilter()
grayscale.desaturate()
deadEnemy.filters = [grayscale]

const map = Sprite.from('displacement-map')
map.texture.source.style.addressMode = 'repeat'
waterLayer.filters = [new DisplacementFilter({ sprite: map, scale: 20 })]
app.stage.addChild(map)                        // the map sprite must be on the stage to be sampled

Release a filter's memory with container.filters = null. Blend filters such as HardMixBlend need import 'pixi.js/advanced-blend-modes'. More effects live in the separate pixi-filters package.

Spritesheet animation

import { AnimatedSprite, Assets } from 'pixi.js'

const sheet = await Assets.load('/sprites/hero.json')   // TexturePacker or AssetPack JSON
const walk = new AnimatedSprite(sheet.animations['walk'])
walk.animationSpeed = 0.15
walk.play()
app.stage.addChild(walk)

walk.textures = sheet.animations['attack']               // switch clips
walk.play()

Text and Graphics

import { Assets, FillGradient, Graphics, Text } from 'pixi.js'

await Assets.load({ src: '/fonts/press-start-2p.woff2', data: { family: 'Press Start 2P' } })

const scoreText = new Text({
  text: 'Score: 0',
  style: {
    fontFamily: 'Press Start 2P',
    fontSize: 24,
    fill: new FillGradient({ type: 'linear', colorStops: [{ offset: 0, color: '#ffffff' }, { offset: 1, color: '#00ff88' }] }),
    stroke: { color: '#000000', width: 4 },
    dropShadow: { color: '#000000', distance: 2 },
  },
})

const healthBar = new Graphics()
healthBar.rect(0, 0, 200, 20).fill(0x333333)
healthBar.rect(2, 2, 196 * hp, 16).fill(0x00ff00)   // v8: shape first, then fill()/stroke()

Gradients are FillGradient objects in v8; an array of colours as fill was v7 syntax. Changing text.text re-rasterizes the text, so for a score that changes every frame use BitmapText.

Many objects: ParticleContainer

import { Particle, ParticleContainer, Texture } from 'pixi.js'

const sparks = new ParticleContainer({ dynamicProperties: { position: true, rotation: true, vertex: false, color: false } })
for (let i = 0; i < 50_000; i++) {
  sparks.addParticle(new Particle({ texture: Texture.from('particle'), x: Math.random() * 800, y: Math.random() * 600 }))
}
app.stage.addChild(sparks)

Particles have no children, events or filters; use addParticle/removeParticle instead of addChild. Properties not listed as dynamic are uploaded only when you call sparks.update(). The API is marked experimental.

Examples

Example 1: Animated hero in a platformer

User prompt: "Load hero.json and make the character walk, then switch to attack when I press space."

Load the sheet with Assets.load('/sprites/hero.json'), create an AnimatedSprite from sheet.animations['walk'], call play(), and on keydown for Space set textures to sheet.animations['attack'], loop = false, then play() and restore walk in onComplete. Result: the sprite loops the walk cycle and plays a single attack animation on each key press.

Example 2: Rain of 100,000 particles

User prompt: "My falling-snow effect with 20,000 Sprites runs at 20 fps. Make it smooth."

Replace the Sprite objects with Particle objects in one ParticleContainer with dynamicProperties: { position: true }, and in the ticker update each particle.y += speed * ticker.deltaTime, wrapping at the bottom. Result: one batched draw call; 100,000 flakes stay at the display refresh rate on a typical laptop.

Guidelines

  • Always await app.init() before touching app.canvas, app.stage or app.screen.
  • Keep layers in separate containers; draw order inside a container is child order unless sortableChildren is on.
  • Spritesheets batch best: sprites that share a texture atlas draw together, and mixing sprite, graphics and text objects alternately breaks batches.
  • Filters, masks and blend modes cost render passes. Use few, and prefer rectangle masks (scissor) over sprite masks.
  • Textures are garbage-collected after about 3600 idle frames; call destroy() on objects you remove. sprite.destroy(true) also destroys the texture, which breaks other sprites that share it, so use sprite.destroy() for shared textures and Assets.unload(alias) to free an asset.
  • Pool objects that appear and disappear often (bullets, particles) instead of creating them every frame.
  • For a plain static page or a simple chart, SVG or Canvas 2D is lighter than PixiJS.
  • Using React? Use the @pixi/react package instead of mixing imperative setup into components.
我的收藏