19

I'm using Material UI v3 within a react project (react v15.6).

What i did so far?

In the sign up page i can get an image from the user to use as his/her profile photo.

What i want to do

I would like to have a shade on the avatar photo to show him that is clickable. like this image...

avatarChooserImage

How can i do such a thing? I tried to use plain CSS, but much effort for nothing.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
Firus
  • 537
  • 1
  • 5
  • 18
  • Thanks for share your position @vir us. I made this question when i had just 1~2 months of experience in front-end development, and yeah, now i can see some improvements that i can make. But i would like to know anything that could help even more the ones with this same problem. – Firus May 06 '20 at 23:09
  • https://codesandbox.io/s/material-ui-avatar-upload-example-hgfux – Michael Pacheco Oct 24 '20 at 06:24

4 Answers4

18

You can do something simple like this,

<IconButton>
 <Avatar 
  src="/images/example.jpg" 
  style={{
    margin: "10px",
    width: "60px",
    height: "60px",
  }} 
 />
</IconButton>

Which allow you to have a clickable avatar.

But since you are using it as file input too, maybe you can do something like this,

<input
 accept="image/*"
 className={classes.input}
 id="contained-button-file"
 multiple
 type="file"
/>
<label htmlFor="contained-button-file">
  <IconButton>
   <Avatar 
     src="/images/example.jpg" 
     style={{
      margin: "10px",
      width: "60px",
      height: "60px",
     }} 
    />
  </IconButton>
</label>

To do an overlay for "edit", have a look at css image overlay. This explains how to place a layer on top on Avatar Icon, it should answer you question.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
will92
  • 956
  • 7
  • 12
1

There's a similar example of this in the documentation:

https://material-ui.com/components/buttons/#upload-button

Instead of using "PhotoCamera" , you can use "Avatar". Allowing you to click on your avatar to upload an image like this for example:

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Avatar from '@material-ui/core/Avatar';
import IconButton from '@material-ui/core/IconButton';



const useStyles = makeStyles((theme) => ({
  root: {
    alignSelf: 'center',
    justifyContent: "center",
    alignItems: "center",
    display: 'flex',
    '& > *': {
      margin: theme.spacing(1),
    },
  },
  input: {
    display: "none",
  },
  large: {
    width: theme.spacing(7),
    height: theme.spacing(7),
  },
}));

export default function ProfileAvatar() {
  const classes = useStyles();


  return (

    <div className={classes.root}>

      <input accept="image/*" className={classes.input} id="icon-button-file" type="file" />
      <label htmlFor="icon-button-file">
        <IconButton color="primary" aria-label="upload picture" component="span">
          <Avatar src="https://www.w3schools.com/howto/img_avatar.png" className={classes.large} />
        </IconButton>
      </label>
    </div>
  );
}
Hoppjerka
  • 128
  • 1
  • 9
1

I wanted to create the same and nearly never styled components before. So here we go, feel free to add things to it and vice versa.

So for the start I went with Mui's complex buttons you can find here: https://mui.com/material-ui/react-button/#complex-button They already got me the base for the overlay styling.

From there I put those in an avatar component, because profile picture and so on. Since I wanted to use that as an onClick file upload, I implemented the <input type="file />. This seemed to only work when inside a button when prop component="label" is set.

see here: How to enable file upload on React's Material UI simple input?

Unfortunately it looks like styled components, like <ImageButton> can not carry this prop.

see here: https://github.com/mui/material-ui/issues/35966

Thats why the workaround with the <ImageInputButton> is/was needed.

I also tried wrapping the <CameraAltIcon> into an <IconButton> which can carry the component prop, but then only the icon is clickable and not the whole avatar, which was my goal here. Wrapping the whole avatar didn't work for me.

Since I have no idea how to codesandbox and co, where you can try it live, I will post the two pictures of behaviour here, as well as the code below.

Styled hoverable avatar, left: normal, right: on hover

import {
  Avatar,
  Button,
  ButtonProps
} from '@mui/material';
import CameraAltIcon from '@mui/icons-material/CameraAlt';
import { styled } from '@mui/material/styles';

//This button is needed so "component='label'" can be set for the styled component ImageButton.
//This way the whole overlay works as an input and is clickable.

  const ImageInputButton = (props: ButtonProps<'label'>) => {
    return <Button {...props} component="label" />;
  };

  //start of styling for logo and overlay
  const ImageButton = styled(ImageInputButton)(({ theme }) => ({
    position: 'relative',
    height: 150,
    [theme.breakpoints.down('sm')]: {
      width: '100% !important', // Overrides inline-style
      height: 100,
    },
    '&:hover, &.Mui-focusVisible': {
      zIndex: 1,
      '& .MuiImageBackdrop-root': {
        opacity: 0.4,
      },
      '& .MuiImageMarked-root': {
        opacity: 0.8,
      },
    },
  }));

  //styling for image/logo/profilepicture
  const ImageSrc = styled('span')({
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    backgroundSize: 'cover',
    backgroundPosition: 'center',
  });

  //styling for opacity effect
  const ImageBackdrop = styled('span')(({ theme }) => ({
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    backgroundColor: theme.palette.common.black,
    opacity: 0,
    transition: theme.transitions.create('opacity'),
  }));

  //styling for overlay logo (here the cameraIcon)
  const Image = styled('span')(({ theme }) => ({
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    opactiy: 0,
    color: theme.palette.common.white,
  }));
  //end of styling

return (
  <Avatar
    sx={{
      height: 150,
      width: 150,
      backgroundColor: 'transparent',
    }}
  >
    <ImageButton
      focusRipple
      key={'random'}
      style={{
        width: '100%',
      }}
    >
      <ImageSrc style={{backgroundImage: `url(${image})`}} />
      <ImageBackdrop className='MuiImageBackdrop-root' />
      <Image>
        <CameraAltIcon
          className='MuiImageMarked-root'
          fontSize='large'
          sx={{
            color: 'white',
            opacity: 0,
          }}
        />
      </Image>
      <input type='file' accept='image/*' hidden onChange={(e) => submitLogo(e.target.files)} />
    </ImageButton>
  </Avatar>
);

desertnaut
  • 57,590
  • 26
  • 140
  • 166
TiMs
  • 11
  • 1
0

An hidden input that accepts images only,
and an IconButton with component="span" property, surrounded by the input's label:

<input accept="image/*" id="upload-avatar-pic" type="file" hidden />
<label htmlFor="upload-avatar-pic">
    <IconButton component="span">
        <Avatar />
    </IconButton>
</label>
I.sh.
  • 252
  • 3
  • 13