算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
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 快照 · 自动分类 · 以原作者文档为准
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.
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.
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 }))
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.
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()
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.
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.
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.
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.
await app.init() before touching app.canvas, app.stage or app.screen.sortableChildren is on.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.@pixi/react package instead of mixing imperative setup into components.