You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让JS熔岩灯背景颜色随明暗主题切换?适配媒体查询

熔岩灯明暗模式颜色同步问题解决

我用JavaScript实现了熔岩灯效果作为页面背景,同时通过CSS媒体查询做了明暗模式切换按钮。现在主题切换能正常修改页面配色,但JS生成的熔岩灯颜色并未同步变化。想知道该用JavaScript还是CSS处理,目标是让熔岩灯颜色随暗色模式媒体查询同步切换。

浅色模式
深色模式

原实现代码

// 配色模式控制
const btn = document.querySelector(".btn-toggle");
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");

const currentTheme = localStorage.getItem("theme");
if (currentTheme == "dark") {
  document.body.classList.toggle("dark-theme");
} else if (currentTheme == "light") {
  document.body.classList.toggle("light-theme");
}

btn.addEventListener("click", function () {
  if (prefersDarkScheme.matches) {
    document.body.classList.toggle("light-theme");
    var theme = document.body.classList.contains("light-theme")
      ? "light"
      : "dark";
  } else {
    document.body.classList.toggle("dark-theme");
    var theme = document.body.classList.contains("dark-theme")
      ? "dark"
      : "light";
  }
  localStorage.setItem("theme", theme);
});

// 创建PixiJS应用,设置背景色并自适应窗口大小
const app = new PIXI.Application({
  background: "yellow",
  resizeTo: window
});

// 将应用视图添加到DOM
document.body.appendChild(app.view);

// 基础设置
app.stage.eventMode = "dynamic";
app.stage.hitArea = app.screen;

// 创建熔岩球容器
const container = new PIXI.Container();
app.stage.addChild(container);

// 熔岩球配色数组
const colors = ["0x4361ee", "0x3a0ca3", "0x7209b7", "0xf72585"];
const blurFilter2 = new PIXI.BlurFilter();

// 生成随机熔岩球的辅助函数
function randomCircle() {
  const circle = new PIXI.Graphics();
  const randomColor = Math.floor(Math.random() * colors.length);
  circle.beginFill(colors[randomColor]);
  circle.drawCircle(0, 0, (Math.random() * app.screen.width) / 4);
  circle.endFill();
  const texture = app.renderer.generateTexture(circle);
  return { texture };
}

// 熔岩球数组
const orbs = [];

// 熔岩球参数
let trackSpeed = 0.03;
let rotationSpeed = 0.01;

// 设置熔岩球边界
const padding = app.screen.width / 4;
const bounds = new PIXI.Rectangle(
  -padding,
  -padding,
  app.screen.width + padding * 2,
  app.screen.height + padding * 2
);

// 创建20个随机熔岩球
for (let i = 0; i < 20; i++) {
  const orb = PIXI.Sprite.from(randomCircle().texture);

  orb.anchor.set(0.5);
  container.addChild(orb);

  orb.direction = Math.random() * Math.PI * 2;
  orb.speed = 1;
  orb.turnSpeed = Math.random() - 0.8;

  orb.x = Math.random() * bounds.width;
  orb.y = Math.random() * bounds.height;

  orb.scale.set(1 + Math.random() * 0.3);
  orb.original = new PIXI.Point();
  orb.original.copyFrom(orb.scale);

  orbs.push(orb);
}

// 给熔岩球添加模糊效果
container.filters = [blurFilter2];
blurFilter2.blur = 300;
blurFilter2.quality = 35;

// 鼠标位置存储
let mouseX;
let mouseY;

app.stage.on("pointermove", (event) => {
  mouseX = event.global.x;
  mouseY = event.global.y;
});

// 动画计时器变量
let count = 0;

// 监听动画更新
app.ticker.add((delta) => {
  count += 0.02;

  // 动画逻辑
  for (let i = 0; i < orbs.length; i++) {
    const orb = orbs[i];

    orb.direction += orb.turnSpeed * 0.01;
    orb.x += Math.sin(orb.direction) * orb.speed;
    orb.y += Math.cos(orb.direction) * orb.speed;

    orb.rotation = -orb.direction - Math.PI / 2;
    orb.scale.x = orb.original.x + Math.sin(count) * 0.2;

    // 边界循环处理
    if (orb.x < bounds.x) {
      orb.x += bounds.width;
    } else if (orb.x > bounds.x + bounds.width) {
      orb.x -= bounds.width;
    }

    if (orb.y < bounds.y) {
      orb.y += bounds.height;
    } else if (orb.y > bounds.y + bounds.height) {
      orb.y -= bounds.height;
    }
  }
});

解决方案:用JavaScript处理同步

因为熔岩灯是PixiJS生成的Canvas内容,CSS无法直接控制Canvas内部的图形颜色,所以必须通过JavaScript监听主题变化并同步更新熔岩灯的配色。具体步骤如下:

  1. 定义双配色方案
    为明暗模式分别准备对应的熔岩球配色:

    // 浅色模式配色
    const lightColors = ["0x4361ee", "0x3a0ca3", "0x7209b7", "0xf72585"];
    // 深色模式配色
    const darkColors = ["0x0d1b2a", "0x1b263b", "0x415a77", "0x778da9"];
    // 根据当前主题初始化配色
    let currentColors = (prefersDarkScheme.matches || currentTheme === "dark") ? darkColors : lightColors;
    
  2. 更新熔岩球生成函数
    修改randomCircle函数,使用当前主题对应的配色数组:

    function randomCircle() {
      const circle = new PIXI.Graphics();
      const randomColor = Math.floor(Math.random() * currentColors.length);
      circle.beginFill(currentColors[randomColor]);
      circle.drawCircle(0, 0, (Math.random() * app.screen.width) / 4);
      circle.endFill();
      const texture = app.renderer.generateTexture(circle);
      return { texture };
    }
    
  3. 添加熔岩球重绘函数
    用于主题切换时重新生成所有熔岩球:

    function refreshLavaLampColors() {
      // 清空旧熔岩球
      container.removeChildren();
      orbs.length = 0;
      // 重新创建20个熔岩球
      for (let i = 0; i < 20; i++) {
        const orb = PIXI.Sprite.from(randomCircle().texture);
        orb.anchor.set(0.5);
        container.addChild(orb);
        orb.direction = Math.random() * Math.PI * 2;
        orb.speed = 1;
        orb.turnSpeed = Math.random() - 0.8;
        orb.x = Math.random() * bounds.width;
        orb.y = Math.random() * bounds.height;
        orb.scale.set(1 + Math.random() * 0.3);
        orb.original = new PIXI.Point();
        orb.original.copyFrom(orb.scale);
        orbs.push(orb);
      }
      // 同步背景色
      app.renderer.backgroundColor = currentTheme === "dark" ? 0x121212 : 0xffffe0;
    }
    
  4. 监听主题切换事件
    包括按钮点击和系统主题变化:

    // 修改按钮点击事件,添加配色更新逻辑
    btn.addEventListener("click", function () {
      let theme;
      if (prefersDarkScheme.matches) {
        document.body.classList.toggle("light-theme");
        theme = document.body.classList.contains("light-theme") ? "light" : "dark";
      } else {
        document.body.classList.toggle("dark-theme");
        theme = document.body.classList.contains("dark-theme") ? "dark" : "light";
      }
      // 更新当前配色
      currentColors = theme === "dark" ? darkColors : lightColors;
      // 重绘熔岩灯
      refreshLavaLampColors();
      localStorage.setItem("theme", theme);
    });
    
    // 监听系统主题变化
    prefersDarkScheme.addEventListener("change", (e) => {
      const newTheme = e.matches ? "dark" : "light";
      currentColors = newTheme === "dark" ? darkColors : lightColors;
      // 同步body类名和本地存储
      document.body.classList.remove("light-theme", "dark-theme");
      document.body.classList.add(`${newTheme}-theme`);
      localStorage.setItem("theme", newTheme);
      // 重绘熔岩灯
      refreshLavaLampColors();
    });
    
  5. 初始化时匹配主题
    在初始创建熔岩球前,确保currentColors已根据当前主题正确设置,保证初始状态的熔岩灯颜色和主题匹配。

内容的提问来源于stack exchange,提问作者Rob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 16:12:05