- added Konvas lib

- added feed from proof of concept
This commit is contained in:
2025-11-21 10:12:42 +00:00
parent 8284b1dd11
commit a21b7bb87e
9 changed files with 200 additions and 7 deletions

View File

@@ -0,0 +1,11 @@
import VideoFeedCard from "./VideoFeedCard";
const CameraGrid = () => {
return (
<div className="grid grid-cols-1 md:grid-cols-2">
<VideoFeedCard />
</div>
);
};
export default CameraGrid;

View File

@@ -0,0 +1,13 @@
import Card from "../../../ui/Card";
import VideoFeedGridPainter from "./VideoFeedGridPainter";
const VideoFeedCard = () => {
return (
<Card className="w-full md:w-[70%]">
<VideoFeedGridPainter />
</Card>
);
};
export default VideoFeedCard;

View File

@@ -0,0 +1,75 @@
import { useRef, type RefObject } from "react";
import { Stage, Layer, Image, Rect } from "react-konva";
import { useCreateVideoSnapshot } from "../hooks/useGetvideoSnapshots";
const VideoFeedGridPainter = () => {
const { latestBitmapRef } = useCreateVideoSnapshot();
const isDrawing = useRef(false);
const rows = 100;
const cols = 100;
const size = 10;
const gap = 0;
const squares = [];
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
squares.push(
<Rect
key={`${row}-${col}`}
x={col * (size + gap)}
y={row * (size + gap)}
width={size}
height={size}
fill="#ddd"
stroke="black"
strokeWidth={0.5}
opacity={0.5}
/>,
);
}
}
const getCoords = (e) => {
isDrawing.current = true;
};
const handleMouseMove = (e) => {
if (!isDrawing.current) {
return;
}
const pos = e.target.getStage().getPointerPosition();
console.log(pos);
};
const handleMouseUp = () => {
isDrawing.current = false;
};
const draw = (bmp: RefObject<ImageBitmap | null>) => {
if (!bmp || !bmp.current) {
return;
} else {
const frame = bmp.current;
return frame;
}
};
return (
<Stage
width={640}
height={360}
onMouseDown={getCoords}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
>
<Layer>
<Image image={draw(latestBitmapRef)} width={640} height={360} />
</Layer>
<Layer opacity={0.3}>{squares}</Layer>
</Stage>
);
};
export default VideoFeedGridPainter;

View File

@@ -0,0 +1,22 @@
import { useQuery } from "@tanstack/react-query";
const getfeed = async () => {
const response = await fetch(`http://100.115.148.59/TargetDetectionColour-preview`, {
signal: AbortSignal.timeout(300000),
cache: "no-store",
});
if (!response.ok) {
throw new Error(`Cannot reach endpoint (${response.status})`);
}
return response.blob();
};
export const useGetVideoFeed = () => {
const videoQuery = useQuery({
queryKey: ["getfeed"],
queryFn: getfeed,
refetchInterval: 500,
});
return { videoQuery };
};

View File

@@ -0,0 +1,25 @@
import { useEffect, useRef } from "react";
import { useGetVideoFeed } from "./useGetVideoFeed";
export const useCreateVideoSnapshot = () => {
const latestBitmapRef = useRef<ImageBitmap | null>(null);
const { videoQuery } = useGetVideoFeed();
const snapShot = videoQuery?.data;
useEffect(() => {
async function createBitmap() {
if (!snapShot) return;
try {
const bitmap = await createImageBitmap(snapShot);
latestBitmapRef.current = bitmap;
} catch (error) {
console.log(error);
}
}
createBitmap();
}, [snapShot]);
return { latestBitmapRef };
};

View File

@@ -1,9 +1,15 @@
import { createFileRoute } from '@tanstack/react-router'
import { createFileRoute } from "@tanstack/react-router";
import CameraGrid from "../features/cameras/components/CameraGrid";
export const Route = createFileRoute('/baywatch')({
export const Route = createFileRoute("/baywatch")({
component: RouteComponent,
})
});
function RouteComponent() {
return <div>Hello "/baywatch"!</div>
return (
<div>
<h2 className="text-xl font-semibold">Cameras</h2>
<CameraGrid />
</div>
);
}

View File

@@ -18,7 +18,7 @@ const Header = () => {
</Link>
<Link to="/baywatch" className="[&.active]:font-bold">
Baywatch
Cameras
</Link>
<Link to="/output" className="[&.active]:font-bold">
Output