How to zoom in/out in react-native-map? - react-native

I am using react-native to build a map application. The api I am using is from this link: https://github.com/lelandrichardson/react-native-maps.
Below is the code I bring the map on my app. I wonder how I can give a zoom value on that map. And how I can change the zoom value when the user clicks a button on the map.
What is the zoom API I should use to achieve this?
import React, { Component, StyleSheet, View, TextInput } from "react-native";
import MapView from "react-native-maps";
class MapPage extends Component {
constructor(props) {
super(props);
this.state = {
region: {
latitude: 4.21048,
longitude: 101.97577,
latitudeDelta: 10,
longitudeDelta: 5,
},
};
}
render() {
return (
<View style={styles.container}>
<TextInput style={styles.inputText}>Map</TextInput>
<MapView
style={styles.map}
mapType={"standard"}
region={this.state.region}
zoomEnabled={true}
scrollEnabled={true}
showsScale={true}
/>
</View>
);
}
}
module.exports = MapPage;
const styles = StyleSheet.create({
map: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
},
container: {
flexDirection: "row",
justifyContent: "space-between",
padding: 30,
flex: 1,
alignItems: "center",
},
inputText: {
height: 36,
padding: 4,
marginRight: 5,
flex: 4,
fontSize: 18,
borderWidth: 1,
borderColor: "#48BBEC",
borderRadius: 8,
color: "#48BBEC",
},
});

You should use the animateToRegion method (see here)
It takes a region object which has latitudeDelta and longitudeDelta. Use these to set the zoom level.
Updated:
in a Region object the latitude and longitude specify the center location and latitudeDelta and longitudeDelta specify the span of the viewable map area.
This image from this blog post illustrates it well (LatΔ and LngΔ).

New React Native Maps API gives you option to call animateCamera method with zoom parameter.
const MapComponent= (props: any) => {
const map: LegacyRef<MapView> = useRef(null);
const onZoomInPress = () => {
map?.current?.getCamera().then((cam: Camera) => {
cam.zoom += 1;
map?.current?.animateCamera(cam);
});
};
return (
<View>
<MapView
ref={map}
provider={PROVIDER_GOOGLE}
region={region}>
</MapView>
<ButtonComponent
style={{position: 'absolute', bottom: 400, left: 0}}
onPress={onZoomInPress}>
Zoom In
</MainButtonBlue>
</View>
);
}

I was able to make this work using Dimensions.get('window');
const window = Dimensions.get('window');
const { width, height } = window
LONGITUDE_DELTA = LATITUD_DELTA + (width / height)
and by default set LATITUD_DELTA = 0.0922.
Then just update this values with the prop onRegionChangeComplete in the <MapView>

This is what I did and it worked out for me very well:
function getRegion(origin, destination, zoom) {
const oLat = Math.abs(origin.latitude);
const oLng = Math.abs(origin.longitude);
const dLat = Math.abs(destination.latitude);
const dLng = Math.abs(destination.longitude);
return {
latitude: (origin.latitude + destination.latitude) / 2,
longitude: (origin.longitude + destination.longitude) / 2,
latitudeDelta: Math.abs(oLat - dLat) + zoom,
longitudeDelta: Math.abs(oLng - dLng) + zoom,
};
}

I created the following based on the mercator math in https://github.com/tuupola/php_google_maps
The key function is mercatorDegreeDeltas(latitude, longitude, width, height, zoom) which returns { latitudeDelta, longitudeDelta } for the specified latitude/longitude center point, map dimensions, and zoom level (1-20).
import React from 'react';
import { useWindowDimensions } from 'react-native';
import MapView from 'react-native-maps';
import { useBottomTabBarHeight } from '#react-navigation/bottom-tabs';
import { useHeaderHeight } from '#react-navigation/elements';
const MERCATOR_OFFSET = Math.pow(2, 28);
const MERCATOR_RADIUS = MERCATOR_OFFSET / Math.PI;
function mercatorLatitudeToY(latitude) {
return Math.round(
MERCATOR_OFFSET -
(
(
MERCATOR_RADIUS *
Math.log(
(1 + Math.sin(latitude * (Math.PI / 180))) /
(1 - Math.sin(latitude * (Math.PI / 180)))
)
) / 2
)
);
}
function mercatorLongitudeToX(longitude) {
return Math.round(
MERCATOR_OFFSET +
(
(
(MERCATOR_RADIUS * longitude) * Math.PI
) / 180
)
);
}
function mercatorXToLongitude(x) {
return (
(
(x - MERCATOR_OFFSET) / MERCATOR_RADIUS
) * 180
) / Math.PI;
}
function mercatorYToLatitude(y) {
return (
(
(
Math.PI / 2
) -
(2 * Math.atan(
Math.exp(
(
y - MERCATOR_OFFSET
) / MERCATOR_RADIUS
)
)
)
) * 180
) / Math.PI;
}
function mercatorAdjustLatitudeByOffsetAndZoom(latitude, offset, zoom) {
return mercatorYToLatitude(mercatorLatitudeToY(latitude) + (offset << (21 - zoom)));
}
function mercatorAdjustLongitudeByOffsetAndZoom(longitude, offset, zoom) {
return mercatorXToLongitude(mercatorLongitudeToX(longitude) + (offset << (21 - zoom)));
}
function mercatorDegreeDeltas(latitude, longitude, width, height, zoom) {
if (!zoom) {
zoom = 20;
}
const deltaX = width / 2;
const deltaY = height / 2;
const northLatitude = mercatorAdjustLatitudeByOffsetAndZoom(latitude, deltaY * -1, zoom);
const westLongitude = mercatorAdjustLongitudeByOffsetAndZoom(longitude, deltaX * -1, zoom);
const southLatitude = mercatorAdjustLatitudeByOffsetAndZoom(latitude, deltaY, zoom);
const eastLongitude = mercatorAdjustLongitudeByOffsetAndZoom(longitude, deltaY, zoom);
const latitudeDelta = Math.abs(northLatitude - southLatitude);
const longitudeDelta = Math.abs(eastLongitude - westLongitude);
return { latitudeDelta, longitudeDelta };
}
// Somewhat arbitrarily, Riverside Park, Independence, KS 67301
const CENTER_UNITED_STATES = {
latitude: 37.24435373025407,
longitude: -95.70234410503208,
};
export default function MapViewWrapper() {
const { width, height } = useWindowDimensions();
const tabBarHeight = useBottomTabBarHeight();
const headerHeight = useHeaderHeight();
const initialRegion = React.useRef(null);
const availableHeight = height - tabBarHeight - headerHeight;
// Only calculate initial region once
if (!initialRegion.current) {
const { latitudeDelta, longitudeDelta } = mercatorDegreeDeltas(
CENTER_UNITED_STATES.latitude,
CENTER_UNITED_STATES.longitude,
width,
availableHeight,
4,
);
initialRegion.current = {
latitude: CENTER_UNITED_STATES.latitude,
longitude: CENTER_UNITED_STATES.longitude,
latitudeDelta: latitudeDelta,
longitudeDelta: longitudeDelta,
};
}
return (
<MapView
initialRegion={initialRegion.current}
style={{ width: width, height: availableHeight }}
/>
);
}
There is at least one issue: if you change the zoom from 4 to 3, it isn't centered properly, but larger zoom values work. I don't need the lower zoom values right now, so I haven't investigated the math any further (maybe some sort of overflow?).

const handleZoomIn = () => {
map.current?.getCamera().then((cam: Camera) => {
if (Platform.OS === 'android') {
cam.zoom += 1;
} else {
cam.altitude /= 2;
}
map.current?.animateCamera(cam);
});
};
const handleZoomOut = () => {
map.current?.getCamera().then((cam: Camera) => {
if (Platform.OS === 'android') {
cam.zoom -= 1;
} else {
cam.altitude *= 2;
}
map.current?.animateCamera(cam);
});
};

//Example of Pinch to Zoom Image in React Native
//https://aboutreact.com/react-native-pinch-to-zoom-image/
//import React in our code
import React from 'react';
//import all the components we are going to use
import { SafeAreaView, StyleSheet, View } from 'react-native';
//import ImageViewer which will help us to zoom Image
import ImageViewer from 'react-native-image-zoom-viewer';
const App = () => {
const images = [
{
url:
'https://raw.githubusercontent.com/AboutReact/sampleresource/master/sample_img.png',
},
{
url:
'https://raw.githubusercontent.com/AboutReact/sampleresource/master/old_logo.png',
},
];
return (
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
<ImageViewer imageUrls={images} renderIndicator={() => null} />
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
flex: 1,
},
});
export default App;

Related

React Native Looping Animation Performance

I have a react native app and I want to have a fireworks animation playing on loop more or less indefinitely. I have implemented an animation I am happy with using the built-in react native animated library, however the performance monitor shows the UI thread down at 30 fps (and I can feel my iPhone heating up after about 10 seconds too). I can bring it back up to 60 fps by reducing the number of firework bursts or stars, but I would like to keep the amount I have if possible because it looks nicer. Are there any optimizations you see I can do to get it back to 60 fps without compromising the look of the fireworks?
import React, { useRef, useEffect, useState, useCallback } from 'react';
import { View, Animated, Easing, TouchableOpacity, Text } from 'react-native';
export default function App() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "black" }}>
<Fireworks />
</View>
);
}
const random = (min, max) => Math.floor(Math.random() * (max - min)) + min;
const NUM_BURSTS = 10;
const STAR_SIZE_MIN = 2;
const STAR_SIZE_MAX = 6;
const NUM_STARS = 200;
const NUM_OUTER_STARS = NUM_STARS / 2;
const DURATION_MIN = 1000;
const DURATION_MAX = 3000;
const BURST_SPEED = 7;
const RADIUS_MIN = 30;
const RADIUS_MAX = 100;
const Fireworks = () => {
const [bursts, setBursts] = useState([]);
useEffect(() => {
//Create all the firework bursts one time at component mount.
const newBursts = [];
for (let i = 0; i < NUM_BURSTS; i++) {
const starSize = random(STAR_SIZE_MIN, STAR_SIZE_MAX);
newBursts.push({
starSize
});
}
setBursts(newBursts);
}, []);
const renderBursts = bursts?.map((burst, index) =>
<FireworkBurst starSize={burst.starSize} key={"" + index} />
);
return (
<View style={{ position: "absolute", top: 0, left: 0, bottom: 0, right: 0 }}>
{renderBursts}
</View>
)
}
const FireworkBurst = ({ starSize }) => {
const viewRef = useRef();
const animValue = useRef(new Animated.Value(0)).current;
const [stars, setStars] = useState([]);
const loop = useRef(true);
useEffect(() => {
const radius = random(RADIUS_MIN, RADIUS_MAX);
const newStars = [];
for (let i = 0; i < NUM_STARS; i++) {
const star = generateStar(i, radius);
newStars.push(star);
}
setStars(newStars);
loop.current = true;
animate();
return () => {
loop.current = false;
}
}, []);
const generateStar = (iteration, radius) => {
const star = {
starSize
};
const end = {}
if (iteration < NUM_OUTER_STARS) {
const value = iteration / NUM_OUTER_STARS;
end.x = Math.sin(value * Math.PI * 2) * radius;
end.x += random(-10, 10);
end.y = -Math.cos(value * Math.PI * 2) * radius;
end.y += random(-10, 10);
}
else {
end.x = random(-(radius - 10), radius - 10);
end.y = random(-(radius - 10), radius - 10);
}
star.end = end;
return star;
}
const animate = useCallback(() => {
const x = random(0, 100);
const y = random(0, 100);
//set native props to avoid re-rendering
viewRef?.current?.setNativeProps({ top: `${y}%`, left: `${x}%` });
animValue.setValue(0);
Animated.timing(
animValue,
{
toValue: 1,
duration: random(DURATION_MIN, DURATION_MAX),
easing: Easing.out(Easing.poly(BURST_SPEED)),
isInteraction: false,
useNativeDriver: true
}
).start(() => {
loop.current && animate();
});
}, [animValue]);
const renderStars = stars.map((star, index) =>
<FireworkStar starSize={star.starSize} end={star.end} animValue={animValue} key={"" + index} />
);
return (
<Animated.View ref={viewRef} style={{ position: "absolute" }}>
{renderStars}
</Animated.View>
)
}
const FireworkStar = ({ starSize, end, animValue }) => {
const translateX = animValue.interpolate({
inputRange: [0, 1],
outputRange: [0, end.x]
})
const translateY = animValue.interpolate({
inputRange: [0, 1],
outputRange: [0, end.y]
})
const opacity = animValue.interpolate({
inputRange: [0, 0.9, 1],
outputRange: [1, 1, 0]
});
const hue = random(0, 255);
const color = `hsl(${hue},100%,70%)`;
return (
<Animated.View
style={{
position: "absolute", top: 0, left: 0,
height: starSize, width: starSize, borderRadius: starSize,
justifyContent: "center", alignItems: "center",
backgroundColor: color,
transform: [
{ translateX },
{ translateY }
]
}}
opacity={opacity}
>
</Animated.View>
)
}

React Native Oval Scroll

How can I do a similar oval scroll?
What can I use for this?
Based on the assumption that you want something like this, I wrote a simple example
If someday the link turns out to be broken, below I attach the code additionally
import React, { useCallback, useState, useRef } from "react";
import {
FlatList,
Text,
View,
StyleSheet,
Dimensions,
Animated
} from "react-native";
const { height } = Dimensions.get("window");
const screenMiddle = height / 2;
const itemScaleOffset = height / 3;
const DATA = new Array(20).fill(0).map((...args) => ({
id: args[1],
title: args[1]
}));
// args[1] is an index, just I hate warnings
const Item = ({ title, offsetY }) => {
const [scrollEdges, setScrollEdges] = useState({
top: 0,
middle: 0,
bottom: 0
});
const onLayout = useCallback(
({
nativeEvent: {
layout: { top, height }
}
}) =>
setScrollEdges((edges) => ({
...edges,
top: top - itemScaleOffset - screenMiddle,
middle: top + height / 2 - screenMiddle,
bottom: top + height + itemScaleOffset - screenMiddle
})),
[]
);
const scale = offsetY.interpolate({
inputRange: [scrollEdges.top, scrollEdges.middle, scrollEdges.bottom],
outputRange: [0.66, 1, 0.66],
extrapolate: "clamp"
});
return (
<Animated.View
onLayout={onLayout}
style={[
{
transform: [
{
scale
}
]
},
styles.item
]}
>
<Text style={styles.title}>{title}</Text>
</Animated.View>
);
};
const keyExtractor = ({ id }) => id.toString();
const App = () => {
const offsetY = useRef(new Animated.Value(0)).current;
const renderItem = useCallback(
({ item: { title } }) => <Item title={title} offsetY={offsetY} />,
[offsetY]
);
return (
<View style={styles.app}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={keyExtractor}
onScroll={Animated.event(
[
{
nativeEvent: {
contentOffset: {
y: offsetY
}
}
}
],
{
useNativeDriver: false
}
)}
/>
</View>
);
};
const styles = StyleSheet.create({
app: {
flex: 1
},
item: {
backgroundColor: "#f9c2ff",
padding: 20,
marginVertical: 8,
marginHorizontal: 16
},
title: {
fontSize: 32
}
});
export default App;
I think you should use Reanimated 2 who has a very easy sintaxis and also very powerful. Maybe in combination with RNGestureHandler.

i want to create app where users can see polyline between markers and when clicked on marker should see image that are stored in my json

when i use image inside i get error index=5 count=2 can anyone help i want to create ployline between markers and when user clicks on marker they should get ployine and image but i am getting polyline but when i use i get error index=5 count=2 and app crashes[enter image description here][1]
//explorescreen.js code
import React from 'react';
import MapView from 'react-native-maps';
import Geolocation from '#react-native-community/geolocation';
import { View, Text, Dimensions, Animated, StyleSheet, Image } from 'react-native';
import Polyline from '#mapbox/polyline';
const { width, height } = Dimensions.get('screen')
const locations = require('../locations')
export default class ExploreScreen extends React.Component {
state = {
latitude: null,
longitude: null,
locations: locations,
direct: false
}
componentDidMount() {
this.GetLocations();
}
GetLocations = async () => {
Geolocation.getCurrentPosition(
({ coords: { latitude, longitude } }) => this.setState({ latitude, longitude }, this.mergeCoords),
(error) => console.log('Error:', error)
)
const { locations: [sampleLocation] } = this.state
this.setState({
desLatitude: sampleLocation.coords.latitude,
desLongitude: sampleLocation.coords.longitude,
}, this.mergeCoords)
}
mergeCoords = () => {
const { latitude, longitude, desLatitude, desLongitude } = this.state
const hasStartAndEnd = (latitude !== null && desLatitude !== null)
// if the line have start and end
if (hasStartAndEnd) {
const concatStart = `${latitude},${longitude}`
const concatEnd = `${desLatitude},${desLongitude}`
this.getDirections(concatStart, concatEnd)
}
}
async getDirections(startLoc, desLoc) {
try {
const resp = await fetch(`https://maps.googleapis.com/maps/api/directions/json?origin=${startLoc}&destination=${desLoc}&key=AIzaSyDoWUeYVrFse7r76yOMX9dAMgelIlNVGSY`)
const resJson = await resp.json();
const points = Polyline.decode(resJson.routes[0].overview_polyline.points);
const coords = points.map(point => {
return {
latitude: point[0],
longitude: point[1]
}
})
this.setState({ coords, direct: true })
} catch (err) {
console.log('Error: ', err)
}
}
onMarkerPress = location => () => {
const { coords: { latitude, longitude } } = location
this.setState({
destination: location,
desLatitude: latitude,
desLongitude: longitude
}, this.mergeCoords)
}
renderMarkers = () => {
const { locations } = this.state
return (
<View>
{
locations.map((location, index) => {
const {
coords: { latitude, longitude }
} = location
return (
<MapView.Marker
key={index}
coordinate={{ latitude, longitude }}
onPress={this.onMarkerPress(location)}>
<Animated.View style={[styles.markerWrap]}>
<Animated.Image
source={require('../assests/images/marker.png')}
style={styles.marker}
resizeMode="cover" />
</Animated.View>
</MapView.Marker>
)
})
}
</View>
)
}
render() {
const { latitude, longitude, coords, destination } = this.state
if (latitude) {
return (
<MapView
showsUserLocation
style={{ flex: 1 }}
initialRegion={{
latitude,
longitude,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
userLocationPriority: 'high',
userLocationFastestInterval: 8000,
}}
>
{this.renderMarkers()}
{this.state.direct &&
<MapView.Polyline
strokeWidth={2}
strokeColor="purple"
coordinates={coords}
/>
}
<Image
source={{ uri: destination && destination.image_url }}
style={{
flex: 1,
width: width * 0.95,
alignSelf: 'center',
height: height * 0.15,
position: 'absolute',
bottom: height * 0.05
}}
/>
</MapView>
);
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>We need your permissions !!</Text>
</View>
)
}[enter image description here][1]
}
const styles = StyleSheet.create({
markerWrap: {
alignItems: "center",
justifyContent: "center",
width: 50,
height: 50,
}, marker: {
width: 30,
height: 30,
},
})
[1]: https://i.stack.imgur.com/JiO3g.png

Make border-radius greater than half of height

I want to make a "rounded bottom" component, without using a ImageBackground, like this:
I tried to use a combination of <LinearGradient/>, but to simplify the code in this question, I used <View/> instead.
Here is my code:
import React from 'react'
import { Dimensions, StyleSheet, View } from 'react-native'
export default class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<View style={classes.container}>
<View style={classes.block} />
<View style={classes.roundedBlock} />
</View>
)
}
}
const classes = StyleSheet.create({
container: {
flex: 1,
marginTop: 30,
},
block: {
height: 135,
backgroundColor: 'red',
},
roundedBlock: {
height: 15,
backgroundColor: 'red',
width: Dimensions.get('window').width,
borderBottomLeftRadius: Dimensions.get('window').width / 2,
borderBottomRightRadius: Dimensions.get('window').width / 2,
}
})
This code is available for tests purpose on Expo Snack
Here is the result:
As you can see, the borderRadius is limited to 7.5px, which is half of the height of the block, instead of half of the width as demanded.
Is there a way to override this limit? If no, is there a way to achieve what I want?
You can use ART from react-native to draw whatever you want to draw. Some unofficial docs https://github.com/react-native-china/react-native-ART-doc/blob/master/doc.md. Please check the Expo Snack or code below.
import React from 'react';
import { Dimensions, StyleSheet, View, ART } from 'react-native';
const {
Surface,
Shape,
Path,
RadialGradient,
Pattern,
Transform,
LinearGradient,
} = ART;
const width = Dimensions.get('window').width;
export default class App extends React.Component {
constructor(props) {
super(props);
}
getPathRect = () => {
const x = width;
const y = 0;
const radius = 1000;
return ART.Path()
.moveTo(x, y)
.lineTo(x - width, y)
.lineTo(x - width, y + width / 2)
.lineTo(x, y + width / 2)
.close();
};
getPathArc = () => {
const x = width;
const y = 0;
const radius = 1000;
return ART.Path()
.moveTo(x, y + width / 2)
.arc(-x, 0, radius, radius)
.close();
};
gradient = () => {
return new LinearGradient(
{
'.01': 'blue', // blue in 1% position
'1': 'red', // opacity white in 100% position
},
'0',
'0',
width,
'0'
);
};
render() {
return (
<View style={classes.container}>
<Surface width={width} height={width}>
<Shape
d={this.getPathRect()}
fill={this.gradient()}
// stroke="red"
strokeWidth="1"
strokeCap="butt"
strokeJoin="bevel"
/>
<Shape
d={this.getPathArc()}
fill={this.gradient()}
// stroke="red"
strokeWidth="1"
strokeCap="butt"
strokeJoin="bevel"
/>
</Surface>
</View>
);
}
}
const classes = StyleSheet.create({
container: {
flex: 1,
marginTop: 30,
},
});

React Native Compass using Expo

I want to display compass on my react-native project, i use this compas-github i already follow all step on that link but got error like this error.
I really new to this.
Please help me or suggest another way to display compass on react-native.
Big thanks
This is my code
import React, {Component} from "react";
import { Text,View, StyleSheet, Image, Animated, Easing, Dimensions} from "react-native";
import {Location, Permissions} from 'expo';
import {
Container,
Header,
Content,
} from "native-base";
export default class KompasPage extends Component{
constructor(props) {
super(props);
this.spinValue = new Animated.Value(0);
this.state = {
location: null,
//errorMessage: null,
heading: null,
truenoth: null,
timer: false,
};
}
timePass() {
this.setState({ timer: true });
}
componentWillMount() {
setTimeout(() => {
this.timePass();
}, 1000);
this._getLocationAsync();
}
componentWillUpdate()
{
this.spinValue()
}
_getLocationAsync = async () => {
//check dev loc permiss
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
this.setState({
errorMessage : 'Permission to access location was denied',
});
}
else {
Expo.Location.watchHeadingAsync((obj) => {
let heading = obj.magHeading;
this.setState({heading: heading})
})
}
};
spin() {
let start = JSON.stringify(this.spinValue);
let heading = Math.round(this.state.heading);
let rot = +start;
let rotM = rot % 360;
if (rotM < 180 && (heading > (rotM +180)))
rot -= 360;
if (rotM >= 180 && (heading <= (rotM -180)))
rot += 360;
rot += (heading - rotM)
Animated.timing(
this.spinValue,
{
toValue: rot,
duration: 300,
easing: Easing.easeInOut
}
).start()
}
render() {
const { navigate } = this.props.navigation;
let LoadingText = 'Loading...';
let display = LoadingText;
if (this.state.errorMessage)
display = this.state.errorMessage;
const spin = this.spinValue.interpolate({
inputRange: [0,360],
outputRange: ['-0deg', '-360deg']
})
display = Math.round(JSON.stringify(this.spinValue))
if(display < 0)
display += 360
if(display > 360)
display -= 360
return(
<View style={styles.container}>
<Text style={styles.text}>{display+'°'}</Text>
<View style={styles.imageContainer} >
<Animated.Image resizeMode='contain' source={require("../image/dasar_kompas.png")}
style={{
width: deviceWidth - 10, height: deviceHeight/2 - 10,
left: deviceWidth /2 - (deviceWidth - 10)/2, top: deviceHeight /2 - (deviceHeight/2 - 10)/2,
transform: [{rotate: spin}],
}} />
</View>
<View style={styles.arrowContainer} >
<Image resizeMode='contain' source={require("../image/kompasbaru.png")} style={styles.arrow} />
</View>
</View>
);
}
}
const deviceWidth = Dimensions.get('window').width
const deviceHeight = Dimensions.get('window').height
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
text: {
color: '#263544',
fontSize: 80,
transform: ([{translateY: -(deviceHeight/2 - (deviceHeight/2 - 10)/2) - 50 }])
},
imageContainer: {
...StyleSheet.absoluteFillObject,
},
arrowContainer: {
...StyleSheet.absoluteFillObject,
},
arrow: {
width: deviceWidth/7,
height: deviceWidth/7,
left: deviceWidth /2 - (deviceWidth/7)/2,
top: deviceHeight /2 - (deviceWidth/7)/2,
opacity: 0.9
}
});
As far as I know, the example you’re following uses some sort of locational data that requires internet for showing direction. If your device has built in compass or magnetometer, you should make use of that. That way it would be more efficient. Here are two examples, one using expo which is much easier to implement but requires, you guessed it expo. And here is another using only react native.