Files
admin 59607097c7 东燊科技(Donsen)宣传网页:中英双语单页站 + 未来科技感视觉系统
- 单页双语站:data-lang 切换中/英,全站内容双语文案
- Hero 不对称双栏:左文案列(eyebrow/标题/渐变 slogan/CTA/数据条)+ 右凤凰视觉舱(深色玻璃 HUD + 能量轨道环 + 均衡器脉冲)
- 视觉系统 fx.css/fx.js(与 main.js 解耦):深空星野/鼠标聚光/自定义光标/点击爆炸/卡片 3D 倾斜/数字滚动;?static=1、prefers-reduced-motion、触屏自动降级
- 产品矩阵四段式结构(痛点→方案→场景→效益),四条产品线全部真实截图(WebP + PNG 兜底)
- 顶栏/页脚透明凤凰标识(phoenix.webp/png),去白底
- 部署配套:404.html、_headers、nginx.conf、robots.txt、favicon/apple-touch-icon/og-cover
- 性能基线:总传输约 153KB、CLS 0、CDP 两套取证套件全绿
2026-10-19 23:13:30 +08:00

289 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 东燊科技 Donsen — 未来科技感动效层(fx.js)
依赖:fx.css。与 main.js 完全解耦,任一方失效不影响基础功能。
模块:① 深空星野 ② 鼠标聚光 ③ 自定义光标 ④ 点击爆炸 ⑤ 卡片 3D 倾斜 ⑥ 数字滚动 */
(function () {
'use strict';
var html = document.documentElement;
var params = new URLSearchParams(location.search);
var staticMode = params.get('static') === '1';
var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// 触屏设备(含无头移动端模拟)不启用自定义光标/聚光/倾斜;爆炸效果保留(pointerdown 兼容触摸)
var isTouch = (navigator.maxTouchPoints || 0) > 0 || 'ontouchstart' in window;
var finePointer = window.matchMedia && window.matchMedia('(pointer:fine)').matches && !isTouch;
var dpr = Math.min(window.devicePixelRatio || 1, 2);
/* ============ ① 深空星野 ============ */
function starfield() {
var cv = document.getElementById('starfield');
if (!cv) return null;
var ctx = cv.getContext('2d');
var W = 0, H = 0, stars = [], shooters = [], raf = 0, mx = 0, my = 0, tmx = 0, tmy = 0;
function resize() {
W = window.innerWidth; H = window.innerHeight;
cv.width = W * dpr; cv.height = H * dpr;
cv.style.width = W + 'px'; cv.style.height = H + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
function seed(done) {
var n = Math.round((W * H) / 14000);
stars = [];
var i = 0;
// 分片生成(每帧 40 颗):避免首屏启动时一次循环占满主线程构成长任务
function chunk() {
var end = Math.min(i + 40, n);
for (; i < end; i++) {
var depth = 0.25 + Math.random() * 0.75;
stars.push({
x: Math.random() * W, y: Math.random() * H,
r: depth * 1.5 + 0.3,
a: 0.25 + Math.random() * 0.65,
tw: 0.6 + Math.random() * 1.8, ph: Math.random() * Math.PI * 2,
vy: depth * 0.05, hue: Math.random() < 0.12 ? 190 : (Math.random() < 0.1 ? 32 : 215),
depth: depth
});
}
if (i < n) { requestAnimationFrame(chunk); return; }
if (done) done();
}
chunk();
}
function seedAndStart() {
stop();
seed(function () {
if (staticMode || reduceMotion) { frame(0); stop(); } else start();
});
}
function spawnShooter() {
if (shooters.length > 1 || Math.random() > 0.35) return;
shooters.push({
x: Math.random() * W * 0.7, y: Math.random() * H * 0.4,
vx: 7 + Math.random() * 5, vy: 2.4 + Math.random() * 1.6, life: 1
});
}
function frame(ms) {
var t = ms / 1000;
tmx += (mx - tmx) * 0.04; tmy += (my - tmy) * 0.04;
ctx.clearRect(0, 0, W, H);
for (var i = 0; i < stars.length; i++) {
var s = stars[i];
var tw = 0.72 + 0.28 * Math.sin(t * s.tw + s.ph);
var px = s.x + tmx * 26 * s.depth;
var py = s.y + tmy * 18 * s.depth;
if (!reduceMotion) { s.y += s.vy; if (s.y > H + 4) { s.y = -4; s.x = Math.random() * W; } }
ctx.beginPath();
ctx.arc(px, py, s.r * tw, 0, Math.PI * 2);
ctx.fillStyle = 'hsla(' + s.hue + ',90%,' + (72 + s.depth * 18) + '%,' + (s.a * tw).toFixed(3) + ')';
ctx.fill();
if (s.r > 1.35) { // 大星加光晕
ctx.beginPath();
ctx.arc(px, py, s.r * 3.2, 0, Math.PI * 2);
ctx.fillStyle = 'hsla(' + s.hue + ',95%,75%,' + (s.a * 0.10).toFixed(3) + ')';
ctx.fill();
}
}
// 流星
if (!reduceMotion && Math.random() < 0.0022) spawnShooter();
for (var j = shooters.length - 1; j >= 0; j--) {
var sh = shooters[j];
sh.x += sh.vx; sh.y += sh.vy; sh.life -= 0.012;
var g = ctx.createLinearGradient(sh.x, sh.y, sh.x - sh.vx * 9, sh.y - sh.vy * 9);
g.addColorStop(0, 'rgba(255,255,255,' + (0.85 * sh.life).toFixed(3) + ')');
g.addColorStop(1, 'rgba(255,255,255,0)');
ctx.strokeStyle = g; ctx.lineWidth = 1.6;
ctx.beginPath(); ctx.moveTo(sh.x, sh.y); ctx.lineTo(sh.x - sh.vx * 9, sh.y - sh.vy * 9); ctx.stroke();
if (sh.life <= 0 || sh.x > W + 60) shooters.splice(j, 1);
}
if (!document.hidden) raf = requestAnimationFrame(frame);
}
function start() { if (!raf && !document.hidden) raf = requestAnimationFrame(frame); }
function stop() { if (raf) { cancelAnimationFrame(raf); raf = 0; } }
document.addEventListener('visibilitychange', function () { document.hidden ? stop() : start(); });
window.addEventListener('resize', function () { resize(); seedAndStart(); });
window.addEventListener('mousemove', function (e) {
mx = (e.clientX / W - 0.5) * 2; my = (e.clientY / H - 0.5) * 2;
});
resize(); seedAndStart();
return { stop: stop, start: start };
}
/* ============ ② 鼠标聚光 + ③ 自定义光标 ============ */
function pointerFx() {
if (staticMode || reduceMotion || !finePointer) return null;
html.classList.add('fx-on');
var spot = document.getElementById('spotlight');
var dot = document.querySelector('.cursor-dot');
var ring = document.querySelector('.cursor-ring');
var tx = innerWidth / 2, ty = innerHeight / 2, sx = tx, sy = ty, rx = tx, ry = ty, raf = 0;
function loop() {
sx += (tx - sx) * 0.12; sy += (ty - sy) * 0.12;
rx += (tx - rx) * 0.24; ry += (ty - ry) * 0.24;
if (spot) spot.style.transform = 'translate3d(' + sx + 'px,' + sy + 'px,0)';
if (dot) dot.style.transform = 'translate3d(' + tx + 'px,' + ty + 'px,0)';
if (ring) ring.style.transform = 'translate3d(' + rx + 'px,' + ry + 'px,0)';
// 三轴全部收敛到指针(<0.5px)即休眠,下次 mousemove 重启——避免指针静止后 rAF 永不停转
if (Math.abs(tx - sx) < 0.5 && Math.abs(ty - sy) < 0.5 &&
Math.abs(tx - rx) < 0.5 && Math.abs(ty - ry) < 0.5) {
raf = 0;
return;
}
raf = requestAnimationFrame(loop);
}
window.addEventListener('mousemove', function (e) {
tx = e.clientX; ty = e.clientY;
if (!raf) raf = requestAnimationFrame(loop);
}, { passive: true });
if (!raf) raf = requestAnimationFrame(loop);
// 悬停可交互元素:光环放大
var hot = 'a,button,[data-tilt],.scene-chips span,.benefit-item';
document.addEventListener('mouseover', function (e) {
if (e.target.closest && e.target.closest(hot) && ring) ring.classList.add('is-hot');
});
document.addEventListener('mouseout', function (e) {
if (e.target.closest && e.target.closest(hot) && ring) ring.classList.remove('is-hot');
});
return true;
}
/* ============ ④ 点击爆炸 ============ */
function explosion() {
var layer = document.getElementById('fx-layer');
if (!layer) return null;
var ctx = layer.getContext('2d');
var parts = [], rings = [], raf = 0;
var COLORS = ['#5B9BFF', '#2E7CF6', '#5BE9FF', '#FFFFFF', '#9D8CFF'];
function resize() {
layer.width = innerWidth * dpr; layer.height = innerHeight * dpr;
layer.style.width = innerWidth + 'px'; layer.style.height = innerHeight + 'px';
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
window.addEventListener('resize', resize); resize();
function burst(x, y, warm) {
if (staticMode || reduceMotion) return;
var n = 34 + Math.round(Math.random() * 10);
for (var i = 0; i < n; i++) {
var ang = Math.random() * Math.PI * 2;
var sp = 1.6 + Math.random() * 7.2;
parts.push({
x: x, y: y,
vx: Math.cos(ang) * sp, vy: Math.sin(ang) * sp - 1.4,
r: 1.1 + Math.random() * 2.6,
life: 1, decay: 0.016 + Math.random() * 0.02,
col: warm && Math.random() < 0.4 ? '#FFB37A' : COLORS[(Math.random() * COLORS.length) | 0]
});
}
rings.push({ x: x, y: y, r: 6, life: 1, warm: !!warm });
var el = document.createElement('div');
el.className = 'shockwave' + (warm ? ' warm' : '');
el.style.left = x + 'px'; el.style.top = y + 'px';
document.body.appendChild(el);
setTimeout(function () { el.remove(); }, 700);
if (!raf) raf = requestAnimationFrame(frame);
}
function frame() {
ctx.clearRect(0, 0, innerWidth, innerHeight);
var i, p;
for (i = parts.length - 1; i >= 0; i--) {
p = parts[i];
p.x += p.vx; p.y += p.vy; p.vy += 0.14; p.vx *= 0.985; p.vy *= 0.985;
p.life -= p.decay;
if (p.life <= 0) { parts.splice(i, 1); continue; }
ctx.globalAlpha = Math.max(0, p.life);
ctx.fillStyle = p.col;
ctx.beginPath(); ctx.arc(p.x, p.y, p.r * p.life + 0.4, 0, Math.PI * 2); ctx.fill();
}
for (i = rings.length - 1; i >= 0; i--) {
var r = rings[i];
r.r += 7.5; r.life -= 0.045;
if (r.life <= 0) { rings.splice(i, 1); continue; }
ctx.globalAlpha = Math.max(0, r.life * 0.8);
ctx.strokeStyle = r.warm ? '#FFB37A' : '#5BE9FF';
ctx.lineWidth = 2 * r.life;
ctx.beginPath(); ctx.arc(r.x, r.y, r.r, 0, Math.PI * 2); ctx.stroke();
}
ctx.globalAlpha = 1;
raf = (parts.length || rings.length) ? requestAnimationFrame(frame) : 0;
}
window.addEventListener('pointerdown', function (e) {
burst(e.clientX, e.clientY, Math.random() < 0.16);
});
return { burst: burst };
}
/* ============ ⑤ 卡片 3D 倾斜 + 指针辉光 ============ */
function tilt() {
if (staticMode || reduceMotion || !finePointer) return null;
var els = document.querySelectorAll('[data-tilt]');
// 矩形缓存:mouseenter 时读一次,mousemove 直接用;滚动/缩放/离开时失效
// (15 张卡 × 每秒数十次 mousemove,每次都 getBoundingClientRect 会强制布局)
function invalidate() {
for (var i = 0; i < els.length; i++) els[i]._rect = null;
}
els.forEach(function (el) {
el.addEventListener('mouseenter', function () {
el._rect = el.getBoundingClientRect();
});
el.addEventListener('mousemove', function (e) {
var r = el._rect;
if (!r) r = el._rect = el.getBoundingClientRect();
var px = (e.clientX - r.left) / r.width, py = (e.clientY - r.top) / r.height;
el.style.setProperty('--mx', (px * 100).toFixed(1) + '%');
el.style.setProperty('--my', (py * 100).toFixed(1) + '%');
var rx = (0.5 - py) * 5, ry = (px - 0.5) * 5;
el.style.transform = 'perspective(900px) rotateX(' + rx.toFixed(2) + 'deg) rotateY(' + ry.toFixed(2) + 'deg) translateY(-4px)';
});
el.addEventListener('mouseleave', function () {
el._rect = null;
el.style.transform = 'perspective(900px) rotateX(0deg) rotateY(0deg)';
});
});
window.addEventListener('scroll', invalidate, { passive: true });
window.addEventListener('resize', invalidate);
return els.length;
}
/* ============ ⑥ 数字滚动 ============ */
function countUp() {
var els = Array.prototype.slice.call(document.querySelectorAll('[data-count]'));
if (!els.length) return 0;
function setFinal(el) { el.textContent = el.getAttribute('data-count') + (el.getAttribute('data-suffix') || ''); }
if (staticMode || reduceMotion || !('IntersectionObserver' in window)) {
els.forEach(setFinal); return els.length;
}
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (!en.isIntersecting) return;
var el = en.target, end = parseFloat(el.getAttribute('data-count')) || 0;
var suffix = el.getAttribute('data-suffix') || '';
var t0 = performance.now(), dur = 1100;
(function step(now) {
var k = Math.min(1, (now - t0) / dur);
var eased = 1 - Math.pow(1 - k, 3);
el.textContent = Math.round(end * eased) + suffix;
if (k < 1) requestAnimationFrame(step); else el.textContent = end + suffix;
})(t0);
io.unobserve(el);
});
}, { threshold: 0.4 });
els.forEach(function (el) { io.observe(el); });
return els.length;
}
/* ============ 启动 ============ */
function boot() {
starfield();
pointerFx();
explosion();
tilt();
countUp();
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
else boot();
})();