CameraImage.jsx 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. import { h } from 'preact';
  2. import { ApiHost, Config } from '../context';
  3. import { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'preact/hooks';
  4. export default function CameraImage({ camera, onload, searchParams = '' }) {
  5. const config = useContext(Config);
  6. const apiHost = useContext(ApiHost);
  7. const [availableWidth, setAvailableWidth] = useState(0);
  8. const [loadedSrc, setLoadedSrc] = useState(null);
  9. const containerRef = useRef(null);
  10. const { name, width, height } = config.cameras[camera];
  11. const aspectRatio = width / height;
  12. const resizeObserver = useMemo(() => {
  13. return new ResizeObserver((entries) => {
  14. window.requestAnimationFrame(() => {
  15. if (Array.isArray(entries) && entries.length) {
  16. setAvailableWidth(entries[0].contentRect.width);
  17. }
  18. });
  19. });
  20. }, [setAvailableWidth, width]);
  21. useEffect(() => {
  22. if (!containerRef.current) {
  23. return;
  24. }
  25. resizeObserver.observe(containerRef.current);
  26. }, [resizeObserver, containerRef.current]);
  27. const scaledHeight = useMemo(() => Math.min(Math.ceil(availableWidth / aspectRatio), height), [
  28. availableWidth,
  29. aspectRatio,
  30. height,
  31. ]);
  32. const img = useMemo(() => new Image(), [camera]);
  33. img.onload = useCallback(
  34. (event) => {
  35. const src = event.path[0].currentSrc;
  36. setLoadedSrc(src);
  37. onload && onload(event);
  38. },
  39. [searchParams, onload]
  40. );
  41. useEffect(() => {
  42. if (!scaledHeight) {
  43. return;
  44. }
  45. img.src = `${apiHost}/api/${name}/latest.jpg?h=${scaledHeight}${searchParams ? `&${searchParams}` : ''}`;
  46. }, [apiHost, name, img, searchParams, scaledHeight]);
  47. return (
  48. <div ref={containerRef}>
  49. {loadedSrc ? <img width={scaledHeight * aspectRatio} height={scaledHeight} src={loadedSrc} alt={name} /> : null}
  50. </div>
  51. );
  52. }