style: 添加预设样式

This commit is contained in:
Baobhan Sith
2025-05-27 21:36:06 +08:00
parent 9c109f4240
commit 4966d2e575
16 changed files with 1779 additions and 148 deletions
@@ -0,0 +1,219 @@
<style>
#floating-bubbles-container {
width: 100%; height: 100%;
background: radial-gradient(circle at center, #2c3e50, #1a2533); /* 深蓝灰色径向渐变 */
overflow: hidden; position: relative;
}
#bubbles-canvas { display: block; position: absolute; top: 0; left: 0; }
</style>
<div id="floating-bubbles-container">
<canvas id="bubbles-canvas"></canvas>
</div>
<script>
(function() {
const canvas = document.getElementById('bubbles-canvas');
const layerElement = canvas.closest('.terminal-custom-html-layer') || canvas.parentElement;
if (!canvas || !layerElement) return;
const ctx = canvas.getContext('2d');
let width, height, animationFrameId;
const bubbles = [];
const numBubbles = 10; // 少量大气泡
const baseSpeed = 0.1;
const baseRadius = 60;
const radiusVariance = 40;
// Perlin noise function (simple implementation for distortion)
// For more sophisticated noise, consider a library
const ClassicalNoise = function(r) { // Classic Perlin noise
if (r == undefined) r = Math;
this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],
[1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],
[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
this.p = [];
for (var i=0; i<256; i++) {
this.p[i] = Math.floor(r.random()*256);
}
// To remove the need for index wrapping, double the permutation table length
this.perm = [];
for(var i=0; i<512; i++) {
this.perm[i]=this.p[i&255];
}
};
ClassicalNoise.prototype.dot = function(g, x, y, z) {
return g[0]*x + g[1]*y + g[2]*z;
};
ClassicalNoise.prototype.mix = function(a, b, t) {
return (1.0-t)*a + t*b;
};
ClassicalNoise.prototype.fade = function(t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
};
ClassicalNoise.prototype.noise = function(x, y, z) {
// Find unit grid cell containing point
var X = Math.floor(x);
var Y = Math.floor(y);
var Z = Math.floor(z);
// Get relative xyz coordinates of point within that cell
x = x - X;
y = y - Y;
z = z - Z;
// Wrap the integer cells at 255 (smaller integer period can be introduced here)
X = X & 255;
Y = Y & 255;
Z = Z & 255;
// Calculate a set of eight hashed gradient indices
var gi000 = this.perm[X+this.perm[Y+this.perm[Z]]];
var gi001 = this.perm[X+this.perm[Y+this.perm[Z+1]]];
var gi010 = this.perm[X+this.perm[Y+1+this.perm[Z]]];
var gi011 = this.perm[X+this.perm[Y+1+this.perm[Z+1]]];
var gi100 = this.perm[X+1+this.perm[Y+this.perm[Z]]];
var gi101 = this.perm[X+1+this.perm[Y+this.perm[Z+1]]];
var gi110 = this.perm[X+1+this.perm[Y+1+this.perm[Z]]];
var gi111 = this.perm[X+1+this.perm[Y+1+this.perm[Z+1]]];
// Calculate noise contributions from eight corners
var n000= this.dot(this.grad3[gi000%12], x, y, z);
var n100= this.dot(this.grad3[gi100%12], x-1, y, z);
var n010= this.dot(this.grad3[gi010%12], x, y-1, z);
var n110= this.dot(this.grad3[gi110%12], x-1, y-1, z);
var n001= this.dot(this.grad3[gi001%12], x, y, z-1);
var n101= this.dot(this.grad3[gi101%12], x-1, y, z-1);
var n011= this.dot(this.grad3[gi011%12], x, y-1, z-1);
var n111= this.dot(this.grad3[gi111%12], x-1, y-1, z-1);
// Compute the fade curve value for x, y, z
var u = this.fade(x);
var v = this.fade(y);
var w = this.fade(z);
// Interpolate along x the contributions from each of the corners
var nx00 = this.mix(n000, n100, u);
var nx01 = this.mix(n001, n101, u);
var nx10 = this.mix(n010, n110, u);
var nx11 = this.mix(n011, n111, u);
// Interpolate the four results along y
var nxy0 = this.mix(nx00, nx10, v);
var nxy1 = this.mix(nx01, nx11, v);
// Interpolate the two last results along z
var nxyz = this.mix(nxy0, nxy1, w);
return nxyz;
};
const perlin = new ClassicalNoise();
let noiseTime = 0;
class Bubble {
constructor() {
this.x = Math.random() * width;
this.y = Math.random() * height;
this.radius = baseRadius + Math.random() * radiusVariance;
this.color1 = `rgba(${Math.floor(Math.random()*50+50)}, ${Math.floor(Math.random()*50+100)}, ${Math.floor(Math.random()*50+150)}, 0.1)`; // Blues/Greens
this.color2 = `rgba(${Math.floor(Math.random()*50+100)}, ${Math.floor(Math.random()*50+50)}, ${Math.floor(Math.random()*50+120)}, 0.2)`; // Purples/Pinks
this.vx = (Math.random() - 0.5) * baseSpeed;
this.vy = (Math.random() - 0.5) * baseSpeed;
this.numPoints = 30 + Math.floor(Math.random() * 20); // 组成气泡边缘的点数
this.distortionFactor = 0.1 + Math.random() * 0.2; // 变形程度
this.noiseSeedX = Math.random() * 1000;
this.noiseSeedY = Math.random() * 1000;
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x - this.radius > width) this.x = -this.radius;
if (this.x + this.radius < 0) this.x = width + this.radius;
if (this.y - this.radius > height) this.y = -this.radius;
if (this.y + this.radius < 0) this.y = height + this.radius;
}
draw() {
ctx.beginPath();
const points = [];
for (let i = 0; i < this.numPoints; i++) {
const angle = (i / this.numPoints) * Math.PI * 2;
const noiseVal = perlin.noise(
(Math.cos(angle) + 1) * 0.5 + this.noiseSeedX + noiseTime * 0.1, // x for noise
(Math.sin(angle) + 1) * 0.5 + this.noiseSeedY + noiseTime * 0.1, // y for noise
noiseTime * 0.2 // z for noise (time evolution)
);
const r = this.radius * (1 + noiseVal * this.distortionFactor);
points.push({
x: this.x + r * Math.cos(angle),
y: this.y + r * Math.sin(angle)
});
}
ctx.moveTo(points[0].x, points[0].y);
for (let i = 0; i < this.numPoints; i++) {
const p1 = points[i];
const p2 = points[(i + 1) % this.numPoints];
const xc = (p1.x + p2.x) / 2;
const yc = (p1.y + p2.y) / 2;
ctx.quadraticCurveTo(p1.x, p1.y, xc, yc);
}
ctx.closePath();
const gradient = ctx.createRadialGradient(this.x, this.y, this.radius * 0.2, this.x, this.y, this.radius);
gradient.addColorStop(0, this.color1);
gradient.addColorStop(1, this.color2);
ctx.fillStyle = gradient;
ctx.globalAlpha = 0.4 + Math.sin(noiseTime + this.noiseSeedX) * 0.2; // Subtle opacity pulse
ctx.fill();
// Optional: subtle highlight
ctx.beginPath();
ctx.arc(this.x - this.radius * 0.3, this.y - this.radius * 0.3, this.radius * 0.3, 0, Math.PI * 2);
ctx.fillStyle = "rgba(200, 220, 255, 0.05)";
ctx.fill();
ctx.globalAlpha = 1;
}
}
function init() {
width = layerElement.offsetWidth;
height = layerElement.offsetHeight;
canvas.width = width;
canvas.height = height;
bubbles.length = 0;
for (let i = 0; i < numBubbles; i++) {
bubbles.push(new Bubble());
}
}
function animate() {
ctx.clearRect(0, 0, width, height);
noiseTime += 0.005;
bubbles.forEach(bubble => {
bubble.update();
bubble.draw();
});
animationFrameId = requestAnimationFrame(animate);
}
let resizeTimeout;
function onResize() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
if (animationFrameId) cancelAnimationFrame(animationFrameId);
init();
animate();
}, 250);
}
init();
animate();
window.addEventListener('resize', onResize);
canvas.cleanup = function() { if (animationFrameId) cancelAnimationFrame(animationFrameId); };
})();
</script>