style: 添加预设样式
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user