-
Notifications
You must be signed in to change notification settings - Fork 0
/
π.js
176 lines (151 loc) Β· 5.06 KB
/
π.js
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
"use strict";
(() => {
let
buzzing = false,
petting = false,
minDist = 100,
workers = Math.max(5, Math.min(100, visualViewport.width * visualViewport.height / 80000)),
timing = 1000 / 60, gate = ~~(timing), maxCorrection = Math.min(8, Math.max(2, gate / timing + 1)),
garden, beesknees = ["lapidarius", "magnus", "pascuorum", "pratorum"], hive = [], touch = { x: Infinity, y: Infinity }, then, elapsed,
request, reqFrame = window.requestAnimationFrame || ((cb) => { return window.setTimeout(cb, 1000 / 60) }), canFrame = window.cancelAnimationFrame || ((i) => { clearTimeout(i) });
class Bombus {
#el = document.createElement("img");
#buzzing = true;
#size = (Math.random() * 10) + 20;
#x = Math.floor(Math.random() * visualViewport.width);
#y = Math.floor(Math.random() * visualViewport.height);
#center = { x: this.#x, y: this.#y };
#direction = { x: 0, y: 0 };
#flipped = Math.random() > .5;
#touchDistance = Infinity;
#velocity = { x: 0, y: 0, absolute: 0, force: 0 };
#oscilation = {
step: 0,
size: Math.random() / 10,
delta: Math.random() / 40 + 0.1
};
constructor() {
this.#el.setAttribute("src", bombus.path + beesknees[Math.ceil((Math.random() * beesknees.length) - 1)] + "." + bombus.extension);
this.#el.setAttribute("width", ~~this.#size);
this.#el.setAttribute("height", ~~this.#size);
garden.appendChild(this.#el);
let speed = Math.random() * 1.2 + 0.5;
this.#direction.x = this.#velocity.x = speed;
this.#direction.y = this.#velocity.y = speed / 3 * (Math.random() > .5 ? 1 : -1);
if (this.#flipped) {
this.#el.classList.add("flip");
this.#direction.x = this.#velocity.x *= -1;
}
this.fly(1);
}
fly(multiplier) {
if (this.#buzzing) {
this.#accelerate(multiplier);
this.#move(multiplier);
}
}
#accelerate(multiplier) {
if (petting) {
this.#touchDistance = Math.sqrt(Math.pow(this.#center.x - touch.x, 2) + Math.pow(this.#center.y - touch.y, 2));
if (this.#touchDistance < minDist) {
this.#velocity.force = Math.min(.1, minDist / Math.pow(this.#touchDistance, 2) * 5);
this.#velocity.x -= ((touch.x - this.#center.x) / this.#touchDistance) * this.#velocity.force / 2 * multiplier;
this.#velocity.y -= ((touch.y - this.#center.y) / this.#touchDistance) * this.#velocity.force * multiplier;
}
}
this.#velocity.y -= Math.cos(this.#oscilation.step += this.#oscilation.delta * (.5 + Math.random())) * this.#oscilation.size * multiplier;
this.#velocity.x = this.#velocity.x * .99 + Math.abs(this.#direction.x) * (this.#velocity.x > this.#direction.x ? -.01 : .01);
this.#velocity.y = this.#velocity.y * .99 + Math.abs(this.#direction.y) * (this.#velocity.y > this.#direction.y ? -.01 : .01);
}
#move(multiplier) {
if (this.#y + 50 < -this.#size) {
this.#y = visualViewport.height;
this.#velocity.y = this.#direction.y
} else if (this.#y - 50 > visualViewport.height) {
this.#y = -this.#size;
this.#velocity.x = this.#direction.x
}
if (this.#x + 50 < -this.#size) {
this.#x = visualViewport.width;
this.#velocity.x = this.#direction.x;
} else if (this.#x - 50 > visualViewport.width) {
this.#x = -this.#size;
this.#velocity.y = this.#direction.y
}
this.#velocity.absolute = Math.abs(this.#velocity.x) + Math.abs(this.#velocity.y);
if (this.#velocity.absolute > 3) {
this.#velocity.x /= this.#velocity.absolute / 1.5;
this.#velocity.y /= this.#velocity.absolute / 1.5;
}
this.#y += this.#velocity.y * multiplier;
this.#x += this.#velocity.x * multiplier;
this.#el.style.left = ~~this.#x + "px";
this.#el.style.top = ~~this.#y + "px";
this.#center.x = this.#x + this.#size / 2;
this.#center.y = this.#y + this.#size / 2;
}
sleep() {
this.#buzzing = false;
garden.removeChild(this.#el);
}
}
function fly(multiplier) {
multiplier = Math.min(maxCorrection, multiplier);
hive.forEach(worker => {
worker.fly(multiplier);
});
};
function animate(now) {
elapsed = now - then;
if (elapsed > gate) {
then = now;
fly(elapsed / timing);
} else {
canFrame(request)
}
if (buzzing) {
request = reqFrame(animate);
}
}
function init() {
if (!garden) {
garden = document.querySelector("#π-garden") || document.createElement("div");
if (!garden.parentElement) {
garden.id = "π-garden";
document.body.append(garden);
}
document.addEventListener("pointermove", (e) => {
petting = true;
touch.x = e.clientX;
touch.y = e.clientY;
});
document.addEventListener("pointerout", () => { petting = false; });
}
if (!buzzing) {
buzzing = true;
then = document.timeline.currentTime;
request = reqFrame(animate);
}
}
window.bombus = {
path: 'bombus/',
extension: 'png',
summer: (bees = workers) => {
init();
for (let i = 0; i < bees; i++) {
hive.push(new Bombus());
}
},
winter: () => {
buzzing = false;
hive.forEach((worker) => {
worker.sleep();
});
hive = [];
},
bps: (bps) => {
gate = ~~(1000 / bps)
maxCorrection = Math.min(8, Math.max(2, gate / timing + 1));
}
};
})()