The following is the code and what it produces:
const polygon = [
[40.775325582986405, -73.97347927093507],
[40.77363562511118, -73.9683508872986],
[40.77157186825386, -73.97103309631349],
[40.77251437933824, -73.97506713867189],
];
const limeOptions = { color: 'lime' }
export default function App() {
return (
<MapContainer
center={center}
zoom={16}
style={{ width: '100vw', height: '100vh' }}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Polygon pathOptions={limeOptions} positions={polygon}>
</Polygon>
<ImageOverlay bounds={polygon} url="http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg"/>
</MapContainer>
);
}
Map with polygon and image
I want it so that the image is inside the polygon, and stretched to fit inside the polygon.
Is there a way to do this? I'm having trouble manipulating the react-leaflet components. Right now I am trying to use an ImageOverlay, but is this the correct thing to even use?
Related
While the image is loading, I want to show a color (black) and have that fade to the image once the image is loaded. Is there any good way to do this?
import Image from 'next/image'
const myLoader = ({ src, width, quality }) => {
return `https://example.com/${src}?w=${width}&q=${quality || 75}`
}
const MyImage = (props) => {
return (
<Image
loader={myLoader}
src="me.png"
alt="Picture of the author"
width={500}
height={500}
/>
)
}
First add classes for opacity. And add a function for onLoadingComplete.
<Image
loader={myLoader}
src="me.png"
alt="Picture of the author"
width={500}
height={500}
className={`${
heroHasLoaded ? "opacity-100" : "opacity-0"
} transition-opacity duration-500`}
onLoadingComplete={doFadeIn}
/>
Add the callback function.
const doFadeIn = () => {
setHeroHasLoaded(true);
};
You also need to set the initial background color. I set this on a <div> that contains the image.
Note that if this image is going to be the Largest Contentful Paint, then also add priority to the <Image /> component. https://nextjs.org/docs/basic-features/image-optimization#priority
Like the title says, I want to ask if there's a way to create a button with gradient color and an icon in React Native? I know that gradient color can't be added without an external library, so I tried this one:
https://www.npmjs.com/package/react-native-gradient-buttons
However, I can't see a way to add Icon as a props to the gradient buttons of this library. The answer doesn't have to use the library, I would just like to know a convenient way to achieve the button I described. Thanks.
You can create your own button with an icon like below
import { Ionicons } from '#expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
const GradientButton = ({ onPress, style, colors, text, renderIcon }) => {
return (
<TouchableOpacity onPress={onPress}>
<LinearGradient colors={colors} style={style}>
{renderIcon()}
<Text style={styles.text}>{text}</Text>
</LinearGradient>
</TouchableOpacity>
);
};
Usage would be
<GradientButton
onPress={() => alert('Button Pressed')}
style={{
padding: 15,
alignItems: 'center',
borderRadius: 5,
flexDirection: 'row',
}}
colors={['#874f00', '#f5ba57']}
text="Press"
renderIcon={() => (
<Ionicons
name="md-checkmark-circle"
size={20}
color="green"
style={{ marginHorizontal: 20 }}
/>
)}
/>
You will have more control over the button and change it anyway you want, you can add more props to customize it the way you want.
You can try out the demo here
https://snack.expo.io/#guruparan/gradientbutton
The above icon and gradient packages are for expo
You can use RN Vector icons and Linear gradient package as well
In the docs of the package you provided it shows an example like this:
<GradientButton
style={{ marginVertical: 8 }}
textStyle={{ fontSize: 20 }}
gradientBegin="#874f00"
gradientEnd="#f5ba57"
gradientDirection="diagonal"
height={60}
width={300}
radius={15}
impact
impactStyle='Light'
onPressAction={() => alert('You pressed me!')}
>
Gradient Button #2
</GradientButton>
Maybe try adding your icon in between the tags instead of as prop?
On android emulator, my React Native 0.62.2 app displays uploaded images within an accordion whose display area is warped with <View> which is styled with width and height. The image is warped with which is <Image> with cache ability. The problem is that the image is positioned way below beyond the viewing boundary and is not visible when open the accordion.
Here is the render code in accordion which is provided with both width and height as canvas:
return (
<>
<TouchableWithoutFeedback onPress={() => toggleListItem()}>
<View style={styles.titleContainer}>
<Text>{title}</Text>
<Animated.View style={{ transform: [{ rotateZ: arrowAngle }] }}>
<Icon name="chevron-down-outline" size={20} />
</Animated.View>
</View>
</TouchableWithoutFeedback>
<Animated.View style={[styles.bodyBackground, { height: bodyHeight }]}>
<View
style={(absPosition ? styles.bodyContainerAbs : styles.bodyContainerCenter), (screenSize ? {width:screenSize.width, height:screenSize.height, flex:1} : null)}
onLayout={event => {
if (screenSize) {
setBodySectionHeight(screenSize.height);
} else {
setBodySectionHeight(event.nativeEvent.layout.height);
};
console.log("layout : ", event.nativeEvent.layout);
}
}> //<<<===here is <View> warping the image area. Both width and height are set.
{children} //<<<===images uploaded displayed here
</View>
</Animated.View>
</>
);
const styles = StyleSheet.create({
bodyBackground: {
backgroundColor: '#EFEFEF',
overflow: 'hidden',
},
bodyContainerCenter: { //<<<==here is the style used for display images
padding: 1,
paddingLeft: 0,
justifyContent: 'center',
alignItems: 'center',
},
bodyContainerAbs: {
padding: 5,
paddingLeft: 10,
position: 'absolute',
bottom: 0,
},
});
Here is the image render code:
const displayImg = (img_source, width, ht, index) => {
if (img_source && img_source!==[] && img_source!=={}) {
return (
<ImageZoom cropWidth={screen_width}
cropHeight={screen_ht}
imageWidth={width}
imageHeight={ht}
enableSwipeDown={true}
style={{padding:1}}
>
<FastImage
source={{uri:img_source}}
resizeMode={FastImage.resizeMode.contain}
style={{ //<<<===image width and height are set
width:width,
height:ht,
position:"absolute" //<<<===does not work
}}
/>
</ImageZoom>
);
} else {
return null;
};
};
When there is one image, it is display with full screen width. For 2 images, then they are displayed side by side occupying half of the screen width:
import { Col, Row, Grid } from 'react-native-easy-grid';
const DisplayImages = ({pics}) => {
if (!pics || pics===[] || pics==={}) return null;
var len = pics.length,
if (len > 0) setImgAccordOpen(true);
switch(len) {
case 0:
return;
case 1: //<<<=== one image is uploaded
return (
<React.Fragment>
{displayImg(pics[0].path, screen_width*full, screen_width*full, 0)}
</React.Fragment>
);
case 2: //<<<=== case of 2 images uploaded
return (
<Grid style={{position:"absolute", paddingTop:0,paddingLeft:0}}>
<Row style={{paddingTop:0}}>
<Col style={{position:"absolute", top:0,left:0, paddingVertical:0}} > //<<==positioned at [0,0] on up left corner.
{displayImg(pics[0].path, screen_width*half, screen_width*half, 0)}
</Col>
<Col style={{position:"absolute", top:0,left:Math.ceil((screen_width-20)/2), paddingTop:0}}> //<<==positioned at [0, middle of screen]
{displayImg(pics[1].path, screen_width*half, screen_width*half, 1)}
</Col>
</Row>
</Grid>
);
.....
Here is the screen shot when one image is uploaded. The image is not positioned at absolute
Here is the screen shot when 2 images are uploaded. The first image is not within the viewing area and not visible at all.
How to position images right within viewing area?
The problem is the <ImageZoom> is not compatible with <FastImage> and only show a portion of image or positioning images way below the viewing screen. After removing <ImageZoom>, then the position of the images is back to normal as style dictated. Another module #dudigital/react-native-zoomable-view works fine with <FastImage>.
I want to reproduce that style in my component (the border bottom).
I am basically loading a rectangular image, then I want to rounded it at the bottom.
I think about :
border-bottom-right-radius and left, but it is not accurate with the custom shape of the screen.
transform scaleX, but it scale the image proportion (obviously)
create a white image with the custom shape and load it at the bottom of the screen, but I want to add a shadow at the bottom of the image... With a white image I could not do that...
Is there a way to do that in a proper style with React-Native ?
Thank you !
This is possible, but it's a little tricky. The idea is you need to create a "mask" of sorts that you can apply the shape to. After doing that you can put the image as a child of the mask element so that it will essentially mask the area you want. It's probably possible to do it with a dynamic size, but I didn't take the time to come up with that solution, i'll leave that to you ;)
Hopefully this'll get you going in the right direction though.
First lets setup the app structure
class App extends Component {
render() {
return (
<View style={styles.app}>
<Mask />
</View>
);
}
}
Pretty straightforward, just a basic app with a mask component. I made it a component so you can pass props to it in the future (like the image uri for instance).
Then the mask component
const logoUri = `http://66.media.tumblr.com/86b941b3445b80a518ea51208f48ab35/tumblr_ntpi99a6Pl1uounv1o1_500.png`;
const Mask = (props) => (
<View style={styles.maskContainer}>
<View style={styles.mask}>
<Image
source={{ uri: logoUri }}
style={styles.img}
/>
</View>
</View>
)
The maskContainer is a positioning element to help center the image.
The mask uses the oval style approach but to get the edges to not round like border radius, we have to scale it 2x
The img style needs to reverse the scaling so that the image itself is not warped :)
const styles = StyleSheet.create({
app: {
marginHorizontal: "auto",
maxWidth: 500,
backgroundColor: "#e0e0e0",
width: 700,
height: 700
},
mask: {
width: 200,
height: 470,
borderBottomLeftRadius: 100,
borderBottomRightRadius: 100,
overflow: "hidden",
transform: [{ scaleX: 2 }]
},
img: {
height: 470,
width: 299,
left: 25,
position: "absolute",
transform: [{ scaleX: 0.5 }, { translate: "-50%" }]
},
maskContainer: {
position: "absolute",
left: "50%",
transform: [{ translate: "-50%" }]
}
});
See it working on this fiddle!
#John Ruddell's answer is very helpful.
I am able to design this by creating a mask and applied through SVG path. So, you need to install 2 libraries.
react-native-svg (expo install react-native-svg)
react-native-masked-view (npm install --save #react-native-community/masked-view)
Following code respect the aspect ratio of image. So, you need the set the aspect ratio of your image and adjust the value of curveAdjustment in order to achieve your desired sharpness of curve.
import React from "react";
import {
Image,
View,
StyleSheet,
Text,
useWindowDimensions,
} from "react-native";
import MaskedView from "#react-native-community/masked-view";
import Svg, { Path } from "react-native-svg";
const logoUri = `http://66.media.tumblr.com/86b941b3445b80a518ea51208f48ab35/tumblr_ntpi99a6Pl1uounv1o1_500.png`;
function SplashScreen(props) {
const windowWidth = useWindowDimensions().width;
const imageAspectWidth = 375;
const imageAspectHeight = 332;
const curveAdjustment = 40;
const maskHeight = (imageAspectHeight / imageAspectWidth) * windowWidth;
const scaleFactor = imageAspectWidth / imageAspectHeight;
const scaledHeight = scaleFactor * maskHeight;
const controlPointX = windowWidth / 2.0;
const controlPointY = scaledHeight + curveAdjustment;
const curveCenterPointY = (controlPointY - maskHeight) / 2;
return (
<View style={styles.main}>
<MaskedView
style={[
styles.mask,
{
height: controlPointY - curveCenterPointY,
},
]}
maskElement={
<Svg height="100%" width="100%">
<Path
d={`M0 0 L${windowWidth} 0 L${windowWidth} ${maskHeight} Q${controlPointX} ${controlPointY} 0 ${maskHeight} Z`}
fill={"#fff"}
/>
</Svg>
}
>
<Image source={{ uri: logoUri }} style={styles.image} />
</MaskedView>
<Text>{"Tag line"}</Text>
</View>
);
}
const styles = StyleSheet.create({
image: {
flex: 1,
resizeMode: "stretch",
},
main: {
flex: 1,
},
mask: {
backgroundColor: "orange",
width: "100%",
},
});
export default SplashScreen;
Final result
Yep. clip-path property!
Just add:
clip-path: circle(69.3% at 50% 30%)
To your class and it will work.
if you want to create it yourself, there's a generator here:
https://bennettfeely.com/clippy/
I am making a graph which must zoom and display tooltips for each data point. In Victory's documentation there is a multi-line chart with tooltips however if you adjust the data set, the tooltips overlap. I found that VictoryPortal is a mechanism for overcoming this but it appears that even in the linked example, it is using VictoryPortal. (The tooltip's props has renderInPortal: true,)
To overcome this I created a second VictoryGroup after the Lines/Scatters which was just Scatters of all the data sets combined and gave them opacity: 0 so it looked like you were hovering the original Scatter dot. This approach works but feel like there must be a better, cleaner way that I'm missing here. I couldn't figure out how to make a snippet work with Victory so here's the code in question:
Method for rendering Lines/Scatters for individual data sets together in a group:
renderLine = (m, i) => (
this.state.s[i]
? <VictoryGroup
key={i}
color={m.color}
data={m.data}
labels={(d) => `y: ${d.y}\nx: ${d.x}`}
labelComponent={
<VictoryTooltip
cornerRadius={0}
style={{ fontSize: 10 }}
/>}
>
<VictoryLine />
<VictoryScatter size={(d, a) => a ? 8 : 3} />
</VictoryGroup>
: null
)
And my render method:
render() {
const { renderLine } = this
return (
<div style={{ width: '50vw', margin: '0 auto' }}>
<VictoryChart
height={400}
width={400}
padding={30}
containerComponent={<VictoryZoomContainer />}>
<VictoryAxis
domain={{ x: [-180, 180] }}
standalone
label={'Angle (°)'}
style={axisStyle}
crossAxis={false}
orientation={'bottom'}
axisLabelComponent={
<VictoryLabel orientation={'top'} y={'97%'} verticalAnchor={'end'} />
}
gridComponent={
<Line
style={{
...axisStyle.grid,
transform: 'translate3d(0,100%,0)'
}}
/>
}
tickCount={12}
tickLabelComponent={<VictoryLabel dy={-5} />}
/>
<VictoryAxis
dependentAxis
orientation={'left'}
label={'Discriminiation (dB)'}
axisLabelComponent={
<VictoryLabel orientation={'left'} x={15} />
}
standalone
offsetX={30}
style={axisStyle}
/>
{masterData.map((d, i) => renderLine(d, i))}
<VictoryGroup
color={'rgba(0,0,0,0)'}
data={[...dataSet, ...dataSet2, ...dataSet3, ...dataSet4]}
labels={(d) => `y: ${d.y}\nx: ${d.x}`}
labelComponent={
<VictoryTooltip
cornerRadius={0}
style={{ fontSize: 10 }}
/>}
>
<VictoryScatter size={(d, a) => a ? 8 : 3} />
</VictoryGroup>
</VictoryChart>
</div>
);
}
Any input or advice would be greatly appreciated. Thanks in advance!
I started with a similar solution but finally I had some successful results with the VoronoiContainer component and using the tooltip from there.
https://formidable.com/open-source/victory/docs/victory-voronoi-container/
You have to put a VoronoiContainer as a containerComponent in VictoryChart.
With that method the VictoryScatter is unnecessary.