pixi-fusion
Version:
This module offers a set of common components needed for playing games.
100 lines (99 loc) • 3.8 kB
JavaScript
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Viewport as ViewportContainer } from "pixi-viewport";
import { Layer } from "../layer";
import { useStage, useWorld } from "../hooks";
import { LayerContext } from "../layer/Layer.context";
import { CameraContext } from "./Camera.context";
export const Camera = ({ children, clampZoom }) => {
const [isReady, setIsReady] = useState(false);
const [followContainer, setFollowContainer] = useState(null);
const [ensureVisibleOptions, setEnsureVisibleOptions] = useState(null);
const { application, size } = useWorld();
const { addObject: addObjectToStage, removeObject: removeObjectFromStage } = useStage();
const [things, setThings] = useState([]);
const viewport = useMemo(() => application
? new ViewportContainer({
worldHeight: size.height,
worldWidth: size.width,
screenHeight: application.canvas.height,
screenWidth: application.canvas.width,
events: application?.renderer?.events
})
: null, [size, application]);
const addObject = useCallback((thing) => {
setThings((oldThings) => [...oldThings, thing]);
}, []);
const removeObject = useCallback((thing) => {
setThings((oldThings) => oldThings.filter(({ uid }) => uid === thing.uid));
}, []);
const conextValue = useMemo(() => ({
addObject,
removeObject
}), [addObject, removeObject]);
useEffect(() => {
if (viewport && clampZoom && application) {
viewport.clampZoom(clampZoom);
}
}, [clampZoom, application, viewport]);
useEffect(() => {
if (viewport) {
if (size.width) {
viewport.width = size.width;
}
if (size.height) {
viewport.height = size.height;
}
}
}, [size, viewport, application]);
useEffect(() => {
if (!application || !viewport) {
return;
}
viewport?.moveCenter(0, 0);
}, [viewport, size, application]);
useEffect(() => {
if (!viewport) {
return;
}
addObjectToStage(viewport);
setIsReady(true);
return () => {
removeObjectFromStage(viewport);
setIsReady(false);
};
}, [viewport, addObjectToStage, removeObjectFromStage]);
useEffect(() => {
if (!viewport || !isReady) {
return;
}
viewport.addChild(...things);
return () => {
viewport.removeChild(...things);
};
}, [things, viewport, isReady]);
useEffect(() => {
if (isReady && viewport && followContainer) {
viewport.follow(followContainer, { radius: 300 });
}
}, [isReady, viewport, followContainer]);
useEffect(() => {
if (isReady && viewport && ensureVisibleOptions) {
viewport.ensureVisible(ensureVisibleOptions.x, ensureVisibleOptions.y, ensureVisibleOptions.width, ensureVisibleOptions.height, ensureVisibleOptions.resizeToFit);
}
}, [isReady, viewport, ensureVisibleOptions]);
const followCallback = useCallback((container) => {
setFollowContainer(container);
}, []);
const ensureVisibleCallback = useCallback((options) => {
setEnsureVisibleOptions(options);
}, []);
const value = useMemo(() => {
return {
follow: followCallback,
ensureVisible: ensureVisibleCallback
};
}, [followCallback, ensureVisibleCallback]);
return (React.createElement(CameraContext.Provider, { value: value },
React.createElement(LayerContext.Provider, { value: conextValue },
React.createElement(Layer, null, children))));
};