CameraImage.jsx 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. import { h } from 'preact';
  2. import ActivityIndicator from './ActivityIndicator';
  3. import { useApiHost, useConfig } from '../api';
  4. import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
  5. import { useResizeObserver } from '../hooks';
  6. export default function CameraImage({ camera, onload, searchParams = '', stretch = false }) {
  7. const { data: config } = useConfig();
  8. const apiHost = useApiHost();
  9. const [hasLoaded, setHasLoaded] = useState(false);
  10. const containerRef = useRef(null);
  11. const canvasRef = useRef(null);
  12. const [{ width: availableWidth }] = useResizeObserver(containerRef);
  13. const { name } = config.cameras[camera];
  14. const { width, height } = config.cameras[camera].detect;
  15. const aspectRatio = width / height;
  16. const scaledHeight = useMemo(() => {
  17. const scaledHeight = Math.floor(availableWidth / aspectRatio);
  18. return stretch ? scaledHeight : Math.min(scaledHeight, height);
  19. }, [availableWidth, aspectRatio, height, stretch]);
  20. const scaledWidth = useMemo(() => Math.ceil(scaledHeight * aspectRatio), [scaledHeight, aspectRatio]);
  21. const img = useMemo(() => new Image(), []);
  22. img.onload = useCallback(
  23. (event) => {
  24. setHasLoaded(true);
  25. if (canvasRef.current) {
  26. const ctx = canvasRef.current.getContext('2d');
  27. ctx.drawImage(img, 0, 0, scaledWidth, scaledHeight);
  28. }
  29. onload && onload(event);
  30. },
  31. [img, scaledHeight, scaledWidth, setHasLoaded, onload, canvasRef]
  32. );
  33. useEffect(() => {
  34. if (scaledHeight === 0 || !canvasRef.current) {
  35. return;
  36. }
  37. img.src = `${apiHost}/api/${name}/latest.jpg?h=${scaledHeight}${searchParams ? `&${searchParams}` : ''}`;
  38. }, [apiHost, canvasRef, name, img, searchParams, scaledHeight]);
  39. return (
  40. <div className="relative w-full" ref={containerRef}>
  41. <canvas data-testid="cameraimage-canvas" height={scaledHeight} ref={canvasRef} width={scaledWidth} />
  42. {!hasLoaded ? (
  43. <div className="absolute inset-0 flex justify-center" style={`height: ${scaledHeight}px`}>
  44. <ActivityIndicator />
  45. </div>
  46. ) : null}
  47. </div>
  48. );
  49. }