Web Animation
2D (pixi.js)
PixiJS is a rendering library that will allow you to create rich, interactive graphics, cross platform applications, and games without having to dive into the WebGL API or deal with browser and device compatibility.
image (load by pixi.js)
前景
前景
後景
後景
z-index
前景1
後景1
主圖層 (2D 圖)
前景n
後景n
- opacity + pointer-event
- transition
- animation
約 500 張
可參照:
Why not use three.js ?
2D library (pixi.js) | 3D library (three.js) | |
---|---|---|
色彩、光線、陰影 | 依照圖片 | 自行校正 |
操作自由度 (視角) | 差(依照圖片) | 極高(自定義) |
檔案大小 | 極大 | 小 |
Compared with
- 操作自由度:使用情境不需要
- 檔案大小:可優化
Tools
png 壓縮神器:
500張/月 (需帳號)
圖片無損壓縮:
無限制
png to jpg
Bulk resize images
也可 Google...
500 張共 973 MB (3600x1620 png)
Result:
Resize 1/2 for desktop
Resize 1/6 for mobile
Compressed by tinypng
desktop: 62.4 MB (1800x810 png)
mobile: 10.2 MB (600x270 png)
Setup
via npm
There is no default export. The correct way to import PixiJS is:
import * as PIXI from 'pixi.js'
npm install pixi.js
Get Started
- Application (Root Container)
- Container
- Loader
- Sprite
- AnimatedSprite
...
<RootContainer>
<Container>
<Sprite />
</Container>
<Container>
</Container>
</RootContainer>
const app = new PIXI.Application({
// settings
});
document.body.appendChild(app.view);
// app.view = canvas
const container = new PIXI.Container();
app.stage.addChild(container);
// app.stage = root container
What is Sprite ?
The Sprite object is the base for all textured objects that are rendered to the screen
Ex: image
In 3D world, a sprite is an image that always face to camera
Further more ...
What is Sprite ?
const sprite = PIXI.Sprite.from('assets/image.png');
or
PIXI.Loader.shared.add("assets/spritesheet.json").load(setup);
function setup() {
let sheet = PIXI.Loader.shared.resources["assets/spritesheet.json"].spritesheet;
let sprite = new PIXI.Sprite(sheet.textures["image.png"]);
...
}
AnimatedSprite
const alienImages = [
"image_sequence_01.png",
"image_sequence_02.png",
"image_sequence_03.png",
"image_sequence_04.png",
];
const textureArray = alienImages.forEach(imgName => (
PIXI.Texture.from(imgName)
));
const animatedSprite = new PIXI.AnimatedSprite(textureArray);
A texture stores the information that represents an image or part of an image.
AnimatedSprite + Loader
const alienImages = [
"image_sequence_01.png",
"image_sequence_02.png",
"image_sequence_03.png",
"image_sequence_04.png",
];
const animateLoader = new PIXI.Loader();
alienImages.forEach((imgName, idx) => {
animateLoader
.add(`frame${idx + 1}`, imgName);
});
/* async */
animateLoader.load((loader, resources) => {
const resourceKeys = Object.keys(resources);
const textures = resourceKeys.map(key => res[key].texture);
const animatedSprite = new PIXI.AnimatedSprite(textures);
});
AnimatedSprite + Loader
import img1 from './static/image_sequence_01.png';
import img2 from './static/image_sequence_02.png';
import img3 from './static/image_sequence_03.png';
import img4 from './static/image_sequence_04.png';
const alienImages = [img1, img2, img3, img4];
const animateLoader = new PIXI.Loader();
alienImages.forEach((imgName, idx) => {
animateLoader
.add(`frame${idx + 1}`, imgName);
});
/* async */
animateLoader.load((loader, resources) => {
const resourceKeys = Object.keys(resources);
const textures = resourceKeys.map(key => res[key].texture);
const animatedSprite = new PIXI.AnimatedSprite(textures);
});
AnimatedSprite
import img1 from './static/image_sequence_01.png';
import img2 from './static/image_sequence_02.png';
import img3 from './static/image_sequence_03.png';
import img4 from './static/image_sequence_04.png';
const alienImages = [img1, img2, img3, img4];
const textures = alienImages.map(imgName => (
PIXI.Texture.from(imgName)
));
const animatedSprite = new PIXI.AnimatedSprite(textures);
hmmm ... ?
Files compiled by webpack with hash filename
Already cached in memories ?
AnimatedSprite
import img1 from './static/image_sequence_01.png';
import img2 from './static/image_sequence_02.png';
import img3 from './static/image_sequence_03.png';
import img4 from './static/image_sequence_04.png';
const alienImages = [img1, img2, img3, img4];
const textureFragments = alienImages.map(imgName => ({
texture: PIXI.Texture.from(imgName),
time: 66.6, // ms (15fps)
}));
const animatedSprite = new PIXI.AnimatedSprite(textureFragments);
Each fragment (image) can define "time"
AnimatedSprite
animatedSprite.play();
animatedSprite.gotoAndPlay(0);
animatedSprite.gotoAndStop(0);
animatedSprite.stop();
commands:
Flow control
animatedSprite.onFrameChange = () => {};
animatedSprite.onComplete = () => {};
Event emitter
Flow control
export const ANIMATION_1_KEY = 'F/ANIMATION_1';
export const ANIMTION_2_KEY = 'F/ANIMATION_2';
export const animationScenes = [{
key: ANIMATION_1_KEY,
tab: tabs[0],
startFrame: 1,
endFrame: 30,
}, {
key: ANIMATION_2_KEY,
tab: tabs[1],
startFrame: 31,
endFrame: 50,
}];
animatedSprite.onComplete = () => {
emitter.emit(
CURRENT_ANIMATION_COMPLETED,
KEY_NAME,
);
};
image1 ~ image30
image31 ~ image50
Flow control
setTimeout ?
time (s)
無法保證時間是準確的,除非這秒數不用那麼精準
主動畫
onComplete
動畫1
動畫2
動畫3
動畫4
time (s)
主動畫
動畫1
動畫2
動畫3
動畫4
t
Moreover...
Your webGL may cause "Memory Leak"
webgl: context_lost_webgl: losecontext: context lost
Then, your browser will lost webGL context, unless you refresh your browser
Optimization (mobile)
z-index
前景1
後景1
主圖層 (2D 圖)
前景n
後景n
手機版只會照順序顯示
(不能從第1幕直接跳到第n幕)
許多幕也包含了大量圖片,所以儘可能減少元素同時 render 在 DOM 上
Optimization (mobile)
Scene Controller
主
圖
層
8
7
6
5
4
3
2
1
8
7
6
5
4
3
2
1
(scene: 1)
return null
return null
Optimization (mobile)
Scene Controller
主
圖
層
8
7
6
5
4
3
2
1
8
7
6
5
4
3
2
1
(scene: 2)
Optimization (mobile)
Scene Controller
主
圖
層
8
7
6
5
4
3
2
1
8
7
6
5
4
3
2
1
(scene: 3)
Thanks
Web Animation
By Travor Lee
Web Animation
2D (pixi.js)
- 216