mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-02-25 18:55:25 -06:00
fix(web): make camera latest.jpg responsive
This commit is contained in:
parent
2beb44b591
commit
f0f3764992
@ -1,6 +1,7 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import Box from './components/Box';
|
import Box from './components/Box';
|
||||||
import Button from './components/Button';
|
import Button from './components/Button';
|
||||||
|
import CameraImage from './components/CameraImage';
|
||||||
import Heading from './components/Heading';
|
import Heading from './components/Heading';
|
||||||
import Switch from './components/Switch';
|
import Switch from './components/Switch';
|
||||||
import { route } from 'preact-router';
|
import { route } from 'preact-router';
|
||||||
@ -27,14 +28,26 @@ export default function CameraMasks({ camera, url }) {
|
|||||||
zones,
|
zones,
|
||||||
} = cameraConfig;
|
} = cameraConfig;
|
||||||
|
|
||||||
|
const resizeObserver = useMemo(
|
||||||
|
() =>
|
||||||
|
new ResizeObserver((entries) => {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
if (Array.isArray(entries) && entries.length) {
|
||||||
|
const scaledWidth = entries[0].contentRect.width;
|
||||||
|
const scale = scaledWidth / width;
|
||||||
|
setImageScale(scale);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
[camera, width, setImageScale]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!imageRef.current) {
|
if (!imageRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const scaledWidth = imageRef.current.width;
|
resizeObserver.observe(imageRef.current);
|
||||||
const scale = scaledWidth / width;
|
}, [resizeObserver, imageRef.current]);
|
||||||
setImageScale(scale);
|
|
||||||
}, [imageRef.current, setImageScale]);
|
|
||||||
|
|
||||||
const [motionMaskPoints, setMotionMaskPoints] = useState(
|
const [motionMaskPoints, setMotionMaskPoints] = useState(
|
||||||
Array.isArray(motionMask)
|
Array.isArray(motionMask)
|
||||||
@ -226,7 +239,7 @@ ${Object.keys(objectMaskPoints)
|
|||||||
|
|
||||||
<Box className="space-y-4">
|
<Box className="space-y-4">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<img ref={imageRef} className="w-full" src={`${apiHost}/api/${camera}/latest.jpg`} />
|
<CameraImage imageRef={imageRef} camera={camera} />
|
||||||
<EditableMask
|
<EditableMask
|
||||||
onChange={handleUpdateEditable}
|
onChange={handleUpdateEditable}
|
||||||
points={editing.subkey ? editing.set[editing.key][editing.subkey] : editing.set[editing.key]}
|
points={editing.subkey ? editing.set[editing.key][editing.subkey] : editing.set[editing.key]}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
import Box from './components/Box';
|
import Box from './components/Box';
|
||||||
|
import CameraImage from './components/CameraImage';
|
||||||
import Events from './Events';
|
import Events from './Events';
|
||||||
import Heading from './components/Heading';
|
import Heading from './components/Heading';
|
||||||
import { route } from 'preact-router';
|
import { route } from 'preact-router';
|
||||||
@ -23,7 +24,6 @@ export default function Cameras() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Camera({ name }) {
|
function Camera({ name }) {
|
||||||
const apiHost = useContext(ApiHost);
|
|
||||||
const href = `/cameras/${name}`;
|
const href = `/cameras/${name}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -32,7 +32,7 @@ function Camera({ name }) {
|
|||||||
href={href}
|
href={href}
|
||||||
>
|
>
|
||||||
<Heading size="base">{name}</Heading>
|
<Heading size="base">{name}</Heading>
|
||||||
<img className="w-full" src={`${apiHost}/api/${name}/latest.jpg`} />
|
<CameraImage camera={name} />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
import CameraImage from './CameraImage';
|
||||||
import { ApiHost, Config } from '../context';
|
import { ApiHost, Config } from '../context';
|
||||||
import { useCallback, useEffect, useContext, useState } from 'preact/hooks';
|
import { useCallback, useEffect, useContext, useState } from 'preact/hooks';
|
||||||
|
|
||||||
export default function AutoUpdatingCameraImage({ camera, searchParams }) {
|
export default function AutoUpdatingCameraImage({ camera, searchParams }) {
|
||||||
const config = useContext(Config);
|
|
||||||
const apiHost = useContext(ApiHost);
|
const apiHost = useContext(ApiHost);
|
||||||
const cameraConfig = config.cameras[camera];
|
|
||||||
|
|
||||||
const [key, setKey] = useState(Date.now());
|
const [key, setKey] = useState(Date.now());
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -17,11 +16,5 @@ export default function AutoUpdatingCameraImage({ camera, searchParams }) {
|
|||||||
};
|
};
|
||||||
}, [key, searchParams]);
|
}, [key, searchParams]);
|
||||||
|
|
||||||
return (
|
return <CameraImage camera={camera} searchParams={`cache=${key}&${searchParams}`} />;
|
||||||
<img
|
|
||||||
className="w-full"
|
|
||||||
src={`${apiHost}/api/${camera}/latest.jpg?cache=${key}&${searchParams}`}
|
|
||||||
alt={`Auto-updating ${camera} image`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
38
web/src/components/CameraImage.jsx
Normal file
38
web/src/components/CameraImage.jsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { h } from 'preact';
|
||||||
|
import { ApiHost, Config } from '../context';
|
||||||
|
import { useCallback, useEffect, useContext, useState } from 'preact/hooks';
|
||||||
|
|
||||||
|
export default function CameraImage({ camera, searchParams = '', imageRef }) {
|
||||||
|
const config = useContext(Config);
|
||||||
|
const apiHost = useContext(ApiHost);
|
||||||
|
const { name, width, height } = config.cameras[camera];
|
||||||
|
|
||||||
|
const aspectRatio = width / height;
|
||||||
|
const innerWidth = parseInt(window.innerWidth, 10);
|
||||||
|
|
||||||
|
const responsiveWidths = [640, 768, 1024, 1280];
|
||||||
|
if (innerWidth > responsiveWidths[responsiveWidths.length - 1]) {
|
||||||
|
responsiveWidths.push(innerWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
const src = `${apiHost}/api/${camera}/latest.jpg`;
|
||||||
|
const { srcset, sizes } = responsiveWidths.reduce(
|
||||||
|
(memo, w, i) => {
|
||||||
|
memo.srcset.push(`${src}?h=${Math.ceil(w / aspectRatio)}&${searchParams} ${w}w`);
|
||||||
|
memo.sizes.push(`(max-width: ${w}) ${Math.ceil((w / innerWidth) * 100)}vw`);
|
||||||
|
return memo;
|
||||||
|
},
|
||||||
|
{ srcset: [], sizes: [] }
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
className="w-full"
|
||||||
|
srcset={srcset.join(', ')}
|
||||||
|
sizes={sizes.join(', ')}
|
||||||
|
src={`${srcset[srcset.length - 1]}`}
|
||||||
|
alt={name}
|
||||||
|
ref={imageRef}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
@ -2,13 +2,9 @@ import { h } from 'preact';
|
|||||||
import { useCallback, useState } from 'preact/hooks';
|
import { useCallback, useState } from 'preact/hooks';
|
||||||
|
|
||||||
export default function Switch({ checked, label, id, onChange }) {
|
export default function Switch({ checked, label, id, onChange }) {
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(() => {
|
||||||
(event) => {
|
onChange(id, !checked);
|
||||||
console.log(event.target.checked, !checked);
|
}, [id, onChange, checked]);
|
||||||
onChange(id, !checked);
|
|
||||||
},
|
|
||||||
[id, onChange, checked]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label for={id} className="flex items-center cursor-pointer">
|
<label for={id} className="flex items-center cursor-pointer">
|
||||||
|
Loading…
Reference in New Issue
Block a user