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);
Related
import { useState } from 'react';
const AddNote = ({ handleAddNote }) => {
const [noteText, setNoteText] = useState('');
const [jobLink, setJobLink] = useState('');
const characterLimit = 200;
const handleDescChange = (event) => {
if (characterLimit - event.target.value.length >= 0) {
setNoteText(event.target.value);
}
};
const handleJobLinkChange = (event) => {
if (event.target.value.length >= 0) {
setJobLink(event.target.value);
console.log(event.target.value);
}
};
const handleSaveClick = () => {
if (noteText.trim().length > 0) {
handleAddNote(noteText);
setNoteText('');
setJobLink('');
}
};
return (
<div className='note new'>
<textarea
rows='8'
cols='10'
placeholder='Type to add a note...'
value={noteText}
onChange={handleDescChange}
></textarea>
<input style={{
paddingTop: "1.5%",
outline: "none",
color: "black",
marginRight: "0px",
marginLeft: "0px",
paddingRight: "0px",
backgroundColor: "white"
}}
className="form-control"
id="link"
name="link"
placeholder="Link to Job Posting"
value={jobLink}
type="link"
onChange={handleJobLinkChange}
/>
<div className='note-footer'>
<small>
{characterLimit - noteText.length} Remaining
</small>
<button className='save' onClick={handleSaveClick}>
Save
</button>
</div>
</div>
);
};
export default AddNote;
import { MdDeleteForever } from 'react-icons/md';
import AddNote from './AddNote';
const Note = ({ id, link, text, date, handleDeleteNote,}) => {
const go = e => {
link = "https://www.google.com";
e.preventDefault();
window.location.href = link;
}
return (
<div className='note'>
<span>{text}</span>
<div className='note-footer'>
<small>{date}</small>
<div className='note-btn'>
<button onClick={go} style={{ backgroundColor: "#001E49", borderRadius: "10px"
, borderColor: "none", margin: "auto", padding: "8px", marginLeft: "0px"
, marginRight: "0px", width: "fit-content"
, borderColor: "none"}}>Apply Here</button>
</div>
</div>
</div>
);
};
export default Note;
import Note from './Note';
import AddNote from './AddNote';
const NotesList = ({
notes,
handleAddNote,
handleDeleteNote,
}) => {
return (
<div className='notes-list'>
{notes.map((note) => (
<Note
id={note.id}
text={note.text}
date={note.date}
handleDeleteNote={handleDeleteNote}
/>
))}
<AddNote handleAddNote={handleAddNote} />
</div>
);
};
export default NotesList;
import React from 'react'
import './Internships.css'
import { useState, useEffect } from 'react';
import { nanoid } from 'nanoid';
import NotesList from '../components/NotesList';
import Search from '../components/Search';
import Header from '../components/Header';
const Internships = () => {
const [notes, setNotes] = useState([
]);
const [searchText, setSearchText] = useState('');
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
const savedNotes = JSON.parse(
localStorage.getItem('react-notes-app-data')
);
if (savedNotes) {
setNotes(savedNotes);
}
}, []);
useEffect(() => {
localStorage.setItem(
'react-notes-app-data',
JSON.stringify(notes)
);
}, [notes]);
const addNote = (text, link) => {
const date = new Date();
const newNote = {
id: nanoid(),
text: text,
date: date.toLocaleDateString(),
link: link,
};
const newNotes = [...notes, newNote];
setNotes(newNotes);
};
const deleteNote = (id) => {
const newNotes = notes.filter((note) => note.id !== id);
setNotes(newNotes);
};
return (
<div className={`${darkMode && 'dark-mode'}`}>
<div className='container'>
<Header />
<Search handleSearchNote={setSearchText} />
<NotesList
notes={notes.filter((note) =>
note.text.toLowerCase().includes(searchText)
)}
handleAddNote={addNote}
handleDeleteNote={deleteNote}
/>
</div>
</div>
);
};
export default Internships
for the 'link= "www.google.com"'(it is a placeholder) the button redirects me to google.com but I want there to be a user inputted website and it redirects me there. I am not able to pass joblink into note.js and it is not reading it when I did inspect element on the button that was created. I don't know how to fix trying to get an input from the user and passing it into note.js.
I am a little bit confused by how the code was written out on the forum, but it seems like you could set a state for the target value in your input. So do something like
const[site, setSite] = useState("https://www.google.com");
<input style={{
paddingTop: "1.5%",
outline: "none",
color: "black",
marginRight: "0px",
marginLeft: "0px",
paddingRight: "0px",
backgroundColor: "white"
}}
className="form-control"
id="link"
name="link"
placeholder="Link to Job Posting"
value={jobLink}
type="link"
onChange={() => setSite(e.target.value)}
/>
And then use callback props and send it to your Note component, then replace your 'go' link to
link = site
I apologize if that made no sense lol.
From my understanding, you have the component AddNote where the user is able to generate a new job post, and these values saved here are needed in the Note component
Here not only the states are saved in the incorrect component, but when they hit save (handleSaveClick) this happens:
setNoteText('');
setJobLink('');
What I recommend is to have a Notes component, including a notes state array where you'll save a json with noteText and jobLink, and then map those to Note components, passing them as props.
And this Notes component can also contain the AddNote Component so this way you can pass the setNotes hook and efficiently work with dynamic job posts:
const [notes, setNotes] = useState([])
{notes?.length > 0 && notes.map(elem => (<Note noteText={elem.noteText} jobLink={elem.jobLink} />))}
(preferably pass the 'elem' as a whole, maybe you'll add new properties to it and the props quantity will grow)
and with the use of useRef hook:
const handleSaveClick = () => {
handleAddNote(prev => [...prev, {noteLink: noteLinkRef.current.value, jobLink: jobLinkRef.current.value}])
}
Hope this helps to understand, if you feel that I need to expand a little bit more, let me know!
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,
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:
I have been trying to achieve hiding (or slide up) my react native startup app top bar after 3 seconds, then have a button to press to show the top bar, but could not. I've searched online how to do it, but have not seen good solution for it. Please I need help here, the below is snippet code of what I tried doing but it did not work.
<HeaderHomeComponent /> is the top header I created and imported it in my Home screen
const Home = () => {
const camera = useRef();
const [isRecording, setIsRecording] = useState(false);
const [flashMode, setFlashMode] = useState(RNCamera.Constants.FlashMode.off);
const [cameraType, setCameraType] = useState(RNCamera.Constants.Type.front);
const [showHeader, setShowHeader] = useState(true);
const onRecord = async () => {
if (isRecording) {
camera.current.stopRecording();
} else {
setTimeout(() => setIsRecording && camera.current.stopRecording(), 23*1000);
const data = await camera.current.recordAsync();
}
};
setTimeout(()=> setShowHeader(false),3000);
const DisplayHeader = () => {
if(showHeader == true) {
return <HeaderHomeComponent />
} else {
return null;
}
}
// useEffect(() => {
// DisplayHeader();
// }, [])
return (
<View style={styles.container}>
<RNCamera
ref={camera}
type={cameraType}
flashMode={flashMode}
onRecordingStart={() => setIsRecording(true)}
onRecordingEnd={() => setIsRecording(false)}
style={styles.preview}
/>
<TouchableOpacity
style={styles.showHeaderButton}
onPress={() => {
setShowHeader(!showHeader);
}}>
<Button title='Show' />
</TouchableOpacity>
<HeaderHomeComponent />
You were really close.
This is how it should be done:
useEffect(() => {
setTimeout(toggleHeader,3000);
}, []);
const toggleHeader = () => setShowHeader(prev => !prev);
Then inside the "return":
{showHeader && <HeaderHomeComponent />}
As simple as that.
This should help you get started in the right direction, you can use animation based on your preference to this code.
import React, {useState, useEffect} from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import Constants from 'expo-constants';
export default function App()
{
const [showStatusbar, setShowStatusbar] = useState(true)
useEffect(() =>
{
setTimeout(() =>
{
setShowStatusbar(false)
}, 3000)
}, [])
return (
<View style={styles.container}>
{
showStatusbar
? <View style = {styles.statusBar}>
<Text>Status Bar</Text>
</View>
: null
}
<Button title = "Show Status bar" onPress = {() => setShowStatusbar(true)}/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ecf0f1',
},
statusBar:
{
height: 50,
backgroundColor:'lightblue',
justifyContent:'center',
alignItems:'center'
}
});
Instead of setTimeout use Animation.
or
Check this lib : https://www.npmjs.com/package/react-native-animated-hide-view
or
check below answers which might help you
Hide and Show View With Animation in React Native v0.46.
How to take full page screenshot of webview in react native? Already tried "react-native-view-shot " Link but it only takes screenshot of the visible area.
Someone please help.
Thanks
For future readers:
Below is the final script after many attempts, which takes full page screenshot from Webview. I have used ProgressWebView instead of Webview. You can use Webview if you want.
This code works in functional components.
Note: When the page is fully loaded then click on the Take Screenshot button
import React, { useState } from 'react';
import { View, Button } from 'react-native';
import { captureRef } from "react-native-view-shot";
import ProgressWebView from "react-native-progress-webview";
import json5 from 'json5'
import { Dimensions } from 'react-native';
const Report = () => {
const [componentHeight, setComponentHeight] = useState(0)
const [globalComponentHeight, setGlobalComponentHeight] = useState(0)
const [componentHeightFlex, setComponentHeightFlex] = useState(1)
let url = 'https://stackoverflow.com/questions/63708244/webview-full-page-screenshot-in-react-native'
let webview = null;
let count = 0
const injectJS = _ => {
const script = `
let method${count} = _ => {
let documentHeight = document.body.scrollHeight
let data = {componentHeight: documentHeight}
window.ReactNativeWebView.postMessage(JSON.stringify(data))
}
method${count}()`
webview.injectJavaScript(script)
count++
}
const takeScreenshot = _ => {
console.log(globalComponentHeight)
const {height} = Dimensions.get("window")
console.log(height)
if(globalComponentHeight <= height) setComponentHeight(height)
else setComponentHeight(globalComponentHeight)
setComponentHeightFlex(null)
setTimeout(_ => {
captureRef(webview, {
format: "png",
quality: 0.9,
result: "base64"
}).then(
_screenshot => {
console.log(_screenshot)
//First save your screenshot from _screenshot(base64 string). You can send base64 string to your server and save
//Then make the component default as below
setComponentHeight(0)
setComponentHeightFlex(1)
},
error => console.error("Oops, screenshot failed", error)
);
}, 100)
}
return (
<View style={{ marginTop: 40, flex: 1, display: 'flex' }}>
<Button mode='contained' onPress={takeScreenshot} title="Take Screenshot"/>
<View
style={{
height: componentHeight,
flex: componentHeightFlex
}}
>
<ProgressWebView
ref={ref => {
if (ref != null)
webview = ref
}}
bounces={false}
style={{ position: 'relative' }}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={true}
source={{ uri: url }}
startInLoadingState={true}
onLoad={e => injectJS()}
onMessage={e => {
let data = json5.parse(e.nativeEvent.data)
// console.log(data)
setGlobalComponentHeight(parseInt(data.componentHeight))
}}
></ProgressWebView>
</View>
</View>
);
}
export default Report