+154
src/components/blog/background/Cloud.astro
+154
src/components/blog/background/Cloud.astro
···
1
+
---
2
+
import { utils } from "@/config";
3
+
import { blog } from "@/config";
4
+
5
+
// utils
6
+
7
+
const toRad = (n: number) => (n * Math.PI) / 180;
8
+
const toDeg = (n: number) => (n * 180) / Math.PI;
9
+
10
+
/* y
11
+
90 ____ 90-n
12
+
| /
13
+
| /
14
+
x | / l
15
+
|/
16
+
n
17
+
18
+
y
19
+
L ____ X
20
+
| /
21
+
| /
22
+
x | / l
23
+
|/
24
+
Y
25
+
*/
26
+
27
+
/**
28
+
* generate the vector [x, y] from the angle and length
29
+
* @param n angle (degrees) from north
30
+
* @param l length (unit as output)
31
+
*/
32
+
const vector = (n: number, l: number): [number, number] => [
33
+
(l / Math.sin(toRad(90))) * Math.sin(toRad(n)),
34
+
(l / Math.sin(toRad(90))) * Math.sin(toRad(90 - n)),
35
+
];
36
+
37
+
const vectorOffset = (
38
+
v: [number, number],
39
+
o: [number, number]
40
+
): [number, number] => {
41
+
return [o[0] - v[0], o[1] - v[1]];
42
+
};
43
+
44
+
// constants
45
+
const r = 100;
46
+
---
47
+
48
+
<svg
49
+
viewBox={`-${blog.background.clouds.bumpRadius[1]} -${blog.background.clouds.bumpRadius[1]} ${2 * (r + blog.background.clouds.bumpRadius[1])} ${r + blog.background.clouds.bumpRadius[1]}`}
50
+
preserveAspectRatio="none"
51
+
{...Astro.props}
52
+
>
53
+
<defs>
54
+
<clipPath id={`cloud-clip-${Astro.props.id}`}>
55
+
{
56
+
new Array(10)
57
+
.fill(0)
58
+
.map((_) => utils.getRandom(blog.background.clouds.bumpRadius))
59
+
.reduce(
60
+
(p, c) => {
61
+
if (p.complete) return p;
62
+
/* C
63
+
/\
64
+
r / \ r
65
+
/___\
66
+
R c R
67
+
r² + r² - c²
68
+
cos C = ────────────
69
+
2 x r x r
70
+
(r² + r² - c²)
71
+
C = cos⁻¹(────────────)
72
+
( 2 x r x r )
73
+
R = (180 - C) / 2 */
74
+
// angle C (opposite of chord, between 2 radii) in radians
75
+
const C = Math.acos((r ** 2 + r ** 2 - c ** 2) / (2 * r * r));
76
+
// angle R (opposite of radius) in radians
77
+
const R = (Math.PI - C) / 2;
78
+
const ang = -(180 - (90 - p.prev) - toDeg(R));
79
+
const center = vectorOffset(vector(ang, c / 2), p.origin);
80
+
const hex = () => {
81
+
const hex = Math.floor(Math.random() * 256).toString(16);
82
+
return "0".repeat(2 - hex.length) + hex;
83
+
};
84
+
const nextOrigin = vectorOffset(vector(ang, c), p.origin);
85
+
if (nextOrigin[1] > 100) {
86
+
const newCenter: [number, number] = [
87
+
(2 * r - p.origin[0]) / 2 + p.origin[0],
88
+
(r - p.origin[1]) / 2 + p.origin[1],
89
+
];
90
+
const newDistance = Math.sqrt(
91
+
(newCenter[0] - p.origin[0]) ** 2 +
92
+
(newCenter[1] - p.origin[1]) ** 2
93
+
);
94
+
return {
95
+
origin: vectorOffset(vector(ang, c), p.origin),
96
+
prev: p.prev + toDeg(C),
97
+
complete: true,
98
+
output: [
99
+
...p.output,
100
+
<circle
101
+
cx={newCenter[0]}
102
+
cy={newCenter[1]}
103
+
r={newDistance}
104
+
/>,
105
+
],
106
+
};
107
+
}
108
+
return {
109
+
origin: vectorOffset(vector(ang, c), p.origin),
110
+
prev: p.prev + toDeg(C),
111
+
complete: false,
112
+
output: [
113
+
...p.output,
114
+
<circle cx={center[0]} cy={center[1]} r={c / 2} />,
115
+
],
116
+
};
117
+
},
118
+
{
119
+
origin: [0, r] as [number, number],
120
+
prev: 0,
121
+
output: [] as any[],
122
+
complete: false,
123
+
}
124
+
).output
125
+
}
126
+
<circle cx={r} cy={r} r={r}></circle>
127
+
</clipPath>
128
+
129
+
<linearGradient
130
+
id={`cloud-gradient-${Astro.props.id}`}
131
+
x1="0"
132
+
x2="0"
133
+
y1="0"
134
+
y2="1"
135
+
>
136
+
<stop offset="0%" stop-color="white"></stop>
137
+
<stop
138
+
offset={`${blog.background.clouds.gradientStops[0]}%`}
139
+
stop-color="white"></stop>
140
+
<stop
141
+
offset={`${blog.background.clouds.gradientStops[1]}%`}
142
+
stop-color="transparent"></stop>
143
+
<stop offset="100%" stop-color="transparent"></stop>
144
+
</linearGradient>
145
+
</defs>
146
+
147
+
<rect
148
+
width={2 * (r + blog.background.clouds.bumpRadius[1])}
149
+
height={r + blog.background.clouds.bumpRadius[1]}
150
+
x={blog.background.clouds.bumpRadius[1] * -1}
151
+
y={blog.background.clouds.bumpRadius[1] * -1}
152
+
clip-path={`url(#cloud-clip-${Astro.props.id})`}
153
+
fill={`url(#cloud-gradient-${Astro.props.id})`}></rect>
154
+
</svg>
+9
-15
src/components/blog/background/Clouds.astro
+9
-15
src/components/blog/background/Clouds.astro
···
1
1
---
2
2
import { blog } from "@/config";
3
+
import Cloud from "./Cloud.astro";
3
4
---
4
5
5
-
<style>
6
-
img {
7
-
position: absolute;
8
-
}
9
-
</style>
10
-
11
6
<div id="clouds">
12
7
{
13
8
new Array(blog.background.clouds.count).fill(0).reduce(
14
-
(prev) => {
9
+
(prev, _, i) => {
15
10
const width =
16
11
blog.background.clouds.width[0] +
17
12
(blog.background.clouds.width[1] -
···
33
28
y: y,
34
29
output: [
35
30
...prev.output,
36
-
<img
37
-
src="https://cdn.zmescience.com/wp-content/uploads/2017/07/8690313402_5f76f736b3_k-1-1024x683.webp"
38
-
alt=""
31
+
<Cloud
39
32
style={`--parallax-speed: ${blog.background.parallax.clouds};
40
-
41
-
width: ${width}rem;
42
-
height: ${height}rem;
43
-
top: ${y}rem;
44
-
left: calc(${x} * 200lvw - 100lvw);`}
33
+
width: ${width}rem;
34
+
height: ${height}rem;
35
+
top: ${y}rem;
36
+
left: calc(${x} * 200lvw - 100lvw);
37
+
position: absolute;`}
38
+
id={"cloud-" + i}
45
39
data-parallax
46
40
/>,
47
41
],