material-ui-next: Setting image size to fit a container - css

I've started out with Material-ui-next and have some problems with displaying images that they use the entire size of a container.
E.g. I use:
const styles = theme => ({
Card: {
width: 300,
margin: 'auto'
},
Media: {
height: 550
}
});
In render:
<Card className={classes.Card}>
<CardMedia
className={classes.Media}
image={ImgPomodoR}
title="a pomodoro tomatoe timer in material design"
/>
<CardContent>
<Typography gutterBottom variant="headline" component="h2">
...
The documentation says I have to specify a height for the image to get displayed. The 'media' example gives the image a height of 0, however, if I apply that my image is not getting displayed - mentioned example.
Right now, for me it's a trial and error of the Media-height, that it fits the Card container without being cropped.
Is there no 'auto' way of doing this?
Any help is highly appreciated,
cheers mates,
Tobias
Edit: I should mention that height: "100%" // maxHeight: "100%" does also not work for me.

I was having the same issue. Setting both width and height to '100%' worked for me.
const styles = theme => ({
Card: {
width: 300,
margin: 'auto'
},
Media: {
height: '100%',
width: '100%'
}
});
However, if your images have different heights, this will cause cards to be different heights and most of the time that is not what you want. For that, you can specify a height and keep the width at '100%'.
const styles = theme => ({
Card: {
width: 300,
margin: 'auto'
},
Media: {
height: 550,
width: '100%'
}
});
This will stretch the images to fit the container. For my case, I wanted part of the image to be shown without stretching the images. To achieve this, simply set the objectFit property to cover. This worked nicely for me.
const styles = theme => ({
Card: {
width: 300,
margin: 'auto'
},
Media: {
height: 550,
width: '100%',
objectFit: 'cover'
}
});
Hope this helps someone,

I think it would work
const styles = theme => ({
card:{
backgroundColor: 'white',
marginBottom: 40,
},
media: {
height: 0,
// paddingTop: '56.25%', // 16:9,
paddingTop: '100%', // 1:1,
},
});

Related

How I can fill the video component in container?

I am trying to style video so that It will fill the remaining space, but nothing is working so far, anyone has any idea what I can do here:
export const VIDEO_STYLES = {
width: '100%',
height: '100%',
alignSelf: "stretch",
objectFit: 'cover',
}
React.cloneElement(videoComponent, { style: VIDEO_STYLES })

How to implement responsive width for Drawer MUI v5 component?

I am using Material UI v5, and am trying to make a responsive drawer where for smaller devices it will take up 100% of screen width while for larger devices it should only take 1/3 of screen width. But I have no idea how to access Paper property to modify the actual width and make it responsive.
My code:
import { Drawer, styled } from "#mui/material";
const ResponsiveDrawer = styled(Drawer)(({ theme }) => ({
[theme.breakpoints.up("md")]: {
width: "33%", // THIS ONLY CHANGES DRAWER WIDTH NOT PAPER WIDTH INSIDE THE DRAWER
},
[theme.breakpoints.down("md")]: {
width: "100%",
},
}));
export { ResponsiveDrawer };
How I use it:
import { ResponsiveDrawer } from "./Style";
<ResponsiveDrawer
anchor="right"
open={drawer.state}
onClose={() => drawer.onClick(false)}
>
...
</ResponsiveDrawer>
I figured it out shortly after posting the question. This involves inline styling using useMediaQuery.
const largeScreen = useMediaQuery(theme.breakpoints.up("sm"))
<Drawer
anchor="right"
open={drawer.state}
onClose={() => drawer.onClick(false)}
PaperProps={largeScreen ? {
sx: {
width: 450,
}
} : {
sx: {
width: "100%",
}
}
}
>
<CartContent cart={cart} drawer={drawer}/>
</Drawer>
You can add a div inside the <Drawer> or <SwipeableDrawer> component like so and control the width of the div through CSS (or emotion/styled, if you prefer).
<Drawer ...>
<div className="container">...</div>
</Drawer>
.container {
width: 95vw; // for mobile
... add media queries for rest of the screen sizes here
}
In their docs (expand the code blow) they give an example how to access the paper CSS, where you could add your width settings:
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
({ theme, open }) => ({
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap',
boxSizing: 'border-box',
...(open && {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme),
}),
...(!open && {
...closedMixin(theme),
'& .MuiDrawer-paper': closedMixin(theme),
}),
}),
);
They add the same mixin to '& .MuiDrawer-paper' in the main drawer css.
So for your responsive drawer you should add this paper selector to your styled CSS (maybe check with the inspector, if its the right one):
const ResponsiveDrawer = styled(Drawer)(({ theme }) => ({
[theme.breakpoints.up("md")]: {
width: "33%",
'& .MuiDrawer-paper': {
width: "33%",
},
},
[theme.breakpoints.down("md")]: {
width: "100%",
'& .MuiDrawer-paper': {
width: "100%",
},
},
}));
More information about customizing nested elements can be found on https://mui.com/material-ui/customization/how-to-customize/#the-sx-prop.

How to make background image height change depending on page content

I'm having issues getting my background image to fill the screen, depending on the amount of page content. My app is made up of a main div and a side bar navigation. I have my background image styles set to the main div like so:
const styles = makeStyles(theme => ({
backgroundStyle: {
flexGrow: 1,
padding: theme.spacing(3),
height: '100vh',
textAlign: 'center',
backgroundImage: `url(${Background})`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center center",
backgroundSize: "cover",
backgroundAttachment: "fixed",
[theme.breakpoints.down('md')]: {
height: '100%'
}
}
height: '100vh' works good until I select a navigation item that stretches beyond the view height, which cuts off the background image. Setting the height to height: '100%' fixes this but for a navigation item that doesn't have much content, the background image does not fill the entire screen. I having the same issues too when switching to a responsive view.
I set up a conditional to handle this:
return <main className={navListItem === 'Topic1' ? backgroundStyle : backgroundStyle2 }>
But it feels like I'm repeating myself because I'm creating another class called backgroundStyle2 that contains all the same styles only height is set to '100%' instead of '100vh'
I'm thinking there has to be an easier way to make the background image change depending on page the content besides using '100%' or '100vh' but I can't figure it out
If you want to have at least 100vh height but also to extend that height according to the content if the content's height is greater than the viewport height, you should use minHeight:100vh; height:100%
const styles = makeStyles(theme => ({
backgroundStyle: {
flexGrow: 1,
padding: theme.spacing(3),
height: '100%',
minHeight:'100vh',
textAlign: 'center',
backgroundImage: `url(${Background})`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center center",
backgroundSize: "cover",
backgroundAttachment: "fixed",
[theme.breakpoints.down('md')]: {
height: '100%'
}
}

How to mirror and resize the part of image with fixed width and height

I'm building "Tagging from photo" functionality.
When the user move or pinch the square on the image,
PanResponder changes the state of x-coordinate(left), y-coordinate(top), the length of square(thumbSize)
With the data, I want to show the part of square real-time
So this image below should be placed into the left of A, All All from the image above.
Here is the part of render showing the "cropped" image.
console.log(left) // 80
console.log(top) // 200
console.log(thumbSize) // 150
<Image
source={{uri: image}}
style={{height:70, width: 70, bottom: (-top), right: (-left)
}} <- style is not complete. I'm putting some example code
/>
This is continuous problem from: How to show the only part of the image.
It works but the solution doesn't meet my expectation.
It's not changing width and height ( I want to fix resize the image from 'the width of square' to '70' for each width and height)
It breaks the whole style (A, All, All things disappear)
I've been trying to solve this idea for days but couldn't find the exact way.
Update: I almost solved it but resizing matters
I changed Image to CroppedImage (new component)
<CroppedImage
source={{uri: image}}
cropTop={top}
cropLeft={left}
cropWidth={thumbSize}
cropHeight={thumbSize}
width={width(100)}
height={width(100)}
resizeMode="contain" />
Here is CroppedImage
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>
);
It seems working but it can't resize (from square width x height to 70x70).
I made a fiddle to show what calculations you have to do to correctly position and resize your tag image:
$('#image').click(function(event) {
var size_ratio = .6;
var img_src = $(this).attr('src');
var tag = $('#tag-rectangle');
var top_position = tag.height()/2 - event.offsetX*size_ratio;
var left_position = tag.width()/2 - event.offsetY*size_ratio;
$('#tag-rectangle').css({
'background-image': 'url('+img_src+')',
'background-position': top_position +'px '+ left_position + 'px',
'background-size': $(this).width()*size_ratio + 'px ' + $(this).height()*size_ratio + 'px'
});
});
#tag-rectangle {
width: 50px;
height: 50px;
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id="image" src="http://fakeimg.pl/250x100/" alt="">
<div id="tag-rectangle"></div>
Well, I finally managed to create a working React Native code (never used it before, sorry if it is noobish code) doing the same as in my other answer.
Here is the code:
import React, { Component } from 'react';
import { TouchableWithoutFeedback, ImageBackground, Image, View, StyleSheet } from 'react-native';
const IMAGEURI = 'http://fakeimg.pl/300x300/';
const SIZERATIO = .6;
const IMAGEWIDTH = 300;
const IMAGEHEIGHT = 300;
const CROPIMAGEWIDTH = 100;
const CROPIMAGEHEIGHT = 100;
export default class App extends Component {
state = {
style: {
marginLeft: 0,
marginTop: 0,
},
uri: ''
};
repositionImage(event) {
this.setState({
style: {
marginLeft: CROPIMAGEWIDTH/2 - event.nativeEvent.locationX*SIZERATIO,
marginTop: CROPIMAGEHEIGHT/2 - event.nativeEvent.locationY*SIZERATIO
},
uri: IMAGEURI
});
}
render() {
return (
<View>
<TouchableWithoutFeedback onPress={(event) => this.repositionImage(event)}>
<View>
<Image
style={styles.image}
source={{ uri: IMAGEURI }}
/>
</View>
</TouchableWithoutFeedback>
<View style={styles.tag}>
<ImageBackground style={[styles.cropped,this.state.style]} source={{uri: this.state.uri }} />
</View>
</View>
);
}
}
const styles = StyleSheet.create({
image: {
width: IMAGEWIDTH,
height: IMAGEHEIGHT,
},
tag: {
borderWidth: 1,
borderColor: '#000',
width: CROPIMAGEWIDTH,
height: CROPIMAGEHEIGHT,
overflow: 'hidden'
},
cropped: {
width: IMAGEWIDTH*SIZERATIO,
height: IMAGEHEIGHT*SIZERATIO
}
});
And here is the Snack
I really hope it helped!! Good luck!!
EDIT: Ok I will explain a bit what I'm doing here.
First, I set a State with the parameters that will change based on some event:
state = {
style: {
marginLeft: 0,
marginTop: 0,
},
uri: ''
};
Then, I make the component to get its properties from that state:
< ImageBackground style={[styles.cropped,this.state.style]} source={{uri: this.state.uri }} />
Finally, I prepare the onPress event to call a function which will update the state:
< TouchableWithoutFeedback onPress={(event) => this.repositionImage(event)}>
Here I'm feeding my function with the event object so I will be available to get the coordinates where the user pressed.
That last function takes the data from the event and updates the state. The view will automatically refresh with the new state data.
repositionImage(event) {
this.setState({
style: {
marginLeft: CROPIMAGEWIDTH/2 - event.nativeEvent.locationX*SIZERATIO,
marginTop: CROPIMAGEHEIGHT/2 - event.nativeEvent.locationY*SIZERATIO
},
uri: IMAGEURI
});
}
To position the image, I simply do a math operation:
CROPIMAGEWIDTH is the width of my tag element so to get the center I divide it by 2. Then, I substract the locationX of the event to move the image to the left so the locationX will be at the center of the tag.
That is only for positioning. To scale it just multiply the size of the image and the locationX by the same value. Note I multiplied the width and height of the image with the SIZERATIO in the cropped style
cropped: {
width: IMAGEWIDTH*SIZERATIO,
height: IMAGEHEIGHT*SIZERATIO
}
An example of this scaling stuff:
If your image has 200 width and you want to scale it to a half, you multiply it by 0.5. So if you click at the, say, pixel 180 starting for the left, the equivalent pixel for your scaled image will have to be multiplied by 0.5 too and it will be 90.
If there is something I didn't explaing clearly enough just ask me again a I will be glad to help you.

React native styling. width: percentage - number

I want to do width: 100% - 50 so I can add an icon which is 50 wide on the right hand side of it.
I have got width: 100% - 20% working by using react-native-extended-styles but I don't see why that is useful because you can do width: '80%'. I cannot get width: 100% - 50 working. Is there a way?
Trying to use the onLayout event to get the container width, then set the <autocomplete> to 100% - 50 of the container width but it isn't working.
let Location = (props) => {
let locationInputElement
const blur = () => {
locationInputElement.blur()
}
let inputContainerWidth
return (
<View style={styles.formItem}>
<View
onLayout={(event) => {
inputContainerWidth = event.nativeEvent.layout.width
console.log(inputContainerWidth)
}}
<Autocomplete
data={props.autocompleteResults.predictions}...
style={{
borderRadius: 8,
backgroundColor: 'red',
alignSelf: 'stretch',
paddingLeft: 10,
position: 'relative',
...styles.label,
...styles.labelHeight,
width: inputContainerWidth - 50
}}
/>
</View>
</View>
)
}
It does console.log 335 when it console.logs inputContainerWidth but the width of the <autocomplete> is 100% still.
I'd agree with Viktor, you should be able to achieve this using Flex Box.
Here's something I put together: https://snack.expo.io/B1jDKOhyb
You set the flexDirection of the formRow to row, and then the first child (the holder View for your AutoComplete component to flex: 1. This makes it fill all available space. The next child View is your icon holder. Which you can set to whatever value you want (in this case 50).
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.formRow}>
<View style={styles.formItem}>
// AutoComplete component goes here
</View>
<View style={styles.formIcon}>
// Icon goes here
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 100
},
formRow: {
flexDirection: 'row',
height: 50,
},
formItem: {
flex: 1,
backgroundColor: 'dodgerblue',
},
formIcon: {
width: 50,
backgroundColor: 'greenyellow',
},
});
This can easily be solved by using Dimensions.
import { Dimensions } from 'react-native';
const MyComponent = () => {
return <Text style={{height: Dimensions.get('window').height - 100}}></Text>
};
export default MyComponent;
You can do it without computing width. Use marginHorizontal: 50 with width:100 or flex:1.
Your code not working because, it's rendered then inputContainerWidth updated. To make it work, there should be another render with new inputContainerWidth. So you can't use a stateless component. Change Location to regular component and add inputContainerWidth to state.
Consider using useWindowDimensions from react-native (see docs):
import { useWindowDimensions } from 'react-native';
const { height, width } = useWindowDimensions();
I did margin: -50 in the parent and it worked for me

Resources