使用pixi做粒子聚合文字的效果[2] - 青草效果

说明

基于本系列的第一批做一点参数调整,就能出来很好玩的效果。而将要做的青草效果,也是为了减少粒子使用数量产生的以外效果。

如果粒子尺寸放大,则填充一个图形需要的粒子总数目肯定是要减少的。
因此我将粒子数量从原来的 1000 * 4 降到 500 * 4。
使用了细长的粒子之后,又增加了随机的角度,这样聚合的时候更像一个杂乱的草堆

1
2
<script src="https://cdn.bootcdn.net/ajax/libs/pixi.js/6.0.2/browser/pixi.js"></script>
<div id="preview-box" style="text-align:center"></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 简单的数组随机取值函数
function random(arr) {
return arr[Math.floor(Math.random() * arr.length)]
}

let
scale = 4, // 舞台放大比例 会影响粒子数量 num; 影响抖动强度 shake;
num = 500 * scale, // 粒子数量
strArr = ['Hello', 'World', 'I\'am','pixi.js'],
rate = 180, // 帧 60帧 相当于 1s
frame = 0, // 运行的总帧数
times = 0, // 动画的总次数
joinTimes = 0, // 聚合动画的次数
color = '#91ff00',
shake = { x: .7, y: .4 }, // x轴 和 y轴 的抖动强度
game = new PIXI.Application({
width: 120 * scale,
height: 28 * scale,
antialias: true,
backgroundColor: 0x333333
});

// 添加pixi舞台到页面上展示
game.view.style.maxWidth = '100%'
document.getElementById('preview-box').appendChild(game.view)

// 生成图形 方便后续提取有色位置
// 也可以用png图片作为shape传入 这里就不做演示了
let shape = strArr.map(str => {
let s = new PIXI.Text(str, {
fontSize: 16 * scale,
fontStyle: 'italic',
fontWeight: 'bold',
letterSpacing: 2
})
s.name = str
s.anchor.set(0.5)
s.position.set(game.renderer.width / 2, game.renderer.height / 2)
return s
})

// 生成粒子
let paticles = new PIXI.ParticleContainer(num);
for (let n = 0; n < num; n++) {
let d = new PIXI.Sprite(PIXI.Texture.WHITE)
d.name = n
d.width = 1
d.height = 7
d.anchor.set(0)
d.angle = Math.random() * 360
d.position.set(game.renderer.width * Math.random(), game.renderer.height * Math.random())
d.dx = d.x
d.dy = d.y
d.alpha = Math.random()
if (typeof color === 'string') d.tint = PIXI.utils.string2hex(color)
if (Array.isArray(color)) {
let c = random(color)
d.tint = PIXI.utils.string2hex(c)
}
paticles.addChild(d)
}
game.stage.addChild(paticles)

// ticker 进行动画执行
PIXI.Ticker.shared.add(() => {

frame++
paticles.children.forEach(d => {
if (d.dx !== d.x) {
d.x += (d.dx - d.x) / 20
}
if (d.dy !== d.y) {
d.y += (d.dy - d.y) / 20
}
})

// 每隔一定帧数做动画
if (frame % rate === 0) {
if (times % 2 === 0) {
console.log(`聚合形状\t${shape[joinTimes % shape.length].name}`)
join(game.renderer, shape[joinTimes % shape.length], paticles)
joinTimes++
} else {
leave(game.renderer, paticles)
}
times++;
}

})

// 聚合动画
function join(renderer, sprite, paticles) {
let { x: ox, y: oy, width, height } = sprite.getBounds();
game.stage.removeChild(game.stage.getChildByName('shape'))

// 提取贴图数据
let shadow = new PIXI.Container()
shadow.addChild(sprite)
let dotData = renderer.extract.pixels(shadow);
shadow.destroy()

// 提取有颜色的点
let colorPos = []
for (let y = 0; y < height; y += 1) {
for (let x = 0; x < width; x += 1) {
let alpha = dotData[y * width * 4 + x * 4 + 3];
if (alpha > 1) {
colorPos.push({
x: ox + x,
y: oy + y
})
}
}
}

// 将所有粒子随机分配到目标点上
paticles.children.forEach(d => {
let pos = random(colorPos)
d.dx = pos.x
d.dy = pos.y
})

// 销毁
dotData = null
colorPos = null
}

// 离散
function leave(renderer, paticles) {
paticles.children.forEach(d => {
d.dx = renderer.width * Math.random()
d.dy = renderer.height * Math.random()
})
}
  1. 说明