I have simply a button 'Create screenshot' and live stream 'rtsp-relay'. When that button is cliked it should take a snapshot or screenshot and show that under image, my problem is it shows blank image, i have tried to do this in two ways and both of them show blank image (live stream works fine).
code works fine with a simple drawing on a canvas:
https://codesandbox.io/s/copy-canvas-c5l8et-c5l8et?file=/src/App.js
but when there is live video playing, it shows blank image when 'Create screenshot' button is clicked.
Any idea why ?
two ways i have tried:
1:
import React, { useRef, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { loadPlayer } from 'rtsp-relay/browser';
const StreamVideo = () => {
const canvasRef = useRef(null);
const createScreenshot = () =>
new Promise((resolve) => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
context.drawImage(canvasRef.current, 0, 0);
canvas.toBlob((blob) => {
const src = URL.createObjectURL(blob);
console.log('src', src);
const image = new Image();
image.onload = () => resolve(image);
image.src = src;
});
});
const handleButtonClick = () => {
createScreenshot().then((image) => {
document.body.append(image);
});
};
useEffect(() => {
if (!canvasRef.current) throw new Error('Ref is null');
loadPlayer({
url: 'ws://localho.../api/stream',
canvas: canvasRef.current,
});
}, []);
return (
<div style={{ border: '5px solid red' }}>
<canvas ref={canvasRef} style={{ width: '100%', height: '100%' }} />
<button onClick={handleButtonClick}>Create screenshot</button>
</div>
);
};
export default StreamVideo;
2:
import React, { useRef, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { loadPlayer } from 'rtsp-relay/browser';
const StreamVideo = () => {
const canvasRef = useRef(null);
const createScreenshot = () =>
new Promise((resolve) => {
canvasRef.current.toBlob((blob) => {
const src = URL.createObjectURL(blob);
const image = new Image();
image.onload = () => resolve(image);
image.src = src;
});
});
const handleButtonClick = () => {
createScreenshot().then((image) => {
document.body.append(image);
});
};
useEffect(() => {
if (!canvasRef.current) throw new Error('Ref is null');
loadPlayer({
url: 'ws://localh.../api/stream',
canvas: canvasRef.current,
});
}, []);
return (
<div style={{ border: '5px solid red' }}>
<canvas ref={canvasRef} style={{ width: '100%', height: '100%' }} />
<button onClick={handleButtonClick}>Create screenshot</button>
</div>
);
};
export default StreamVideo;
image:
Related
I just starting to learn React. I've been having issues with the react-image-crop package. The document they have was not newbie friendly, I just barely made it work at this point. Now my issue is that the result cropped image is totally different from the user's selection. My thought is that it might be caused by the scaling of the original image when you select the cropping area. I have limited the window size because some people might choose to upload a large image. If you have any experience using this package, please let me know what I could do to fix this issue, thank you.
import "react-image-crop/dist/ReactCrop.css";
import React, { useState, useRef } from "react";
import ReactCrop from "react-image-crop";
export default function ImageUploader(props) {
const [imgSrc, setImgSrc] = useState();
const [crop, setCrop] = useState();
const [originalImg, setOrgImg] = useState(null);
const imgRef = useRef(null);
const handleImage = async (event) => {
setImgSrc(URL.createObjectURL(event.target.files[0]));
};
const getCroppedImg = async (image, pixelCrop) => {
try {
const canvas = document.createElement("canvas");
console.log(crop);
canvas.width = pixelCrop.width;
canvas.height = pixelCrop.height;
const ctx = canvas.getContext("2d");
// Here is what I think where the problem is at:
ctx.drawImage(
image,
pixelCrop.x,
pixelCrop.y,
pixelCrop.width,
pixelCrop.height,
0,
0,
pixelCrop.width,
pixelCrop.height
);
const base64Image = await canvas.toDataURL("image/jpeg", 1);
props.setCurrentImages(pushImage(props.images, base64Image));
console.log(base64Image);
console.log(props.images);
} catch (e) {
console.log(e);
}
};
function pushImage(array, newImage) {
if (array.lengh === 0) return [newImage];
return [...array, newImage];
}
function handleCropButton() {
getCroppedImg(imgRef.current, crop);
props.setUploadImg(false);
}
return (
<div style={{ height: "600px" }}>
<div>
<input type="file" onChange={handleImage} accept="image/*" />
<button onClick={handleCropButton}>Crop</button>
</div>
<ReactCrop
crop={crop}
aspect={1}
onChange={(c) => setCrop(c)}
onComplete={(crop) => setCrop(crop)}
>
<img
src={imgSrc}
alt=""
style={{ height: "600px" }}
onLoad={() => {
setOrgImg({
height: imgRef.current.clientHeight,
width: imgRef.current.clientWidth,
});
}}
ref={imgRef}
/>
</ReactCrop>
</div>
);
}
I am trying to create a single button in my React app that allows me to upload an image, and then using the web share api, share the uploaded image to whatever the user wants to...
My issue is, I cannot seem to execute the two one after the other.
Upload first, share second.
I have tried maybe using a setTimeout, or a setInterval, or waiting for a state to equal true, but I cant seem to figure it out.
Here is my code:
import { useRef, useState } from "react";
import { toBlob } from "html-to-image";
import Button from '#mui/material/Button';
import { styled } from '#mui/material/styles';
import IconButton from '#mui/material/IconButton';
import PhotoCamera from '#mui/icons-material/PhotoCamera';
import Stack from '#mui/material/Stack';
import '../styles/styles.css'
export default function Model() {
const [image, setImage] = useState(null);
const handleUpload = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onloadend = () => {
setImage(reader.result);
}
reader.readAsDataURL(file);
}
const handleShare = async () => {
console.log=("share clicked")
const base64url = image
const blob = await (await fetch(base64url)).blob();
const file = new File([blob], 'fileName.png', { type: blob.type });
navigator.share({
title: 'Test Title',
text: ' Experience',
files: [file],
})
}
const ColorButton = styled(Button)(({ theme }) => ({
borderRadius: 50,
border: '4px solid',
borderColor: '#F0B152',
backgroundColor: '#000000',
color: 'white',
}));
return (
<div className="App">
<Button variant="contained" component="label">
Upload
<input hidden accept="image/*" multiple type="file" onChange={handleUpload} onClick={handleShare}/>
</Button>
</div>
);
}
You can move the handleShare function inside an useEffect hook and add image as a dependency. So that every time the setImage function is executed the useEffect hook will trigger the handleShare function.
import { useEffect, useState } from "react";
import Button from "#mui/material/Button";
export default function App() {
const [image, setImage] = useState(null);
useEffect(() => {
if (image) {
const handleShare = async () => {
const base64url = image;
const blob = await (await fetch(base64url)).blob();
const file = new File([blob], "fileName.png", { type: blob.type });
navigator.share({
title: "Test Title",
text: " Experience",
files: [file]
});
};
handleShare();
}
}, [image]);
const handleUpload = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onloadend = () => {
setImage(reader.result);
};
reader.readAsDataURL(file);
};
return (
<div className="App">
<Button variant="contained" component="label">
Upload
<input
hidden
accept="image/*"
multiple
type="file"
onChange={handleUpload}
/>
</Button>
</div>
);
}
Every time you enter this url: https://picsum.photos/200, is shown a different image. I want my react component to render every 5 seconds a different image with this url, but I can't do it. This is my code:
import { useEffect, useState } from "react";
const VariableImage = () => {
const imageUrl = "https://picsum.photos/200";
const [image, setImage] = useState(imageUrl);
useEffect(() => {
setInterval(() => {
const newImage = new Image();
newImage.src = imageUrl;
setImage(imageUrl);
}, 5000);
}, [imageUrl]);
return (
<>
<img src={image} alt="scenery" height="200" width="200" />
</>
);
};
export default VariableImage;
An image is shown in first render but later don't change.
If anyone could help me I would be very grateful. Thanks.
Add a dummy randomized query parameter to the external URL so as to force the browser to make a new request (and give you a new image).
Doing new Image isn't helping you any here - you can leave that off entirely.
const { useEffect, useState } = React;
const imageUrl = "https://picsum.photos/200";
const VariableImage = () => {
const [src, setSrc] = useState(imageUrl);
useEffect(() => {
setInterval(() => {
setSrc(imageUrl + '?forcerefresh=' + Math.random());
}, 5000);
}, []);
return <img src={src} alt="scenery" height="200" width="200" />;
};
ReactDOM.createRoot(document.querySelector('.react')).render(<VariableImage />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div class='react'></div>
'
You are adding the same imageUrl(reference) into it, because of which react
is not able to find any changes so it is not updating the state.
'
Example with vanilla js.
const img = document.querySelector("img");
setInterval(() => {
img.src = "https://picsum.photos/200" + "?forcerefresh=" + Math.random();
}, 5000);
<image src="https://picsum.photos/200" />
Please add clearInterval(id) to stop the time when the component unmounts.
const { useEffect, useState } = React;
const imageUrl = "https://picsum.photos/200";
const VariableImage = () => {
const [src, setSrc] = useState(imageUrl);
useEffect(() => {
const id = setInterval(() => {
setSrc(imageUrl + "?forcerefresh=" + Math.random());
}, 5000);
return () => clearInterval(id)
}, []);
return (
<>
<img src={src} alt="scenery" height="200" width="200" />
</>
)
};
I am trying to add an image cropper component to my project, for that using the react-cropper package. But there is a problem that how to add a fixed width and height for cropper box like "width:200px; height:300px;"
import React, { useState } from "react";
import Cropper from "react-cropper";
import "cropperjs/dist/cropper.css";
import "./Demo.css";
type Props = {
LabelName?: string;
};
export const Demo = (props:Props) => {
const {LabelName} = props;
const [image, setImage] = useState("");
const [cropData, setCropData] = useState("#");
const [cropper, setCropper] = useState<any>();
const onChange = (e: any) => {
e.preventDefault();
let files;
if (e.dataTransfer) {
files = e.dataTransfer.files;
} else if (e.target) {
files = e.target.files;
}
const reader = new FileReader();
reader.onload = () => {
setImage(reader.result as any);
};
reader.readAsDataURL(files[0]);
};
const getCropData = () => {
if (typeof cropper !== "undefined") {
setCropData(cropper.getCroppedCanvas().toDataURL());
}
};
return (
<div>
<div style={{ width: "100%" }}>
<input type="file" onChange={onChange} />
<br />
<Cropper
zoomTo={0.5}
initialAspectRatio={1}
preview=".img-preview"
src={image}
viewMode={1}
minCropBoxHeight={10}
minCropBoxWidth={10}
background={false}
responsive={true}
autoCropArea={1}
checkOrientation={false}
onInitialized={(instance) => {
setCropper(instance);
}}
guides={true}
/>
</div>
<br style={{ clear: "both" }} />
</div>
);
};
export default Demo;
Now I can change like this,
But my requirement is to fix that copper size, Please give me a solution to fix this situation.
Size:
minCropBoxWidth: 200,
minCropBoxHeight: 300,
Required aspect ratio:
aspectRatio: 1 / 1.5,
Block change cropBox:
cropBoxResizable: false,
As title says i want to take screenshot of canvas, here what i have done and what problem: I'm using html2canvas to take a snapshot of canvas (stream), simply i have a button 'take a snapshot' and div which contains < h1>this text is viewable< / h1> and under that is live stream(stream works fine), but as you can see from picture below when i click that button it only takes snapshot of that h1 text and video shows blank and in console there is yellow triangle which says 'React_devtools_backend.js:3973 #1 155ms Unable to clone WebGL context as it has preserveDrawingBuffer=false ', i have not set that... , any idea why video is blank ? and how can i change that to 'true' ?
English is not my mother language so could be mistakes
image:
video.js:
import { Height } from '#material-ui/icons';
import React, { useRef, useEffect, useState } from 'react';
import { loadPlayer } from 'rtsp-relay/browser';
const StreamVideo = () => {
const canvasRef = useRef(null);
useEffect(() => {
if (!canvasRef.current) throw new Error('Ref is null');
loadPlayer({
url: 'ws://localhost:.../api/stream',
canvas: canvasRef.current,
});
}, []);
return (
<div style={{ border: '5px solid red' }}>
<canvas ref={canvasRef} style={{ width: '100%', height: '100%' }} />
</div>
);
};
export default StreamVideo;
here using that :
const ref = useRef();
const [pic, setPic] = useState(null);
const createPicHandle = () => {
html2canvas(ref.current).then((canvas) => {
setPic(canvas.toDataURL());
});
};
<button onClick={createPicHandle}>take a snapshot</button>
<div ref={ref}>
<h1>this text is viewable</h1>
<StreamVideo />
</div>
{pic ? (
<img src={pic} style={{ width: '100%', height: 'auto' }} alt="" />
) : null}
if needed here is nodejs:
const express = require('express');
const app = express();
const { proxy, scriptUrl } = require('rtsp-relay')(app);
const handler = proxy({
url: `rtsp://....:.../Streaming/Channels/101`,
verbose: false,
transport: 'tcp',
});
app.ws('/api/stream', handler);
app.get('/', (req, res) =>
res.send(
`hg`,
),
);
app.listen(5000);