Files
u-desktop/web/themes/fractal_src.html
绝尘 9fd3acede3 新增: 星座运势+AI资讯+知识卡片+桌面设置窗口+秒显示开关
- 星座运势: 天聚数行API集成,5维进度条+幸运标签+今日概述
- AI资讯: 天聚数行API,图文布局5条展示,文件缓存2小时刷新
- 知识卡片: AI生成,关键字+提示词配置,30分钟刷新
- 桌面设置: 独立WebView2窗口,760x1350,含壁纸/布局/城市/颜色等配置
- 显示控制: 壁纸/时间/天气/星座/知识/AI资讯独立开关,秒显示开关
- 文件缓存: 星座运势+AI资讯缓存到本地,启动即显示上次数据
- initDone防抖: 防止设置窗口初始化触发卡片重载
2026-05-26 04:34:00 +08:00

240 lines
6.7 KiB
HTML

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Dynamic Wallpaper</title>
<style>
* { margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #000; }
canvas { display: block; width: 100vw; height: 100vh; }
#info {
position: fixed; top: 20px; right: 20px;
color: rgba(255,255,255,0.6); font: 14px monospace;
pointer-events: none; user-select: none;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="info"></div>
<script>
const canvas = document.getElementById('c');
const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
if (!gl) { document.body.innerHTML = '<h1 style="color:#fff;text-align:center;margin-top:40vh">WebGL 不可用</h1>'; }
let paused = false;
let fullscreen = false;
let mouseX = 0, mouseY = 0;
let lastMove = 0;
// --- 状态控制 (Go Bridge) ---
window.setPaused = function(v) { paused = v; };
window.setFullscreen = function(v) { fullscreen = v; };
// --- 鼠标 ---
document.addEventListener('mousemove', e => {
mouseX = e.clientX / window.innerWidth;
mouseY = 1.0 - e.clientY / window.innerHeight;
lastMove = performance.now();
});
document.addEventListener('click', e => {
// 点击涟漪效果 — 传给 shader
clickX = e.clientX / window.innerWidth;
clickY = 1.0 - e.clientY / window.innerHeight;
clickTime = performance.now();
});
let clickX = 0, clickY = 0, clickTime = 0;
// --- Shader ---
const vertSrc = `
attribute vec2 a_pos;
void main() { gl_Position = vec4(a_pos, 0.0, 1.0); }
`;
// 极光 + 流体噪声 shader
const fragSrc = `
precision highp float;
uniform float u_time;
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_click;
uniform vec2 u_clickPos;
// simplex-like noise
vec3 mod289(vec3 x) { return x - floor(x * (1.0/289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0/289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, 0.366025403784439,
-0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod289(i);
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m; m = m*m;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution;
vec2 p = uv * 2.0 - 1.0;
p.x *= u_resolution.x / u_resolution.y;
float t = u_time * 0.3;
// 鼠标影响
vec2 mp = u_mouse * 2.0 - 1.0;
mp.x *= u_resolution.x / u_resolution.y;
float mouseDist = length(p - mp);
float mouseInfluence = smoothstep(0.8, 0.0, mouseDist) * 0.3;
// 点击涟漪
float ripple = 0.0;
if (u_click > 0.0) {
vec2 cp = u_clickPos * 2.0 - 1.0;
cp.x *= u_resolution.x / u_resolution.y;
float d = length(p - cp);
ripple = sin(d * 30.0 - u_click * 8.0) * exp(-d * 3.0) * exp(-u_click * 2.0) * 0.15;
}
// 多层噪声
float n1 = snoise(p * 1.5 + vec2(t * 0.5, t * 0.3)) * 0.5 + 0.5;
float n2 = snoise(p * 3.0 + vec2(-t * 0.7, t * 0.5)) * 0.5 + 0.5;
float n3 = snoise(p * 0.8 + vec2(t * 0.2, -t * 0.4) + mouseInfluence) * 0.5 + 0.5;
// 极光色彩
vec3 c1 = vec3(0.05, 0.2, 0.4); // 深蓝
vec3 c2 = vec3(0.0, 0.8, 0.5); // 翠绿
vec3 c3 = vec3(0.3, 0.1, 0.6); // 紫色
vec3 c4 = vec3(0.1, 0.5, 0.9); // 天蓝
// 极光带
float aurora = smoothstep(0.3, 0.7, n3) * smoothstep(0.8, 0.4, n1);
vec3 auroraColor = mix(c2, c4, n2) * aurora * 1.2;
// 底层渐变
vec3 bg = mix(c1, c3, uv.y * 0.5 + n1 * 0.3);
bg += auroraColor;
// 星星效果
float stars = pow(snoise(p * 20.0), 12.0) * 0.8;
bg += vec3(stars);
// 涟漪叠加
bg += vec3(ripple * 2.0, ripple * 3.0, ripple * 4.0);
// 鼠标光晕
bg += vec3(0.1, 0.3, 0.5) * mouseInfluence;
// 轻微暗角
float vignette = 1.0 - smoothstep(0.5, 1.5, length(p * 0.7));
bg *= vignette * 0.9 + 0.1;
gl_FragColor = vec4(bg, 1.0);
}
`;
// --- 编译 Shader ---
function createShader(type, src) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error('Shader error:', gl.getShaderInfoLog(s));
return null;
}
return s;
}
const vs = createShader(gl.VERTEX_SHADER, vertSrc);
const fs = createShader(gl.FRAGMENT_SHADER, fragSrc);
const prog = gl.createProgram();
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
gl.linkProgram(prog);
gl.useProgram(prog);
// 全屏四边形
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);
const aPos = gl.getAttribLocation(prog, 'a_pos');
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
// Uniforms
const uTime = gl.getUniformLocation(prog, 'u_time');
const uRes = gl.getUniformLocation(prog, 'u_resolution');
const uMouse = gl.getUniformLocation(prog, 'u_mouse');
const uClick = gl.getUniformLocation(prog, 'u_click');
const uClickPos = gl.getUniformLocation(prog, 'u_clickPos');
// --- 渲染循环 ---
let lastFrame = 0;
let targetFPS = 30;
let currentFPS = 30;
function resize() {
const dpr = window.devicePixelRatio || 1;
canvas.width = window.innerWidth * dpr;
canvas.height = window.innerHeight * dpr;
gl.viewport(0, 0, canvas.width, canvas.height);
}
window.addEventListener('resize', resize);
resize();
function render(now) {
requestAnimationFrame(render);
if (paused || fullscreen) return;
// 帧率控制
const interval = 1000 / targetFPS;
if (now - lastFrame < interval) return;
lastFrame = now;
// 自适应帧率:鼠标静止 5s 后降帧
const idleTime = now - lastMove;
if (idleTime > 5000) {
targetFPS = 10;
} else {
targetFPS = 30;
}
const t = now / 1000.0;
const clickElapsed = clickTime > 0 ? (now - clickTime) / 1000.0 : 0.0;
gl.uniform1f(uTime, t);
gl.uniform2f(uRes, canvas.width, canvas.height);
gl.uniform2f(uMouse, mouseX, mouseY);
gl.uniform1f(uClick, clickElapsed > 3.0 ? 0.0 : clickElapsed);
gl.uniform2f(uClickPos, clickX, clickY);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
requestAnimationFrame(render);
// 通知 Go 层壁纸就绪
if (window.wallpaperReady) {
wallpaperReady().then(() => console.log('wallpaper embedded'));
}
</script>
</body>
</html>