@@ -81,3 +81,13 @@ code {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
#night {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #0e0016;
|
||||
z-index: -9999;
|
||||
}
|
||||
|
||||
228
src/static/js/base.js
Normal file
228
src/static/js/base.js
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* @licstart The following is the entire license notice for the JavaScript
|
||||
* code in this file.
|
||||
*
|
||||
* Copyright (C) 2025 purplebored.pl <purplebored@posteo.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License, Version 2.0
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*
|
||||
* @licend The above is the entire license notice for the JavaScript code
|
||||
* in this page.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
function create_night(canvas_element) {
|
||||
const context = canvas_element.getContext("2d");
|
||||
|
||||
const star_range = 0.7; /* Variation in star size */
|
||||
const star_spread = 0.002; /* Star density per pixel */
|
||||
const concentration_corner = "top-left"; /* Concentration corner position */
|
||||
const concentration_strength = 5.0; /* How much should it be concentrated? */
|
||||
const twinkle_intensity = 5.0; /* How noticeable should the twinkle be? */
|
||||
|
||||
const interaction_radius = 150; /* Radius around mouse/touch where stars react */
|
||||
const interaction_brightness_factor = 5.0; /* How much brightness increases */
|
||||
const interaction_size_factor = 1.5; /* How much the stars should grow in size (only during interaction) */
|
||||
|
||||
const magic = [
|
||||
16.31189, 32454.4619, 9371.1474, 6848, 1544, 6848, 2156,
|
||||
]; /* Just some magic numbers */
|
||||
|
||||
/* Seeded "random" number */
|
||||
function seeded_random(seed) {
|
||||
const value = Math.sin(seed) * 10000;
|
||||
return value - Math.floor(value);
|
||||
}
|
||||
|
||||
let stars = [];
|
||||
let time = 0;
|
||||
let mouse_x = -1,
|
||||
mouse_y = -1;
|
||||
|
||||
/* Determine corner position for concentration */
|
||||
function get_corner(width, height) {
|
||||
switch (concentration_corner) {
|
||||
case "top-right":
|
||||
return { x: width, y: 0 };
|
||||
case "bottom-left":
|
||||
return { x: 0, y: height };
|
||||
case "bottom-right":
|
||||
return { x: width, y: height };
|
||||
default:
|
||||
return { x: 0, y: 0 }; /* top-left */
|
||||
}
|
||||
}
|
||||
|
||||
/* Function to generate star shape */
|
||||
function draw_star(x, y, radius, alpha) {
|
||||
const num_points = 5 + Math.floor(Math.random() * 3);
|
||||
const angle = Math.PI / num_points;
|
||||
|
||||
context.beginPath();
|
||||
|
||||
for (let idx = 0; idx < num_points; idx++) {
|
||||
const angle_offset = (idx * 2 * Math.PI) / num_points;
|
||||
const outer_x = x + radius * Math.cos(angle_offset);
|
||||
const outer_y = y + radius * Math.sin(angle_offset);
|
||||
|
||||
context.lineTo(outer_x, outer_y);
|
||||
|
||||
/* Inner points for star shape */
|
||||
const inner_radius = radius / 2 + Math.random() * 0.2;
|
||||
const inner_x = x + inner_radius * Math.cos(angle_offset + angle);
|
||||
const inner_y = y + inner_radius * Math.sin(angle_offset + angle);
|
||||
|
||||
context.lineTo(inner_x, inner_y);
|
||||
}
|
||||
|
||||
context.closePath();
|
||||
context.fillStyle = `rgba(255, 255, 180, ${alpha})`;
|
||||
context.fill();
|
||||
}
|
||||
|
||||
/* Function to generate star background */
|
||||
function generate_stars() {
|
||||
const { width, height } = canvas_element;
|
||||
const star_count = Math.floor(width * height * star_spread);
|
||||
const corner = get_corner(width, height);
|
||||
|
||||
stars = [];
|
||||
|
||||
for (let idx = 0; idx < star_count; idx++) {
|
||||
const base_random = seeded_random(idx * magic[0]);
|
||||
|
||||
/* Uniform random position */
|
||||
const raw_x = seeded_random(base_random * magic[1]) * width;
|
||||
const raw_y = seeded_random(base_random * magic[2]) * height;
|
||||
|
||||
/* Calculate distance from the corner to control concentration */
|
||||
const dx = raw_x - corner.x;
|
||||
const dy = raw_y - corner.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
/* Apply exponential falloff based on distance (closer = more likely to be drawn) */
|
||||
const max_distance = Math.sqrt(width * width + height * height);
|
||||
const weight = Math.exp(
|
||||
-concentration_strength * (distance / max_distance),
|
||||
);
|
||||
|
||||
/* Skip stars that don't meet the concentration threshold */
|
||||
if (seeded_random(base_random * magic[3]) > weight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Randomized star properties */
|
||||
const radius =
|
||||
seeded_random(base_random * magic[4]) * star_range +
|
||||
0.4 +
|
||||
Math.random() * 0.2;
|
||||
const twinkle_speed =
|
||||
seeded_random(base_random * magic[5]) * 1.5 + 0.3;
|
||||
const twinkle_phase =
|
||||
seeded_random(base_random * magic[6]) * Math.PI * 2;
|
||||
|
||||
stars.push({
|
||||
x: raw_x,
|
||||
y: raw_y,
|
||||
radius,
|
||||
twinkle_speed,
|
||||
twinkle_phase,
|
||||
original_radius: radius,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize canvas and regenerate stars on window resize */
|
||||
function resize_canvas() {
|
||||
canvas_element.width = window.innerWidth;
|
||||
canvas_element.height = window.innerHeight;
|
||||
generate_stars();
|
||||
}
|
||||
|
||||
window.addEventListener("resize", resize_canvas);
|
||||
resize_canvas();
|
||||
|
||||
/* Handle mouse/touch movement effects */
|
||||
function handle_interaction(event) {
|
||||
if (event.touches) {
|
||||
mouse_x = event.touches[0].clientX;
|
||||
mouse_y = event.touches[0].clientY;
|
||||
} else {
|
||||
mouse_x = event.clientX;
|
||||
mouse_y = event.clientY;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("mousemove", handle_interaction);
|
||||
window.addEventListener("touchmove", handle_interaction);
|
||||
|
||||
/* Animate stars (twinke) */
|
||||
function animate() {
|
||||
time += 0.01;
|
||||
|
||||
const { width, height } = canvas_element;
|
||||
context.fillStyle = "#0e0016";
|
||||
context.fillRect(0, 0, width, height);
|
||||
|
||||
for (const star of stars) {
|
||||
const twinkle = Math.sin(
|
||||
time * star.twinkle_speed + star.twinkle_phase,
|
||||
);
|
||||
let alpha = 0.6 + twinkle_intensity * (0.5 + 0.5 * twinkle);
|
||||
|
||||
/* Interaction effect based on mouse/touch proximity */
|
||||
if (mouse_x !== -1 && mouse_y !== -1) {
|
||||
const dx = mouse_x - star.x;
|
||||
const dy = mouse_y - star.y;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance < interaction_radius) {
|
||||
const effect = Math.max(
|
||||
0,
|
||||
1 - distance / interaction_radius,
|
||||
);
|
||||
|
||||
alpha *=
|
||||
1 +
|
||||
effect *
|
||||
interaction_brightness_factor; /* Make the star brighter */
|
||||
|
||||
star.radius =
|
||||
star.original_radius *
|
||||
(1 +
|
||||
effect *
|
||||
interaction_size_factor); /* Grow the star size */
|
||||
}
|
||||
}
|
||||
|
||||
draw_star(star.x, star.y, star.radius, alpha);
|
||||
}
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
animate();
|
||||
}
|
||||
|
||||
function base_main() {
|
||||
const night = document.getElementById("night");
|
||||
create_night(night);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
base_main();
|
||||
});
|
||||
@@ -19,8 +19,8 @@
|
||||
/>
|
||||
<meta property="og:type" content="{% block type %}website{% endblock %}" />
|
||||
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<meta name="theme-color" content="{% block colour %}#121212{% endblock %}" />
|
||||
<meta name="color-scheme" content="dark" />
|
||||
<meta name="theme-color" content="{% block colour %}#0e0016{% endblock %}" />
|
||||
|
||||
<meta property="og:locale" content="{{ locale | escape }}" />
|
||||
<meta name="foss:src" content="{{ source_code | escape }}" />
|
||||
|
||||
Reference in New Issue
Block a user