Im using a lavalamp javascript effect for a background. I Have also implemented a dark/light mode toggle with CSS media query. The toggle works as expected, switching the colour theme but not the javascript lavalamp colours. Would this be handled in the javascript or in the CSS?
The desired effect would allow lavalamp colours also toggle and be set by the dark mode media query.
https://codepen.io/cleaton/pen/NWJMyGG
// Colour mode controls
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);
});
// Create a PixiJS application of type cavas with specify background color and make it resizes to the iframe window
const app = new PIXI.Application({
background: "yellow",
resizeTo: window
});
// Adding the application's view to the DOM
document.body.appendChild(app.view);
// Basic settings
app.stage.eventMode = "dynamic";
app.stage.hitArea = app.screen;
// Create container for orbs
const container = new PIXI.Container();
app.stage.addChild(container);
// Make a nice color pallete for our random orbs
const colors = ["0x4361ee", "0x3a0ca3", "0x7209b7", "0xf72585"];
const blurFilter2 = new PIXI.BlurFilter();
// Helper Functions
function randomCircle() {
const circle = new PIXI.Graphics();
// create random circle
const randomColor = Math.floor(Math.random() * colors.length);
circle.beginFill(colors[randomColor]);
circle.drawCircle(0, 0, (Math.random() * app.screen.width) / 4);
circle.endFill();
// generateTexture converts a graphic to a texture, which can be used to
// create a sprite
const texture = app.renderer.generateTexture(circle);
return {
texture
};
}
// Orbs
const orbs = [];
// orb vars
let trackSpeed = 0.03;
let rotationSpeed = 0.01;
// set wrapping boundaries (invisible rectangle) to be roughly equal to orb size
const padding = app.screen.width / 4;
const bounds = new PIXI.Rectangle(
-padding,
-padding,
app.screen.width + padding * 2,
app.screen.height + padding * 2
);
// create 20 orbs with randomized variables
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);
}
// Blur the orbs
// This seems like it could get pretty heavy pretty fast, low quality is fast but not very smooth
container.filters = [blurFilter2];
blurFilter2.blur = 300;
blurFilter2.quality = 35;
// Events
// store cursor coords
let mouseX;
let mouseY;
app.stage.on("pointermove", (event) => {
mouseX = event.global.x;
mouseY = event.global.y;
});
// Ticker variables
let count = 0;
// Listen for animate update
app.ticker.add((delta) => {
count += 0.02;
// animate orbs
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;
// wrap the orbs around as they hit the bounds
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;
}
}
});
2
Answers
From a quick test, the CSS
filter
property appears to work on<canvas>
elements.In your codepen example, you would add the following:
but… there’s a got’cha: the z-index of the canvas changes when applying the filter. Adding
position: absolute;
andz-index: -1;
will prevent the canvas from covering the page.Converting my comments into an answer.
Drawing on a canvas makes it quite hard to alter the colors, you’ll need to
Since you’ve already placed all the orbs in a
container
, we can remove the orbs with the following method:[docs]
Then, call the draw logic again, I’ve moved that to a seperate function
addOrbs
which callsrandomCircle
as you made, but I’ve added adarkColors
array which is chosen over thecolors
array iftheme === 'dark'
.I’ve removed the animation for this demo to reduce the amount of code.
Some parts could also still be optimised, but this is just to share the idea.
Hope it helps.