How to focus crop image in React Native - javascript

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.

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.

Mouse wheel (zoom) on canvas img

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 ?

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");

Is there a way to scale a View in react-native?

I have a View in react-native with a few components. While everything shows up correctly on the iPhone 6 and 5, when viewing it on an iPhone 4s, the bottom of one of the components is slightly cut off.
I see there are ways to scale base64 icons. Is there any way to scale an entire container View to be uniformly smaller or larger?
Your question can be break down into two parts:
1, To scale width, height, paddings and margins. These can be easily achieve by using % and aspectRatio.
2, To scale Text, you might want to consider using Extended StyleSheet, which allows you to use rem.
You can simply following tutorial "7 Tips to Develop React Native UIs For All Screen Sizes" for how to use the above tips.
Additionally, check out Extended StyleSheet Scaling, which allows to use $scale variable to scale base on conditions.
Does something like this help you ?
YourStyleSheet.js
import {StyleSheet} from 'react-native';
var Dimensions = require('Dimensions');
var {width, height} = Dimensions.get('window');
export function create(styles: Object): {[name: string]: number} {
const platformStyles = {};
Object.keys(styles).forEach((name) => {
let {sm,md,lg, ...style} = {...styles[name]};
// iphone 4s and older
if(sm && width < 375){
style = {...style, ...sm};
}
// iphone 5,5s
if(md && width >= 375 && width <414){
style = {...style, ...md};
}
// iphone 6 and bigger
if(lg && width >= 414){
style = {...style, ...lg};
}
platformStyles[name] = style;
});
return StyleSheet.create(platformStyles);
}
Then in your style you can specify the size of the component on different screen sizes like this
import YourStyleSheet from './path/YourStyleShett'
const styles = YourStyleSheet.create({
component:{
sm:{fontSize: 20,},
md:{fontSize: 30,},
lg:{fontSize: 30,},
textAlign: 'center',
marginBottom: 10,
fontWeight:'bold',
color:colors.white,
}
});

How to auto-slide the window out from behind keyboard when TextInput has focus?

I've seen this hack for native apps to auto scroll the window, but wondering best way to do it in React Native... When a <TextInput> field gets focus and is positioned low in the view, the keyboard will cover up the text field.
You can see this issue in example UIExplorer's TextInputExample.js view.
Does anyone have a good solution?
2017 Answer
The KeyboardAvoidingView is probably the best way to go now. Check out the docs here. It is really simple compared to Keyboard module which gives Developer more control to perform animations. Spencer Carli demonstrated all the possible ways on his medium blog.
2015 Answer
The correct way to do this in react-native does not require external libraries, takes advantage of native code, and includes animations.
First define a function that will handle the onFocus event for each TextInput (or any other component you would like to scroll to):
// Scroll a component into view. Just pass the component ref string.
inputFocused (refName) {
setTimeout(() => {
let scrollResponder = this.refs.scrollView.getScrollResponder();
scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
React.findNodeHandle(this.refs[refName]),
110, //additionalOffset
true
);
}, 50);
}
Then, in your render function:
render () {
return (
<ScrollView ref='scrollView'>
<TextInput ref='username'
onFocus={this.inputFocused.bind(this, 'username')}
</ScrollView>
)
}
This uses the RCTDeviceEventEmitter for keyboard events and sizing, measures the position of the component using RCTUIManager.measureLayout, and calculates the exact scroll movement required in scrollResponderInputMeasureAndScrollToKeyboard.
You may want to play around with the additionalOffset parameter, to fit the needs of your specific UI design.
Facebook open sourced KeyboardAvoidingView in react native 0.29 to solve this problem. Documentation and usage example can be found here.
We combined some of the code form react-native-keyboard-spacer and the code from #Sherlock to create a KeyboardHandler component that can be wrapped around any View with TextInput elements. Works like a charm! :-)
/**
* Handle resizing enclosed View and scrolling to input
* Usage:
* <KeyboardHandler ref='kh' offset={50}>
* <View>
* ...
* <TextInput ref='username'
* onFocus={()=>this.refs.kh.inputFocused(this,'username')}/>
* ...
* </View>
* </KeyboardHandler>
*
* offset is optional and defaults to 34
* Any other specified props will be passed on to ScrollView
*/
'use strict';
var React=require('react-native');
var {
ScrollView,
View,
DeviceEventEmitter,
}=React;
var myprops={
offset:34,
}
var KeyboardHandler=React.createClass({
propTypes:{
offset: React.PropTypes.number,
},
getDefaultProps(){
return myprops;
},
getInitialState(){
DeviceEventEmitter.addListener('keyboardDidShow',(frames)=>{
if (!frames.endCoordinates) return;
this.setState({keyboardSpace: frames.endCoordinates.height});
});
DeviceEventEmitter.addListener('keyboardWillHide',(frames)=>{
this.setState({keyboardSpace:0});
});
this.scrollviewProps={
automaticallyAdjustContentInsets:true,
scrollEventThrottle:200,
};
// pass on any props we don't own to ScrollView
Object.keys(this.props).filter((n)=>{return n!='children'})
.forEach((e)=>{if(!myprops[e])this.scrollviewProps[e]=this.props[e]});
return {
keyboardSpace:0,
};
},
render(){
return (
<ScrollView ref='scrollView' {...this.scrollviewProps}>
{this.props.children}
<View style={{height:this.state.keyboardSpace}}></View>
</ScrollView>
);
},
inputFocused(_this,refName){
setTimeout(()=>{
let scrollResponder=this.refs.scrollView.getScrollResponder();
scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
React.findNodeHandle(_this.refs[refName]),
this.props.offset, //additionalOffset
true
);
}, 50);
}
}) // KeyboardHandler
module.exports=KeyboardHandler;
First you need to install react-native-keyboardevents.
In XCode, in the project navigator, right click Libraries ➜ Add
Files to [your project's name] Go to node_modules ➜
react-native-keyboardevents and add the .xcodeproj file
In XCode, in the
project navigator, select your project. Add the lib*.a from the keyboardevents
project to your project's Build Phases ➜ Link Binary With Libraries Click
.xcodeproj file you added before in the project navigator and go the Build
Settings tab. Make sure 'All' is toggled on (instead of 'Basic').
Look for Header Search Paths and make sure it contains both
$(SRCROOT)/../react-native/React and $(SRCROOT)/../../React - mark
both as recursive.
Run your project (Cmd+R)
Then back in javascript land:
You need to import the react-native-keyboardevents.
var KeyboardEvents = require('react-native-keyboardevents');
var KeyboardEventEmitter = KeyboardEvents.Emitter;
Then in your view, add some state for the keyboard space and update from listening to the keyboard events.
getInitialState: function() {
KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, (frames) => {
this.setState({keyboardSpace: frames.end.height});
});
KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, (frames) => {
this.setState({keyboardSpace: 0});
});
return {
keyboardSpace: 0,
};
},
Finally, add a spacer to your render function beneath everything so when it increases size it bumps your stuff up.
<View style={{height: this.state.keyboardSpace}}></View>
It is also possible to use the animation api, but for simplicity's sake we just adjust after the animation.
react-native-keyboard-aware-scroll-view solved the problem for me.
react-native-keyboard-aware-scroll-view on GitHub
Try this:
import React, {
DeviceEventEmitter,
Dimensions
} from 'react-native';
...
getInitialState: function() {
return {
visibleHeight: Dimensions.get('window').height
}
},
...
componentDidMount: function() {
let self = this;
DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
self.keyboardWillShow(e);
});
DeviceEventEmitter.addListener('keyboardWillHide', function(e: Event) {
self.keyboardWillHide(e);
});
}
...
keyboardWillShow (e) {
let newSize = Dimensions.get('window').height - e.endCoordinates.height;
this.setState({visibleHeight: newSize});
},
keyboardWillHide (e) {
this.setState({visibleHeight: Dimensions.get('window').height});
},
...
render: function() {
return (<View style={{height: this.state.visibleHeight}}>your view code here...</View>);
}
...
It worked for me. The view basically shrinks when the keyboard is displayed, and grows back again when its hidden.
Just wanted to mention, now there is a KeyboardAvoidingView in RN. Just import it and use it as any other module in RN.
Here is the link to the commit on RN:
https://github.com/facebook/react-native/commit/8b78846a9501ef9c5ce9d1e18ee104bfae76af2e
It is available from 0.29.0
They have also included an example on UIExplorer.
Maybe is to late, but the best solution is to use a native library, IQKeyboardManager
Just drag and drop IQKeyboardManager directory from demo project to your iOS project. That's it. Also you can setup some valus, as isToolbar enabled, or the space between text input and keyboard in the AppDelegate.m file. More details about customisation are in the GitHub page link that I've added.
I used TextInput.onFocus and ScrollView.scrollTo.
...
<ScrollView ref="scrollView">
...
<TextInput onFocus={this.scrolldown}>
...
scrolldown: function(){
this.refs.scrollView.scrollTo(width*2/3);
},
#Stephen
If you don't mind not having the height animate at exactly the same rate that the keyboard appears, you can just use LayoutAnimation, so that at least the height doesn't jump into place. e.g.
import LayoutAnimation from react-native and add the following methods to your component.
getInitialState: function() {
return {keyboardSpace: 0};
},
updateKeyboardSpace: function(frames) {
LayoutAnimation.configureNext(animations.layout.spring);
this.setState({keyboardSpace: frames.end.height});
},
resetKeyboardSpace: function() {
LayoutAnimation.configureNext(animations.layout.spring);
this.setState({keyboardSpace: 0});
},
componentDidMount: function() {
KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
},
componentWillUnmount: function() {
KeyboardEventEmitter.off(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
KeyboardEventEmitter.off(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
},
Some example animations are (I'm using the spring one above):
var animations = {
layout: {
spring: {
duration: 400,
create: {
duration: 300,
type: LayoutAnimation.Types.easeInEaseOut,
property: LayoutAnimation.Properties.opacity,
},
update: {
type: LayoutAnimation.Types.spring,
springDamping: 400,
},
},
easeInEaseOut: {
duration: 400,
create: {
type: LayoutAnimation.Types.easeInEaseOut,
property: LayoutAnimation.Properties.scaleXY,
},
update: {
type: LayoutAnimation.Types.easeInEaseOut,
},
},
},
};
UPDATE:
See #sherlock's answer below, as of react-native 0.11 the keyboard resizing can be solved using built in functionality.
You can combine a few of the methods into something a little simpler.
Attach a onFocus listener on your inputs
<TextInput ref="password" secureTextEntry={true}
onFocus={this.scrolldown.bind(this,'password')}
/>
Our scroll down method looks something like :
scrolldown(ref) {
const self = this;
this.refs[ref].measure((ox, oy, width, height, px, py) => {
self.refs.scrollView.scrollTo({y: oy - 200});
});
}
This tells our scroll view (remember to add a ref) to scroll to down to the position of our focused input - 200 (it's roughly the size of the keyboard)
componentWillMount() {
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardWillHide',
this.keyboardDidHide.bind(this)
)
}
componentWillUnmount() {
this.keyboardDidHideListener.remove()
}
keyboardDidHide(e) {
this.refs.scrollView.scrollTo({y: 0});
}
Here we reset our scroll view back to the top,
I'm using a simpler method, but it's not animated yet. I have a component state called "bumpedUp" which I default to 0, but set to 1 when the textInput gets focus, like this:
On my textInput:
onFocus={() => this.setState({bumpedUp: 1})}
onEndEditing={() => this.setState({bumpedUp: 0})}
I also have style that gives the wrapping container of everything on that screen a bottom margin and negative top margin, like this:
mythingscontainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
},
bumpedcontainer: {
marginBottom: 210,
marginTop: -210,
},
And then on the wrapping container, I set the styles like this:
<View style={[styles.mythingscontainer, this.state.bumpedUp && styles.bumpedcontainer]}>
So, when the "bumpedUp" state gets set to 1, the bumpedcontainer style kicks in and moves the content up.
Kinda hacky and the margins are hardcoded, but it works :)
I use brysgo answer to raise the bottom of my scrollview. Then I use the onScroll to update the current position of the scrollview. I then found this React Native: Getting the position of an element to get the position of the textinput. I then do some simple math to figure out if the input is in the current view. Then I use scrollTo to move the minimum amount plus a margin. It's pretty smooth. Heres the code for the scrolling portion:
focusOn: function(target) {
return () => {
var handle = React.findNodeHandle(this.refs[target]);
UIManager.measureLayoutRelativeToParent( handle,
(e) => {console.error(e)},
(x,y,w,h) => {
var offs = this.scrollPosition + 250;
var subHeaderHeight = (Sizes.width > 320) ? Sizes.height * 0.067 : Sizes.height * 0.077;
var headerHeight = Sizes.height / 9;
var largeSpace = (Sizes.height - (subHeaderHeight + headerHeight));
var shortSpace = largeSpace - this.keyboardOffset;
if(y+h >= this.scrollPosition + shortSpace) {
this.refs.sv.scrollTo(y+h - shortSpace + 20);
}
if(y < this.scrollPosition) this.refs.sv.scrollTo(this.scrollPosition - (this.scrollPosition-y) - 20 );
}
);
};
},
I also meet this question. Finally, I resolve it by defining the height of each scene, such as:
<Navigator
...
sceneStyle={{height: **}}
/>
And, I also use a third-party module https://github.com/jaysoo/react-native-extra-dimensions-android to get the real height.

Categories

Resources