Basic CSS Grid based video grid demo
This commit is contained in:
parent
6addd838b4
commit
f9d799ff05
5 changed files with 258 additions and 2 deletions
|
|
@ -26,6 +26,7 @@ import { useConferenceCallManager } from "./ConferenceCallManagerHooks";
|
|||
import { JoinOrCreateRoom } from "./JoinOrCreateRoom";
|
||||
import { LoginOrRegister } from "./LoginOrRegister";
|
||||
import { Room } from "./Room";
|
||||
import { GridDemo } from "./GridDemo";
|
||||
|
||||
export default function App() {
|
||||
const { protocol, host } = window.location;
|
||||
|
|
@ -55,6 +56,9 @@ export default function App() {
|
|||
>
|
||||
<Room manager={manager} />
|
||||
</AuthenticatedRoute>
|
||||
<Route exact path="/grid">
|
||||
<GridDemo />
|
||||
</Route>
|
||||
</Switch>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
90
src/GridDemo.jsx
Normal file
90
src/GridDemo.jsx
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import classNames from "classnames";
|
||||
import { useDrag } from "react-use-gesture";
|
||||
import { useSpring, useTransition, animated } from "@react-spring/web";
|
||||
import styles from "./GridDemo.module.css";
|
||||
|
||||
let tileIdx = 0;
|
||||
|
||||
export function GridDemo() {
|
||||
const [stream, setStream] = useState();
|
||||
const [tiles, setTiles] = useState([]);
|
||||
|
||||
const startWebcam = useCallback(async () => {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
||||
setStream(stream);
|
||||
setTiles([{ stream, key: tileIdx++ }]);
|
||||
}, []);
|
||||
|
||||
const addTile = useCallback(() => {
|
||||
const newStream = stream.clone();
|
||||
setTiles((tiles) => [...tiles, { stream: newStream, key: tileIdx++ }]);
|
||||
}, [stream]);
|
||||
|
||||
const removeTile = useCallback(() => {
|
||||
setTiles((tiles) => {
|
||||
const newArr = [...tiles];
|
||||
newArr.pop();
|
||||
return newArr;
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(tiles);
|
||||
}, [tiles]);
|
||||
|
||||
const tileTransitions = useTransition(tiles, {
|
||||
from: { opacity: 0, scale: 0.5 },
|
||||
enter: { opacity: 1, scale: 1 },
|
||||
leave: { opacity: 0, scale: 0.5 },
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={styles.gridDemo}>
|
||||
<div className={styles.buttons}>
|
||||
{!stream && <button onClick={startWebcam}>Start Webcam</button>}
|
||||
{stream && <button onClick={addTile}>Add Tile</button>}
|
||||
{stream && <button onClick={removeTile}>Remove Tile</button>}
|
||||
</div>
|
||||
<div className={styles.grid}>
|
||||
{tileTransitions((style, tile) => (
|
||||
<ParticipantTile key={tile.key} style={style} {...tile} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ParticipantTile({ style, stream }) {
|
||||
const videoRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
if (stream) {
|
||||
videoRef.current.srcObject = stream;
|
||||
videoRef.current.play();
|
||||
} else {
|
||||
videoRef.current.srcObject = null;
|
||||
}
|
||||
}, [stream]);
|
||||
|
||||
const [{ x, y }, api] = useSpring(() => ({
|
||||
from: { x: 0, y: 0 },
|
||||
config: {
|
||||
tension: 250,
|
||||
},
|
||||
}));
|
||||
|
||||
const bind = useDrag(({ down, movement: [mx, my] }) => {
|
||||
api.start({ x: down ? mx : 0, y: down ? my : 0 });
|
||||
});
|
||||
|
||||
return (
|
||||
<animated.div
|
||||
{...bind()}
|
||||
className={styles.participantTile}
|
||||
style={{ x, y, ...style }}
|
||||
>
|
||||
<video ref={videoRef} playsInline />
|
||||
</animated.div>
|
||||
);
|
||||
}
|
||||
32
src/GridDemo.module.css
Normal file
32
src/GridDemo.module.css
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
.gridDemo {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.grid {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
grid-auto-flow: dense;
|
||||
grid-gap: 8px;
|
||||
justify-items: stretch;
|
||||
padding: 8px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.participantTile {
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
.participantTile video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue