I am trying to crop an image in reactjs without using any library.First the user will upload image and the user will crop the image using raw js and replace the uploaded image.How can i do it without using any library.I want to use the crop component somthing like this in reactjsreference.For me html cansvas seems to be not working how can i do it using html canvas?this is the sandbox link:sandbox
this is the code i did for showing image:
import React, { Component } from "react";
class ReactCrop extends Component {
constructor(props) {
super(props);
this.state = {
file: null,
};
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
file: URL.createObjectURL(event.target.files[0])
})
console.log("ok",this.state.file)
}
saveCroped() {
const width = 500;
const height = 300;
const fileName = e.target.files[0].name;
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onload = event => {
const img = new Image();
img.src = event.target.result;
img.onload = () => {
const elem = document.createElement('canvas');
elem.width = width;
elem.height = height;
const ctx = elem.getContext('2d');
// img.width and img.height will contain the original dimensions
ctx.drawImage(img, 0, 0, width, height);
ctx.canvas.toBlob((blob) => {
const file = new File([blob], fileName, {
type: 'image/jpeg',
lastModified: Date.now()
});
}, 'image/jpeg', 1);
},
reader.onerror = error => console.log(error);
};
}
componentDidMount() {
console.log('componentDidMount colling ...');
}
render() {
return (
<div className="app">
<div style={{width:'450px', margin:'0 auto'}}>
<label htmlFor="file" style={{paddingRight:'80px',marginLeft:'-10px'}}>
<input id="files" type="file" style={{border:'0px',paddingBottom:'12px'}} key={this.state.inputKey}
onChange={this.handleChange} type="file"/>
</label>
{this.state.file === null?
<p></p>
: <img style={{width:'362px',height:'365px',paddingTop:'10px',marginRight:'85px'}} onClick={this.rotate} src={this.state.file}/>
}
</div>
<button type="button" onClick={() => {this.saveCroped()}}>crop</button>
</div>
);
}
}
export default ReactCrop;
Interesting question. Here is a working example: (it wont download in online sandbox, but works fine on independent branches e.g. localhost, see console messages) sandbox
import React, { useRef, useEffect, useState } from "react";
const Crop = function(){
const canvas = useRef();
const file = useRef();
const preview = useRef();
const [imageParams, setImageParams] = useState();
const [context, setContext] = useState();
// Initialize
useEffect(function(){
file.current.onchange = onNewImage;
setContext( canvas.current.getContext('2d') );
},[])
// Draw image on imageParams Change
useEffect(function(){
if(!imageParams) return;
context.drawImage( preview.current, 0, 0, imageParams.width*(700/imageParams.height), 700 )
}, [imageParams])
// Get with and height, replace preview, set params
function onNewImage(){
const newImgUrl = URL.createObjectURL( file.current.files[0] );
const newImage = document.createElement('img');
newImage.setAttribute('src', newImgUrl);
newImage.onload = function(){
const {width, height} = newImage;
const type = file.current.files[0].type
newImage.setAttribute('width', '50px');
preview.current.parentElement.append(newImage);
preview.current.remove();
preview.current = newImage;
setImageParams({ width, height, type });
}
}
// Save image on click
function handleSave(){
canvas.current.toBlob(function(blob){
const anchor = document.createElement('a');
anchor.innerHTML = 'download';
anchor.download = "my_file."+imageParams.type.replace(/^.{1,}\//,'');
anchor.href = (window.webkitURL || window.URL).createObjectURL(blob);
anchor.dataset.downloadurl = [imageParams.type, anchor.download, anchor.href].join(':');
anchor.click();
preview.current.parentElement.append(anchor);
}, imageParams.type);
}
return <>
<div className="input">
<input
ref={file}
type="file"
accept="image/png, image/jpeg" />
<div
style={{width:"100px", display: 'inline-block'}}>
<img
ref={preview}
/>
</div>
<button
onClick={handleSave} >save image</button>
</div>
<div class="canvas">
<canvas
ref={canvas}
width='700px'
height='700px' />
</div>
</>;
}
export default Crop;
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 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 need to have ability to take a picture with desktop camera or mobile phone camera from my web application localhost:3000/user-camera route component. And, please dont write about any native solutions because I'm not working on mobile app.
I have try with react-camera and react-webcam package but nothing works.
https://www.npmjs.com/package/react-webcam
Import React from 'react'
Import Webcam from 'react-webcam'
const videoConstraints = {
width: 1280,
height: 720,
facingMode: "user"
};
const WebcamCapture = () => {
const webcamRef = React.useRef(null);
const capture = React.useCallback(
() => {
const imageSrc = webcamRef.current.getScreenshot();
},
[webcamRef]
);
return (
<>
<Webcam
audio={false}
height={720}
ref={webcamRef}
screenshotFormat="image/jpeg"
width={1280}
videoConstraints={videoConstraints}
/>
<button onClick={capture}>Capture photo</button>
</>
);
};
So, is there a way to do this by using javascript maybe using navigator, or is there a npm package that works with react. Does anyone have experience with this?
Thanks
Try this simple module I created on the fly just to test this interesting feature:
const camera = function () {
let width = 0;
let height = 0;
const createObjects = function () {
const video = document.createElement('video');
video.id = 'video';
video.width = width;
video.width = height;
video.autoplay = true;
document.body.appendChild(video);
const canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.width = width;
canvas.width = height;
document.body.appendChild(canvas);
}
return {
video: null,
context: null,
canvas: null,
startCamera: function (w = 680, h = 480) {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
width = w;
height = h;
createObjects();
this.video = document.getElementById('video');
this.canvas = document.getElementById('canvas');
this.context = this.canvas.getContext('2d');
(function (video) {
navigator.mediaDevices.getUserMedia({video: true}).then(function (stream) {
video.srcObject = stream;
video.play();
});
})(this.video)
}
},
takeSnapshot: function () {
this.context.drawImage(this.video, 0, 0, width, height);
}
}
}();
export default camera;
To use this module first import it as regular es6 module
import camera from './camera.js'
Then call:
camera.startCamera();
camera.takeSnapshot();
OK. I manage to resolve my issue by using navigator and getting media device from there to use. For anyone else who is trying to make something like this, I want you to know that Chrome doesn't allow you to use this before you set your web app to use secure connection HTTPS. In react for testing use set HTTPS=true&&npm start, so you'll start your react app as https and it will show you camera and you can put it anywhere in your component's html tags.
import { useCallback, useRef, useState } from "react";
import Webcam from "react-webcam";
const videoConstraints = {
width: 640,
height: 480,
facingMode: "user",
};
const WebcamCapture = () => {
const webcamRef = useRef<any>(null);
const [imgSrc, setImgSrc] = useState<any>(null);
const capture = useCallback(() => {
const imageSrc = webcamRef.current.getScreenshot();
setImgSrc(imageSrc);
}, [webcamRef, setImgSrc]);
return (
<>
<Webcam
audio={false}
ref={webcamRef}
screenshotFormat="image/jpeg"
videoConstraints={videoConstraints}
minScreenshotWidth={180}
minScreenshotHeight={180}
/>
<button onClick={capture}>Capture Photo</button>
{imgSrc && <img src={imgSrc} alt="img" />}
</>
);
};
I hope to set background image from input local file.
But it occurs 'net::ERR_UNKNOWN_URL_SCHEME' error message.
My input tag:
<input
accept="image/*"
className="input_img"
type="file"
onChange={e => this.uploadImage(e)}
/>
My uploadImage function:
uploadImage = e => {
let node = document.getElementById("result");
node.style.backgroundImage = "url(" + e.target.result + ")";
};
How do I do it?
You could use a FileReader to read the data of the file, and then set the backgroundImage with the result.
Example
class App extends Component {
uploadImage = (e) => {
const { files } = e.target;
if (files.length === 0) {
return;
}
const file = files[0];
const fileReader = new FileReader();
fileReader.onload = () => {
this.background.style.backgroundImage = `url(${fileReader.result})`;
};
fileReader.readAsDataURL(file);
};
render() {
return (
<div>
<input
accept="image/*"
className="input_img"
type="file"
onChange={this.uploadImage}
/>
<div
style={{width: 200, height: 200}}
ref={ref => this.background = ref}
></div>
</div>
);
}
}