I am trying to crop an image before uploading it through an API. I am showing a modal (Dialog) to do this, and using this library react-image-crop to achieve this.
Here is the code snippet:
showCropImageModal() {
const actions = [
<FlatButton
label="Cancel"
primary={true}
onClick={this.handleCancel}
/>,
<FlatButton
label="Crop"
primary={true}
keyboardFocused={true}
onClick={this.handleCropClose}
/>,
];
if (this.state.showImageCropper) {
return (
<div>
<Dialog
title="Crop the image"
actions={actions}
modal={true}
open={this.state.showImageCropper}
autoScrollBodyContent={true}
>
<ReactCrop
src={this.state.selectedImageURL}
crop={this.state.crop}
onComplete={(crop, pixel) => {console.log(crop, pixel)}}
onChange={(crop) => { console.log(crop); this.setState({crop}); }}
/>
</Dialog>
</div>
);
}
}
On "Crop" action I am handling it using the handleCropClose function:
handleCropClose(){
let {selectedFile, crop} = this.state
const croppedImg = this.getCroppedImg(selectedFile, crop.width, crop.height, crop.x, crop.y, 2);
console.log(croppedImg)
this.setState({showImageCropper: false})
}
And here is getCroppedImg code:
getCroppedImg(imgObj, newWidth, newHeight, startX, startY, ratio) {
/* the parameters: - the image element - the new width - the new height - the x point we start taking pixels - the y point we start taking pixels - the ratio */
// Set up canvas for thumbnail
console.log(imgObj)
var img = new Image();
img.src = this.state.selectedImageURL;
var tnCanvas = this.refs.canvas;
tnCanvas.width = newWidth;
tnCanvas.height = newHeight;
tnCanvas.getContext('2d').drawImage(img, startX, startY, newWidth, newHeight);
return tnCanvas.toDataURL("image/png");
}
Now, I am not able to get the right preview or new image file object so that I could use that to show as preview in the modal there itself and than use the same to upload it. I am not even getting the right image ratio. Any help?
Here is the image:
react-image-crop using percent for scaling, make sure to calculate. Also make sure while create new object image on the fly to render virtual dom.
Here, try this:
import React, { Component } from 'react';
import ReactCrop, { makeAspectCrop } from 'react-image-crop';
import { FlatButton, Dialog } from 'material-ui';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import sample from './sample.png';
import 'react-image-crop/dist/ReactCrop.css';
class App extends Component {
state = {
showImageCropper: false,
selectedImageURL: sample,
crop: {
x: 0,
y: 0,
// aspect: 16 / 9,
},
selectedFile: null,
croppedImage: sample
};
showCropImageModal() {
const actions = [
<FlatButton
label="Cancel"
primary={true}
onClick={this.handleCancel}
/>,
<FlatButton
label="Crop"
primary={true}
keyboardFocused={true}
onClick={this.handleCropClose}
/>,
];
if (this.state.showImageCropper) {
return (
<div>
<Dialog
title="Crop the image"
actions={actions}
modal={true}
open={this.state.showImageCropper}
autoScrollBodyContent={true}
>
<ReactCrop
src={this.state.selectedImageURL}
crop={this.state.crop}
// onImageLoaded={this.onImageLoaded}
onComplete={this.onCropComplete}
onChange={this.onCropChange}
/>
</Dialog>
</div>
);
}
}
onCropComplete = (crop, pixels) => {
}
onCropChange = (crop) => {
this.setState({ crop });
}
// onImageLoaded = (image) => {
// this.setState({
// crop: makeAspectCrop({
// x: 0,
// y: 0,
// // aspect: 10 / 4,
// // width: 50,
// }, image.naturalWidth / image.naturalHeight),
// image,
// });
// }
handleCancel = () => {
this.setState({ showImageCropper: false });
}
handleCropClose = () => {
let { crop } = this.state;
// console.log("selectedFile", selectedFile);
// console.log("crop",crop);
const croppedImg = this.getCroppedImg(this.refImageCrop, crop);
this.setState({ showImageCropper: false, croppedImage: croppedImg })
}
getCroppedImg(srcImage,pixelCrop) {
/* the parameters: - the image element - the new width - the new height - the x point we start taking pixels - the y point we start taking pixels - the ratio */
// Set up canvas for thumbnail
// console.log(imgObj);
// let img = new Image();
// img.src = this.state.selectedImageURL;
// let tempCanvas = document.createElement('canvas');
// let tnCanvas = tempCanvas;
// tnCanvas.width = newWidth;
// tnCanvas.height = newHeight;
// tnCanvas.getContext('2d').drawImage(img, startX, startY, newWidth, newHeight);
// return tnCanvas;
let img = new Image();
img.src = this.state.selectedImageURL;
const targetX = srcImage.width * pixelCrop.x / 100;
const targetY = srcImage.height * pixelCrop.y / 100;
const targetWidth = srcImage.width * pixelCrop.width / 100;
const targetHeight = srcImage.height * pixelCrop.height / 100;
const canvas = document.createElement('canvas');
canvas.width = targetWidth;
canvas.height = targetHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(
img,
targetX,
targetY,
targetWidth,
targetHeight,
0,
0,
targetWidth,
targetHeight
);
return canvas.toDataURL('image/jpeg');
}
handleOpen = () => {
this.setState({ showImageCropper: true });
}
render() {
return (
<MuiThemeProvider>
<div className="App">
{ this.showCropImageModal() }
<img src={this.state.selectedImageURL} style={{display: "none"}} ref={(img) => {this.refImageCrop = img}} alt="" />
<img src={this.state.croppedImage} alt="" />
<FlatButton
label="Open popup"
primary={true}
onClick={this.handleOpen}
/>
</div>
</MuiThemeProvider>
);
}
}
export default App;
Solution :
First, use pixel coordinates:
- change : onChange={(crop) => { console.log(crop); this.setState({crop}); }}
- to onChange={(crop, pixelCrop) => { console.log(crop); this.setState({crop, pixelCrop}); }}.
Use this.state.pixelCrop instead of this.state.crop for getCroppedImg.
Then, update getCroppedImg to fetch the image asynchronously using a Promise and crop it.
getCroppedImg(imgObj, newWidth, newHeight, startX, startY, ratio) {
/* the parameters: - the image element - the new width - the new height - the x point we start taking pixels - the y point we start taking pixels - the ratio */
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = resolve;
img.onerror = reject;
img.src = this.state.selectedImageURL;
}).then(img => {
// Set up canvas for thumbnail
var tnCanvas = this.refs.canvas;
tnCanvas.width = newWidth;
tnCanvas.height = newHeight;
tnCanvas
.getContext('2d')
.drawImage(
img,
startX, startY, newWidth, newHeight,
0, 0, newWidth, newHeight
);
return tnCanvas.toDataURL("image/png");
});
}
Explanation :
You are missing parameters to drawImage. You are asking the canvas to draw the image at position (startX, startY) and scale it to (newWidth, newHeight).
To crop the image you need additional parameters :
drawImage(
image,
sx, sy, sw, sh,
dx, dy, dw, dh
);
Where :
Example :
const img = new Image()
const canvas = document.createElement('canvas')
img.src = 'https://cmeimg-a.akamaihd.net/640/clsd/getty/991dda07ecb947f1834bf1aa89153cf6'
const newWidth = 200
const newHeight = 200
const startX = 200
const startY = 100
img.onload = () => {
canvas.width = newWidth;
canvas.height = newHeight;
canvas.getContext('2d').drawImage(img, startX, startY, newWidth, newHeight, 0, 0, newWidth, newHeight);
}
document.body.appendChild(canvas)
document.body.appendChild(img)
Related
I am trying to resize the product images by keeping the aspect ratio and written the following function that will return the width & height. Currently it does not render the Card.Img tag element.
export default function SingleProduct({ prod }) {
const calculateAspectRatioFit = (image) => {
const maxWidth = 200; const maxHeight = 200;
let img = new Image();
img.src = image;
img.onload = function () {
var ratio = Math.min(maxWidth / this.width, maxHeight / this.height);
return (<Card.Img variant="top" src={image} style={{ width: `${this.width * ratio}`, height: `${this.height * ratio}` }} />)
};
}
return (
<div className="products">
<Card>
{calculateAspectRatioFit(prod.thumbnail)}
</Card>
</div>
)
}
I know it's something related to callback. How to resolve this?
I am trying to create a logo and try to add a logo in the middle of the qr code and i am able to generate the qr code but unable to get the qr code with logo on the middle i have tried this code but unable to get the result getting this error
Error: You need to specify a canvas element
and i am using this library https://github.com/soldair/node-qrcode
and here is the tried code
const QRCode = require("qrcode");
const getQRcodeImage = async () => {
try {
let canvas = await QRCode.toCanvas(`my sample text`);
//adding a log at center
const imgDim = { width: 30, height: 30 }; //logo dimention
var context = canvas.getContext("2d");
var imageObj = new Image();
imageObj.src = "./Capture.png";
imageObj.onload = function () {
context.drawImage(
imageObj,
canvas.width / 2 - imgDim.width / 2,
canvas.height / 2 - imgDim.height / 2,
imgDim.width,
imgDim.height
);
};
return canvas;
} catch (e) {
console.error(e);
return "";
}
};
getQRcodeImage();
const QRCode = require("qrcode");
const { createCanvas, loadImage } = require("canvas");
async function create(dataForQRcode, center_image, width, cwidth) {
const canvas = createCanvas(width, width);
QRCode.toCanvas(
canvas,
dataForQRcode,
{
errorCorrectionLevel: "H",
margin: 1,
color: {
dark: "#000000",
light: "#ffffff",
},
}
);
const ctx = canvas.getContext("2d");
const img = await loadImage(center_image);
const center = (width - cwidth) / 2;
ctx.drawImage(img, center, center, cwidth, cwidth);
return canvas.toDataURL("image/png");
}
async function main() {
const qrCode = await create(
"http://shauryamuttreja.com/qr/",
"data:image/png;base64,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",
150,
50
);
console.log(qrCode);
}
main();
I have a gallery-like component, and the important part is:
<Gallery>
<Header>
<img src={galleryIcon} alt='Galley icon' />
<h1>My Gallery</h1>
</Header>
<Images isImage={custom.length > 0}>
{custom.length > 0 &&
custom.map((c, i) => (
<img
id={`custom${i}`}
key={`custom${i}`}
src={c.img}
alt='brick'
onClick={() => (setEdit((prev) => !prev), setActual(c))}
/>
))}
<div className='add'>
<button onClick={() => setGallery((prev) => !prev)}>
<img src={add} alt='Add icon' />
</button>
<p>Click to add a brick from our collections!</p>
</div>
</Images>
</Gallery>
Each image have this style:
img {
box-sizing: border-box;
height: 28vh;
width: 28vh;
margin-right: 1.5rem;
cursor: pointer;
}
Also, once the user input a new image I resize to no break the scale, with this function:
export function resizeImage(
file: File | Blob,
maxWidth: number,
maxHeight: number,
scale = 1
): Promise<Blob> {
return new Promise<Blob>((fulfill, reject) => {
const image = new Image();
image.src = URL.createObjectURL(file);
image.onload = () => {
URL.revokeObjectURL(image.src);
const width = image.width;
const height = image.height;
if (width <= maxWidth && height <= maxHeight) {
fulfill(file);
}
let newWidth = 0;
let newHeight = 0;
if (scale !== 1) {
newWidth = (width * scale) / maxWidth;
newHeight = (height * scale) / maxHeight;
} else if (width > height) {
newHeight = height * (maxWidth / width);
newWidth = maxWidth;
} else {
newWidth = width * (maxHeight / height);
newHeight = maxHeight;
}
console.log(newWidth, newHeight);
const canvas = document.createElement('canvas');
canvas.width = newWidth;
canvas.height = newHeight;
const context = canvas.getContext('2d');
context?.drawImage(image, 0, 0, newWidth, newHeight);
canvas.toBlob((blob) => blob && fulfill(blob), file.type);
};
image.onerror = reject;
});
}
And finally, the Resize component:
const Resize: React.FC<Props> = ({ actual, setResize, setCustom, custom }) => {
let stats: Stats = {} as Stats;
const getStats = useCallback((): Stats => stats, []);
const updateStats = useCallback(
(newStats: Stats): Stats => (stats = newStats),
[]
);
const getResizedCustom = useCallback(
async (copy: Custom): Promise<Custom> => {
const actualWidth = window.innerWidth;
const actualHeight = window.innerHeight;
const maxWidth = actualWidth < 1152 ? 180 : 360;
const maxHeight = actualHeight < 800 ? 180 : 360;
const newBlob = await resizeImage(
copy.blob,
maxWidth,
maxHeight,
getStats().scale
);
return {
blob: newBlob,
img: URL.createObjectURL(newBlob),
id: copy.id,
type: 'custom',
price: copy.price,
amount: copy.amount,
};
},
[stats]
);
const updateActual = useCallback(async () => {
// remove actual
const newCustomArr = [...custom];
const customCopy = newCustomArr.splice(newCustomArr.indexOf(actual), 1)[0];
const newCustom = await getResizedCustom(customCopy);
console.log(customCopy);
console.log(newCustom);
setCustom([...newCustomArr, newCustom]);
}, [actual, custom, setCustom, getResizedCustom]);
return (
<Container>
<Header>
<h1>ADJUST YOUR BRICK</h1>
<img
src={close}
alt='close icon'
onClick={() => setResize((prev) => !prev)}
/>
</Header>
<Main>
<h2>Pinch and zoom</h2>
<TransformWrapper onZoomChange={updateStats}>
<TransformComponent>
<img src={actual.img} alt='brick' />
</TransformComponent>
</TransformWrapper>
<button
onClick={async () => (
await updateActual(), setResize((prev) => !prev)
)}
>
DONE
</button>
</Main>
</Container>
);
};
I'm also using the react-zoom-pan-pinch to have the ability to zoom in and out the image. My question is: How I could resize the image on the DOM, based on the scale provided by the onZoomChange function from TransformWrapper component? Is that possible? There's a better and less "hacky" way to resize a image on DOM based on a zoom scale?
I'll try to provide a minimum repo soon, but for now, here's the full repo: https://github.com/Mdsp9070/brickart/tree/dev
It is for sure possible, but you need to be passing more props to drawImage. Check out the docs. It has four more optional arguments that you're not using. If you just use the first four then the whole image is included. When using all eight props, the first four refer to what portion of the original image to use (the cropping) while the next four specify where to put that selection of the the image on your canvas.
You need to do some math and some event tracking to calculate the source rectangle based on the center of the zoom and the scale.
I am capturing images through react-webcam package but it is in a horizontal manner.
I am trying to view the image in a vertical manner. so I want the captured image to rotate at 90 degrees.
capture image is using a method that is this.webcam.getScreenshot().
so I am trying to set properties in this method so that the captured image is in a vertical manner, that it is rotated at 90 degrees.
I tried rotating the preview and capturing the image whereas, that doesn't work.
as images still are in a horizontal manner.
I want to rotate the image on capture
I tried // imageSrc.css('transform','rotate(90deg)');
but that does not work.
the image is captured as a base64 image
here webcam is the preview and capture button gets triggered by a button
capture = () => {
const imageSrc = this.webcam.getScreenshot();
this.setState({
picsArray: [...this.state.picsArray, imageSrc],
})
};
expected result: The picture is rotated 90 degrees on capturing the image using this method.
actual result: The image is not rotated at 90 degrees and doesn't know how to do it on capture.
Thank you for your time.
import React, { Component } from 'react';
import { render } from 'react-dom';
import Webcam from "react-webcam";
class App extends Component {
state = {
url: '',
};
setRef = webcam => {
this.webcam = webcam;
};
capture = () => {
const imageSrc = this.webcam.getScreenshot();
this.rotateImage(imageSrc, 180, (url) => {
this.setState({ url});
console.log(url, imageSrc);
});
};
rotateImage = (imageBase64, rotation, cb) => {
var img = new Image();
img.src = imageBase64;
img.onload = () => {
var canvas = document.createElement("canvas");
// var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, img.width / 2, img.height / 2);
ctx.rotate(rotation * (Math.PI / 180));
ctx.drawImage(img, -img.width / 2, -img.height / 2);
cb(canvas.toDataURL("image/jpeg"))
};
};
render() {
const videoConstraints = {
width: 1280,
height: 720,
facingMode: "user"
};
return (
<div>
<Webcam
audio={false}
height={350}
ref={this.setRef}
screenshotFormat="image/jpeg"
width={350}
videoConstraints={videoConstraints}
/>
<button onClick={this.capture}>Capture photo</button>
<img src={this.state.url} width="200" height="100" />
<canvas id="canvas" style={{display: 'none'}}></canvas>
</div>
);
}
}
render(<App />, document.getElementById('root'));
Update
Below code work for any dimension, only need to adjust in this.dim to reflect your need
import React, { Component } from 'react';
import { render } from 'react-dom';
import Webcam from "react-webcam";
class App extends Component {
state = {
url: '',
};
dim = {
height: 300, // adjust your original height
width: 200, // adjust your original width
};
setRef = webcam => {
this.webcam = webcam;
};
capture = () => {
const imageSrc = this.webcam.getScreenshot();
this.rotateImage(imageSrc, (url) => {
this.setState({ url});
console.log(url, imageSrc);
});
};
rotateImage = (imageBase64, cb) => {
var img = new Image();
img.src = imageBase64;
img.onload = () => {
var canvas = document.createElement("canvas");
const maxDim = Math.max(img.height, img.width);
canvas.width = img.height;
canvas.height = img.width;
var ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, maxDim / 2, maxDim / 2);
ctx.rotate(90 * (Math.PI / 180));
ctx.drawImage(img, -maxDim / 2, -maxDim / 2);
cb(canvas.toDataURL("image/jpeg"))
};
};
render() {
const videoConstraints = {
width: this.dim.width,
height: this.dim.height,
facingMode: "user"
};
return (
<div>
<Webcam
audio={false}
height={this.dim.height}
ref={this.setRef}
screenshotFormat="image/jpeg"
width={this.dim.width}
videoConstraints={videoConstraints}
/>
<button onClick={this.capture}>Capture photo</button>
<img src={this.state.url} width={this.dim.height} height={this.dim.width} />
<canvas id="canvas" style={{display: 'none'}}></canvas>
</div>
);
}
}
render(<App />, document.getElementById('root'));
You can rotate the image immediately in the preview using the style property:
90:
<Webcam style={{ transformOrigin: '0 0', transform: `translateX(${(480 + 640) / 2}px) rotate(90deg)` }} /> // height + width
180:
<Webcam style={{transform: 'rotate(180deg)'}} />,
270:
<Webcam style={{ transformOrigin: '0 0', transform: `translate(${(480 - 640) / 2}px, ${480}px) rotate(-90deg)` }} />
You should put your base64 image in an img tag in this way:
<img src="data:image/png;base64, YOUR_BASE_64" />
and then apply the CSS transform that you mentioned the img.
canvas.toDataURL("image/jpg") is working when I'm using an image that is local.
Like this this.originalImage = 'assets/img/defaultImage-900.jpg';
When I change it to a picture from the camera its not working.
Like this this.originalImage = "data:image/jpeg;base64," + imageData;
The error I get in Xcode is
{"code":18,"name":"SecurityError","message":"The operation is insecure.","line":40830,"column":61,"sourceURL":"http://localhost:8080/var/containers/Bundle/Application/4FDE886B-8A64-46AD-8E0C-FDA23C5218CD/Oslo%20Origo.app/www/build/main.js"}
This worked before I updated to iOS 10.3.1...
How do i fix?
What I want do to is to merge two images (900x900) on top of each other, to one image.
save() {
//get filter index
this.selectedFilter = this.slides.getActiveIndex();
// run the canvas thing
this.applyFilter(this.filters[this.selectedFilter]).subscribe(() => {
//done
});
}
Apply the filter on this.originalImage(base64 image from the camera)
applyFilter(filterUrl: string) {
return Observable.create((observer: any) => {
let baseImg = new Image();
let filterImg = new Image();
let canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
baseImg.src = this.originalImage;
baseImg.onload = () => {
canvas.width = baseImg.width;
canvas.height = baseImg.height;
ctx.drawImage(baseImg, 0, 0);
filterImg.src = filterUrl;
filterImg.onload = () => {
let hRatio = canvas.width / filterImg.width;
let vRatio = canvas.height / filterImg.height;
let ratio = Math.max(hRatio, vRatio);
let centerShift_x = (canvas.width - filterImg.width * ratio) / 2;
let centerShift_y = (canvas.height - filterImg.height * ratio) / 2;
ctx.drawImage(filterImg, 0, 0, filterImg.width, filterImg.height, centerShift_x, centerShift_y, filterImg.width * ratio, filterImg.height * ratio);
try{
this.resultImage = canvas.toDataURL("image/jpg");
} catch (error){
console.log(error)
}
observer.next();
}
};
});
}
Camera settings
// set options
let options = {
quality : 30,
destinationType : Camera.DestinationType.DATA_URL,
sourceType : 1,
allowEdit : true,
encodingType: Camera.EncodingType.JPEG,
cameraDirection: Camera.Direction.FRONT,
correctOrientation: true,
saveToPhotoAlbum: false,
targetHeight: 900,
targetWidth: 900
};
Camera.getPicture(options).then((imageData) => {
this.zone.run(() => {
this.originalImage = "data:image/jpeg;base64," + imageData;
});
}, (err) => {
if(this.originalImage == null || undefined){
this.navCtrl.setRoot(SocialTabsPage, { tabIndex: 0 });
}
});
For future people with a similar issue. In my case, originalImage came from the HTML.
I was getting the same error when its HTML was like this:
<img src="https://example.com/img.jpg" crossorigin="anonymous" ... />
I simply changed the order of the attributes, and the error went away:
<img crossorigin="anonymous" src="https://example.com/img.jpg" ... />
I found a solution.
The error I got was The operation is insecure.. And for whatever reason the app did not liked that I was using the image from the camera.
So I added baseImg.crossOrigin = 'anonymous'; to the image from the camera and a filterImg.crossOrigin = 'anonymous'; to the filter image.
But this only worked for iOS 10.3 and greater, så I added another if{} function.
So now it looks like this...
...
let baseImg = new Image();
let filterImg = new Image();
if (this.platform.is('ios')){
if (this.platform.version().num > 10.2){
baseImg.crossOrigin = 'anonymous';
filterImg.crossOrigin = 'anonymous';
}
}
let canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
...