import {
Application,
Container,
FederatedPointerEvent,
Graphics,
Rectangle,
Sprite,
} from "pixi.js";
async function setup(app: Application) {
await app.init({ background: "#111111", resizeTo: window, antialias: true });
document.getElementById("pixi-container")!.appendChild(app.canvas);
}
function makeCyanPointer(): Graphics {
const size = 20;
const height = (Math.sqrt(3) / 2) * size;
const g = new Graphics();
g.fill(0x00ffff);
g.moveTo(0, (-height * 2) / 3);
g.lineTo(-size / 2, height / 3);
g.lineTo(size / 2, height / 3);
g.closePath();
g.fill();
return g;
}
function addBoxes(app: Application): void {
const cx = app.screen.width / 2;
const cy = app.screen.height / 2;
const size = 320;
const redBox = new Graphics();
redBox.rect(-size / 2, -size / 2, size, size);
redBox.setStrokeStyle({ color: 0xff4444, width: 3 });
redBox.stroke();
redBox.hitArea = new Rectangle(-size / 2, -size / 2, size, size);
redBox.x = cx - 220;
redBox.y = cy;
const greenBox = new Graphics();
greenBox.rect(-size / 2, -size / 2, size, size);
greenBox.setStrokeStyle({ color: 0x44ff88, width: 3 });
greenBox.stroke();
greenBox.hitArea = new Rectangle(-size / 2, -size / 2, size, size);
greenBox.x = cx + 220;
greenBox.y = cy;
const texture = app.renderer.generateTexture(makeCyanPointer());
// Red box: canvas-drawn sprite follows the cursor (native cursor hidden)
const cyanPointer = new Sprite(texture);
cyanPointer.anchor.set(0.5, 0.5);
cyanPointer.visible = false;
redBox.eventMode = "static";
redBox.cursor = "none";
redBox.on("pointerenter", () => {
cyanPointer.visible = true;
});
redBox.on("pointerleave", () => {
cyanPointer.visible = false;
});
// Green box: native cursor replaced with the same icon via CSS data URL
const extractedCanvas = app.renderer.extract.canvas(texture);
const dataURL = (extractedCanvas as HTMLCanvasElement).toDataURL("image/png");
const hx = Math.round(texture.width / 2);
const hy = 0;
greenBox.eventMode = "static";
greenBox.cursor = `url("${dataURL}") ${hx} ${hy}, default`;
const world = new Container();
world.eventMode = "static";
world.hitArea = new Rectangle(-100000, -100000, 200000, 200000);
world.addChild(redBox);
world.addChild(greenBox);
world.addChild(cyanPointer);
app.stage.addChild(world);
world.on("pointermove", (e: FederatedPointerEvent) => {
const pos = e.getLocalPosition(world);
cyanPointer.x = pos.x;
cyanPointer.y = pos.y;
});
}
(async () => {
const app = new Application();
await setup(app);
addBoxes(app);
})();