Skip to content

Commit 87c7cd3

Browse files
authored
Добавляет снег 2025 (#1317)
1 parent 0ee87fb commit 87c7cd3

File tree

7 files changed

+151
-1
lines changed

7 files changed

+151
-1
lines changed

src/includes/blocks/footer.njk

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<legend class="visually-hidden">Тема:</legend>
55
<span class="footer__theme-toggle-label" aria-hidden="true">Тема:</span>
66
{% include "blocks/theme-toggle.njk" %}
7+
{% include "blocks/snow-toggle.njk" %}
78
</fieldset>
89
<div class="footer__lists">
910
<ul class="footer__list footer-list footer__list_social font-theme font-theme--code base-list">

src/includes/blocks/snow-25.njk

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<canvas id="snowCanvas"></canvas>

src/layouts/base.njk

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
{{ content | safe }}
2222

2323
{% include "blocks/cookie-notification.njk" %}
24+
{% include "blocks/snow-25.njk" %}
2425

2526
<script src="/scripts/index.js" {% if env.isDevEnv %}type="module"{% endif %}></script>
2627
</body>

src/scripts/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ import './modules/person-badges-tooltip.js'
2626
import './modules/answer.js'
2727
import './modules/pwa.js'
2828
import './modules/triggers.js'
29+
import './modules/snow-toggle-25.js'

src/scripts/modules/snow-toggle-25.js

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
window.onload = function () {
2+
class Snowfall {
3+
constructor(canvas) {
4+
this.canvas = canvas
5+
this.context = canvas.getContext('2d')
6+
this.snowCounter = 0
7+
this.speedMultiplier = 0
8+
this.snowflakes = []
9+
this.animationFrameId = null
10+
11+
this.resize()
12+
}
13+
14+
createSnowflake() {
15+
const radius = getRandomInt(1, 10)
16+
return {
17+
xpos: getRandomInt(0, this.canvas.width),
18+
ypos: getRandomInt(-this.canvas.height, 0),
19+
radius: radius,
20+
opacity: radius * 10,
21+
speed: this.speedMultiplier * (radius / 6),
22+
dx: (Math.random() - 0.5) * 2,
23+
}
24+
}
25+
26+
drawSnowflake(flake) {
27+
this.context.beginPath()
28+
this.context.arc(flake.xpos, flake.ypos, flake.radius, 0, Math.PI * 2)
29+
this.context.fillStyle = `hsl(202.33deg 53.09% 84.12% / ${flake.opacity}%)`
30+
this.context.fill()
31+
}
32+
33+
updateSnowflake(flake) {
34+
flake.xpos += flake.dx
35+
flake.ypos += flake.speed
36+
37+
if (flake.ypos - flake.radius > this.canvas.height) {
38+
flake.ypos = getRandomInt(-this.canvas.height, 0)
39+
flake.xpos = getRandomInt(0, this.canvas.width)
40+
}
41+
}
42+
43+
start() {
44+
this.snowCounter = 100
45+
this.speedMultiplier = 1
46+
47+
this.stop()
48+
this.snowflakes = Array.from({ length: this.snowCounter }, () => this.createSnowflake())
49+
this.animate()
50+
}
51+
52+
animate() {
53+
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
54+
this.snowflakes.forEach((flake) => {
55+
this.updateSnowflake(flake)
56+
this.drawSnowflake(flake)
57+
})
58+
this.animationFrameId = requestAnimationFrame(this.animate.bind(this))
59+
}
60+
61+
stop() {
62+
if (this.animationFrameId) {
63+
cancelAnimationFrame(this.animationFrameId)
64+
this.animationFrameId = null
65+
}
66+
67+
snowfall.snowflakes = []
68+
snowfall.context.clearRect(0, 0, canvas.width, canvas.height)
69+
}
70+
71+
setCounter(newCount) {
72+
this.snowCounter = newCount
73+
this.snowflakes = Array.from({ length: this.snowCounter }, () => this.createSnowflake())
74+
}
75+
76+
resize() {
77+
this.canvas.width = window.innerWidth
78+
this.canvas.height = window.innerHeight
79+
}
80+
}
81+
82+
function debounce(func, wait) {
83+
let timeout
84+
return function (...args) {
85+
clearTimeout(timeout)
86+
timeout = setTimeout(() => func.apply(this, args), wait)
87+
}
88+
}
89+
90+
function getRandomInt(min, max) {
91+
return Math.floor(Math.random() * (max - min + 1)) + min
92+
}
93+
94+
function changeSnowAnimation(animationName) {
95+
if (animationName === 'none') {
96+
snowfall.stop()
97+
document.title = pageTitle
98+
} else if (animationName === 'snowfall') {
99+
snowfall.start()
100+
document.title = '❄️ ' + pageTitle
101+
}
102+
}
103+
104+
const canvas = document.getElementById('snowCanvas')
105+
const snowToggle = document.querySelector('.snow-toggle')
106+
const snowfall = new Snowfall(canvas)
107+
const pageTitle = document.title
108+
snowfall.start()
109+
document.title = '❄️ ' + pageTitle
110+
const storageKey = 'snow'
111+
112+
let currentStorage = localStorage.getItem(storageKey)
113+
114+
if (currentStorage) {
115+
snowToggle.querySelector(`.snow-toggle__control[value='${currentStorage}']`).checked = true
116+
117+
changeSnowAnimation(currentStorage)
118+
}
119+
120+
window.addEventListener('storage', () => {
121+
changeSnowAnimation(localStorage.getItem(storageKey))
122+
})
123+
124+
document.querySelectorAll('input[name="snow"]').forEach((radio) => {
125+
radio.addEventListener('change', (event) => {
126+
const value = event.target.value
127+
localStorage.setItem(storageKey, event.target.value)
128+
changeSnowAnimation(value)
129+
})
130+
})
131+
132+
window.addEventListener(
133+
'resize',
134+
debounce(() => {
135+
snowfall.resize()
136+
}, 150),
137+
)
138+
}

src/styles/blocks/snow-25.css

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/* stylelint-disable-next-line selector-id-pattern */
2+
#snowCanvas {
3+
position: fixed;
4+
inset: 0;
5+
z-index: 100;
6+
display: block;
7+
pointer-events: none;
8+
}

src/styles/index.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
@import url("blocks/footer.css");
2424
@import url("blocks/theme-toggle.css");
2525
@import url("blocks/snow-toggle.css");
26-
@import url("blocks/snow.css");
26+
@import url("blocks/snow-25.css");
2727
@import url("blocks/intro.css");
2828
@import url("blocks/featured-article.css");
2929
@import url("blocks/featured-articles-list.css");

0 commit comments

Comments
 (0)