Mouse wheel (zoom) on canvas img - javascript

React beginner here, i have a canvas where is img, i wanted to implement zooming when user takes mouse on the picture then user could zoom in and out using mouse wheel,
i implemented it correctly because now zoom in and zoom out is possible but got one problem, img becomes smaller (but zooming works), if i remove 'TransformComponent' then img becomes bigger(as it should be this size) but user cannot zoom anymore.
English is not my mother language so could be mistakes
.
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
renderPreview() {
const camera = this.props.currentCamera;
// render size (not display size) of the canvas
const canvasRenderWidth = camera
? camera.width
: this.state.config.previewStyle.width;
const canvasRenderHeight = camera
? camera.height
: this.state.config.previewStyle.height;
// display size of the canvas
let canvasStyle = {
width: canvasRenderWidth >= canvasRenderHeight ? "100%" : "auto",
height: canvasRenderHeight > canvasRenderWidth ? "100%" : "auto",
};
return (
<div style={this.state.config.previewStyle}>
<TransformWrapper
>
<TransformComponent>
<canvas
ref="CameraPreviewCanvas"
width={canvasRenderWidth}
height={canvasRenderHeight}
onClick={this.onClickCanvas}
onMouseMove={this.onMouseOverCanvas}
style={canvasStyle}
></canvas>
</TransformComponent>
</TransformWrapper>
<img
ref="CameraPreviewImage"
src={this.state.snapshot.snapshotUrl}
style={{ display: "none" }}
></img>
</div>
);
}
img:
i want img width to be 100% as it is without 'TransformComponent'
any idea how to fix this ?

Related

Fit the map view of an Image Overlay to the size of the container Leaflet

I am using React Leaflet to create a custom map.
The card (Image Overlay) is currently being rendered, but it does not fill the container in which the card is placed.
The size of the container is set strictly and I would like the map to fill the container as much as possible. All attempts to resize with CSS failed.
Live example: https://codesandbox.io/s/dark-worker-8qbzfr
My code:
import "./styles.css";
import { MapContainer, ImageOverlay } from "react-leaflet";
import "leaflet/dist/leaflet.css";
const M = ({ width, height, zoom, center }) => {
const wh = [width, 500];
const origin = [0, 0];
const bounds = [origin, wh];
return (
<div style={{ width: "1100px", height: "600px" }}>
<MapContainer
style={{ height: "100%", minHeight: "100%" }}
bounds={zoom ? undefined : bounds}
boundsOptions={{
padding: [0, 0]
}}
maxBounds={bounds}
zoom={center ? zoom : undefined}
center={zoom ? center : undefined}
>
<ImageOverlay
url="../ptichnikDraw.png"
bounds={bounds}
className="map_main"
/>
</MapContainer>
</div>
);
};
const App = () => {
return (
<div
style={{
display: "flex",
justifyContent: "center"
}}
>
<M width={1500} height={500} center={[0, 0]} />
</div>
);
};
export default App;
You want your "card" (the <ImageOverlay>) to fill the initial map container/viewport.
You will not be able to do so using (only) CSS, but with Leaflet map options and methods, which are exposed by React Leaflet wrapper:
Use the Leaflet map whenReady option (exposed as a prop of <MapContainer>) to attach a callback that is executed once the map is initialized
Runs the given function fn when the map gets initialized
While not explicitly documented, that callback receives a load event argument, which target property is the newly instantiated Leaflet map
Fired when the map is initialized
Call the fitBounds() map method on that instance, with the same bounds as your Image Overlay, to have the map view be adjusted accordingly
Sets a map view that contains the given geographical bounds with the maximum zoom level possible.
Important: make sure to set the map zoomSnap option to value 0, otherwise the fitBounds will be modified so that the final zoom is a multiple of zoomSnap (default to 1, i.e. integer zoom levels)
Forces the map's zoom level to always be a multiple of this, particularly right after a fitBounds() or a pinch-zoom. By default, the zoom level snaps to the nearest integer; lower values (e.g. 0.5 or 0.1) allow for greater granularity. A value of 0 means the zoom level will not be snapped after fitBounds or a pinch-zoom.
const M = ({ width, height, zoom, center }) => {
const wh = [width, 500];
const origin = [0, 0];
const bounds = [origin, wh];
return (
<div style={{ width: "1100px", height: "600px" }}>
<MapContainer
style={{ height: "100%", minHeight: "100%" }}
bounds={zoom ? undefined : bounds}
boundsOptions={{
padding: [0, 0]
}}
maxBounds={bounds}
zoom={center ? zoom : undefined}
center={zoom ? center : undefined}
zoomSnap={0} // Important to disable snap after fitBounds
whenReady={(e) => e.target.fitBounds(bounds)} // Have the map adjust its view to the same bounds as the Image Overlay
>
<ImageOverlay
url="../ptichnikDraw.png"
bounds={bounds}
className="map_main"
/>
</MapContainer>
</div>
);
};
Updated CodeSandbox: https://codesandbox.io/s/pedantic-tree-lz1o9q
Using the default Leaflet JavaScript library you can use map.invalidateSize(). Don't know how that would work in React but maybe you can figure that one out using the documentation.

How to access canvas for a screenshot when using react-stl-obj-viewer component

I'm using a component react-stl-obj-viewer to render a 3d stl image. I can render the 3d stl image correctly. Once the image is rendered, I'm trying to move it around and have a button to take a screenshot of it.
<div>
<STLViewer
onSceneRendered={(element) => {
console.log(element);
}}
sceneClassName="test-scene"
file={this.state.selectedFile}
modelColor="#073FE9"
/>
</div>
<div style={{ display: "inline-block" }}>
<Button
id="thumbnail"
style={{ zIndex: "-1", margin: "20px 0px 20px 0px" }}
onClick={(e) => {
this.save3dRender(e, webGlContextExists);
}}
>
Save Frame as Thumbnail
</Button>
{!this.state.showThumbnail ? (
<Container>
<h2>Did you get this image</h2>
<Image src={this.state.thumbnailFile} />
</Container>
) : null}
</div>
In order to take a screenshot of the image, I was trying to use .toDataURL("image/png"). My button does this when pressed.
save3dRender = (e, geeL) => {
e.preventDefault();
var testThumbnail = geeL.canvas.toDataURL("image/png");
this.state.thumbnailFile = testThumbnail;
this.state.thumbnailFileRender = true;
this.state.showThumbnail = false;
};
I am somewhat new at this but thought I was on the right track. Since the STLViewer component is creating the canvas, I was trying to access it this way with .canvas. Not sure if that is correct but I do get a data:image/png;base64 address. However it is a blank image.
Am I accessing the correct canvas? Is there something I need to do to the image after getting it as data:/image/png;base64.
Fixed the issue. It was an array that I needed to reference.
var testThumbnail = geeL[0].toDataURL("image/png");
instead of
var testThumbnail = geeL.canvas.toDataURL("image/png");

react drag,drop, and connect arrow(or anything else) with animation between elements

after few days of trying with no success, I came here (again..), to all of you experts.
first of all: live demo! because we all love live demos.
Codesandbox
https://codesandbox.io/s/drag-with-not-animation-b3eh7?file=/src/App.js
I'm trying to make interactive draggable and dropable arrows between containers(means - there is a connector to a box, you can drag your mouse from one of the containers to another box and an arrow between them will be created).
implementation 1: I can get an animation of a draggable arrow while dragging - but the onDrop event does not fire.
implementation 2: in the second implementation I can make the drop effect happen but not the animation.
NEVER THEM BOTH!
HELP! ;<
more detailed explanations inside the code.
what I've already tried:
react-dnd - did not work also because it's based on the same DOM event system the native browser drag & drop API based on(or maybe(and probably) I did it wrong?).
Here you go :
To draw a Xarrow you need a starting point and ending point
Start point : will always be dragging from
End Point : Where you are dropping
Here We have 2 refs ,
ref0 : Starting drag point ( which will be the box )
ref1 : Draggable point ( Will be the draggable point )
Here is the code that needs to be changed, please do read the comments also, that will make the flow clear.
const ConnectPointsWrapper = ({ boxId, handler, ref0 }) => {
const ref1 = useRef();
const [position, setPosition] = useState({});
const [beingDragged, setBeingDragged] = useState(false);
return (
<React.Fragment>
<div
className="connectPoint"
ref={ref1} // <---- referencing the point
style={{
...connectPointStyle,
...connectPointOffset[handler],
...position // <----- Updating the position as we drags
}}
draggable
onDragStart={e => {
setBeingDragged(true);
e.dataTransfer.setData("arrow", boxId);
}}
onDrag={e => {
setPosition({ // <---- Setting up the position to draw line as we drags
position: "fixed",
left: e.clientX,
top: e.clientY,
transform: "none",
opacity: 0
});
}}
onDragEnd={e => {
setPosition({});
setBeingDragged(false);
}}
/>
{beingDragged ? <Xarrow start={ref0} end={ref1} /> : null} // <---- this will draw the arrow b/w ref0 and ref1
</React.Fragment>
);
};
const Box = ({ text, handler, addArrow, boxId }) => {
const ref0 = useRef();
return (
<div
id={boxId}
style={boxStyle}
ref={ref0} // <---- referencing the box it self
onDragOver={e => e.preventDefault()}
onDrop={e => {
if (e.dataTransfer.getData("arrow") === boxId) {
console.log(e.dataTransfer.getData("arrow"), boxId);
} else {
const refs = { start: e.dataTransfer.getData("arrow"), end: boxId };
addArrow(refs);
}
}}
>
{text}
<ConnectPointsWrapper {...{ boxId, handler, ref0 }} /> // <---- Passing the `ref0` to `ConnectPointsWrapper` to draw line from point
</div>
);
};
WORKING DEMO :
NOTE :
I was trying to set style just via ref1 and not with setPosition,
you can check the below code snippet for that,
<div
className="connectPoint"
style={{
...connectPointStyle,
...connectPointOffset[handler]
}}
draggable
onDragStart={e => {
setBeingDragged(true);
e.dataTransfer.setData("arrow", boxId);
}}
onDrag={e => {
setPosition({}); // <---- just to force re-rendering, to draw arrow with updated value
ref1.current.style.position = "fixed";
ref1.current.style.left = e.clientX + "px";
ref1.current.style.top = e.clientY + "px";
ref1.current.style.transform = "none";
ref1.current.style.opacity = 0;
}}
ref={ref1}
onDragEnd={e => {
ref1.current.style.position = "absolute";
ref1.current.style.left = connectPointOffset[handler].left;
ref1.current.style.top = connectPointOffset[handler].top;
ref1.current.style.transform = connectPointOffset[handler].transform;
ref1.current.style.opacity = 0.5;
setBeingDragged(false);
}}
/>
WORKING DEMO : ( this is just another way )
EDIT :
WORKING DEMO : ( With draggable box also )

How can I prevent both vertical and horizontal stretching of contained text while resizing text-based objects in Fabric.js?

I want to be able to scale text-based objects (for example with classes and subclasses of IText, Textbox) without stretching the text inside of the object. The interaction of scaling and moving is on user-side (UI), not programmatically (I am working on a drawing editor).
The behaviour I am looking for is similar to the one in the sticky notes apps: You can scale the paper but this action does not scale your text too.
I have already checked this, but this is only for horizontal prevention: Fabric.js How to resize IText horizontally without stretching the text
This is neither what I want/mean: Fabric.js : How to set a custom size to Text or IText?
I know that a scaling factor different than 1 implies the stretching of the inner text, and that by changing the width and height I can resize the object while keeping the text unscaled, so I tried updating these during scaling using events listeners.
I tried many combinations of the IText,Textbox with some scaling events, like this one:
fbtext.on('scaling', function() {
this.set({
width : this.width * this.scaleX,
height: this.height * this.scaleY,
scaleX: 1,
scaleY: 1,
});
});
I also tried this with Textbox:
fbtext.on('scaling', function() {
this.set({
height: this.height * this.scaleY,
});
});
But nothing worked so far. I am out of ideas.
var canvas = new fabric.Canvas('mycanvas');
let textbox = new fabric.Textbox("Sample text", {
left: 100,
top: 100
});
let lastHeight;
const updateTextSize = () => {
const controlPoint = textbox.__corner;
//mr and ml are the only controlPoints that dont modify textbox height
if( controlPoint && controlPoint != "mr" && controlPoint != "ml"){
lastHeight = textbox.height * textbox.scaleY;
}
textbox.set({
height: lastHeight || textbox.height,
scaleY:1
});
canvas.renderAll();
};
textbox.on('scaling', updateTextSize );
textbox.on('editing:entered', updateTextSize);
canvas.on('text:changed', updateTextSize);
canvas.add(textbox);
canvas {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.3.2/fabric.min.js"></script>
<canvas id="mycanvas" width="400" height="400"></canvas>
Here is a code sample that will let you modify the "sticky note size" without streching out the text
Thanks #Ivan, your solution is working for me, with a small glitch.
When increasing the size of the text box using corners the text is getting stretched and compressed. I haven't found the solution of this yet and have currently implemented a workaround to hide all the corner scaling points.
box.setControlsVisibility({
bl: false,
br: false,
tl: false,
tr: false
});
http://jsfiddle.net/bkgvewh6/
Just to add I am posting this as an answer and not as a comment to your answer as I don't have stack overflow reputation to do so.

How to focus crop image in React Native

According to the docs, the react native's Image component support the following resizeModes:
'cover', 'contain', 'stretch', 'repeat', 'center'
If the image is larger then the Image component, the center mode fits the image in the Image in the component by uniformly scaling the image such that the center point of the image is in center of the Image component.
I would like to know if there is a hack or a solution in which we can define a custom point (say 0,300) as a focus point such that it is the center of the Image view.
What I want to achieve is somewhat like focus crop in fresco, but should also work for IOS.
I think you need to handle like this
const CroppedImage = React.createClass({
render() {
return (
<View
style={[
{
overflow: 'hidden',
height: this.props.cropHeight,
width: this.props.cropWidth,
backgroundColor: 'transparent'
},
this.props.style
]}
>
<Image
style={{
position: 'absolute',
top: this.props.cropTop * -1,
left: this.props.cropLeft * -1,
width: this.props.width,
height: this.props.height
}}
source={this.props.source}
resizeMode={this.props.resizeMode}
>
{this.props.children}
</Image>
</View>
);
}
});
Look at this example
Link
React-Native has a built-in API to handle image cropping, ImageEditor. It makes image cropping fairly simple. See the function below for an example.
The inputted image takes the form of a URI. The image is cropped, and a URI pointing to a cropped image is provided (the image is housed via React-Native's ImageStore API). Subsequently use this URI to display the cropped image as you wish.
// Make sure you import ImageEditor from react-native!
async cropImage(){
// Construct a crop data object.
cropData = {
offset:{x:0,y:0},
size:{width:20, height:20},
// displaySize:{width:20, height:20}, THESE 2 ARE OPTIONAL.
// resizeMode:'contain',
}
// Crop the image.
try{
await ImageEditor.cropImage(uri,
cropData, (successURI) => {Something awesome with successURI!},
(error) =>{console.log('cropImage,',error)}
)
}
catch(error){
console.log('Error caught in this.cropImage:', error)
}
}
// End of function.

Categories

Resources