A good way to handle @material-ui Skeleton scaling within a variable height grid row?

房东的猫 提交于 2020-08-05 07:25:23

问题


I'm trying to display a grid of Avatar images. While in a transition state I would like for a skeleton representation of the Image to appear. For that I'm using @material-ui/lab/Skeleton.

The problem I'm having is that, because my images are set to autoscale within a grid using height: auto, width: 100%, and the height/length of the content displayed under the image varies, there is no set height val that I can pass over to the skeleton component.

You can see the problem this causes if you scale down the width of the sandbox screen. The grid item's height increases causing the circle skeleton to begin morphing into an oval.

Is there a solution here that might give me behavior similar to the image's height: auto, width: 100%?

The full code of what I have so far is below and in the sandbox here: https://codesandbox.io/s/skeleton-scaling-y00cd.

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Avatar from "@material-ui/core/Avatar";
import Skeleton from "@material-ui/lab/Skeleton";
import Typography from "@material-ui/core/Typography";
import clsx from "clsx";
import Grid from "@material-ui/core/Grid";

const useStyles = makeStyles(theme => ({
  root: {
    textAlign: "center",
    height: "100%",
    width: "100%"
  },
  title: {
    marginTop: theme.spacing(1)
  },
  avatarRoot: {
    // width: '100%',
    // height: 'auto',
    // minHeight: '273px',
  },
  withTitle: {
    margin: "auto"
  },
  img: {},
  content: {
    lineHeight: "1.4em"
  },
  link: {
    color: "inherit",
    textDecoration: "none"
  },
  icon: {},
  fillContainer: {
    height: `auto`,
    width: `100%`,
    fontSize: "4em"
  },
  container: {
    marginTop: "250px"
  },
  fallback: {
    height: "75%",
    width: "auto"
  },
  loader: {},
  avatarLoader: {
    height: "75%",
    width: "100%"
  },
  titleLoader: {
    width: "60%",
    margin: "auto",
    marginTop: "8px",
    height: "5%"
  },
  contentLoader: {
    width: "40%",
    margin: "auto",
    marginTop: "8px",
    height: "5%"
  },
  testImg: {
    borderRadius: "100%",
    height: "auto",
    width: "100%"
  },
  isLoading: {
    display: "none"
  },
  imgContainer: {
    paddingTop: "100%",
    borderRadius: "100%",
    backgroundPosition: "center",
    backgroundSize: "contain",
    backgroundImage: "url(https://via.placeholder.com/500)"
  }
}));

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

  return (
    <div className={classes.root}>
      <Grid container spacing={1} className={classes.container}>
        <Grid container item xs={3} spacing={0} direction="column">
          <Avatar
            src={"https://via.placeholder.com/500"}
            variant="circle"
            className={clsx(classes.avatarRoot, {
              [classes.isLoading]: false,
              [classes.withTitle]: true,
              [classes.fallback]: false,
              [classes.fillContainer]: true
            })}
          />
          <Typography className={classes.title}>MUI Avatar</Typography>
          <Typography className={classes.content}>test test test</Typography>
        </Grid>
        <Grid container item xs={3} spacing={0} direction="column">
          <div className={classes.imgContainer} />
          <div>
            <Typography className={classes.title}>background image</Typography>
            <Typography className={classes.content}>test test test</Typography>
          </div>
        </Grid>
        <Grid container item xs={3} spacing={0} direction="column">
          <img
            className={classes.testImg}
            src={"https://via.placeholder.com/500"}
            alt={"test"}
          />
          <div>
            <Typography className={classes.title}>image el</Typography>
            <Typography className={classes.content}>test test test</Typography>
          </div>
        </Grid>
        <Grid container item xs={3} spacing={0} direction="column">
          <Skeleton
            variant="circle"
            className={clsx(classes.avatarLoader, classes.avatarRoot)}
          />
          <Skeleton className={clsx(classes.titleLoader, classes.title)} />
          <Skeleton className={clsx(classes.contentLoader, classes.content)} />
        </Grid>
      </Grid>
    </div>
  );
}

回答1:


The solution below is based on the article here: https://css-tricks.com/aspect-ratio-boxes/#article-header-id-3

The gist of the solution is to use padding-top expressed as a percentage to create a box with a specific aspect ratio (square in this case). Padding in percentages is based on width even when specifying padding-top or padding-bottom, so a padding-top of 100% produces a padding height equal to the width.

The relevant CSS/JSS is:

  avatarSkeletonContainer: {
    height: 0,
    overflow: "hidden",
    paddingTop: "100%",
    position: "relative"
  },
  avatarLoader: {
    position: "absolute",
    top: 0,
    left: 0,
    width: "100%",
    height: "100%"
  },

This is then used as follows:

        <Grid container item xs={3} spacing={0} direction="column">
          <div className={classes.avatarSkeletonContainer}>
            <Skeleton variant="circle" className={classes.avatarLoader} />
          </div>
          <Skeleton className={clsx(classes.titleLoader, classes.title)} />
          <Skeleton className={clsx(classes.contentLoader, classes.content)} />
        </Grid>

Here's the full code of my modification of your sandbox:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Avatar from "@material-ui/core/Avatar";
import Skeleton from "@material-ui/lab/Skeleton";
import Typography from "@material-ui/core/Typography";
import clsx from "clsx";
import Grid from "@material-ui/core/Grid";

const useStyles = makeStyles(theme => ({
  root: {
    textAlign: "center",
    height: "100%",
    width: "100%"
  },
  title: {
    marginTop: theme.spacing(1)
  },
  withTitle: {
    margin: "auto"
  },
  content: {
    lineHeight: "1.4em"
  },
  link: {
    color: "inherit",
    textDecoration: "none"
  },
  fillContainer: {
    height: `auto`,
    width: `100%`,
    fontSize: "4em"
  },
  container: {
    marginTop: "250px"
  },
  fallback: {
    height: "75%",
    width: "auto"
  },
  avatarSkeletonContainer: {
    height: 0,
    overflow: "hidden",
    paddingTop: "100%",
    position: "relative"
  },
  avatarLoader: {
    position: "absolute",
    top: 0,
    left: 0,
    width: "100%",
    height: "100%"
  },
  titleLoader: {
    width: "60%",
    margin: "auto",
    marginTop: "8px",
    height: "5%"
  },
  contentLoader: {
    width: "40%",
    margin: "auto",
    marginTop: "8px",
    height: "5%"
  },
  testImg: {
    borderRadius: "100%",
    height: "auto",
    width: "100%"
  },
  isLoading: {
    display: "none"
  },
  imgContainer: {
    paddingTop: "100%",
    borderRadius: "100%",
    backgroundPosition: "center",
    backgroundSize: "contain",
    backgroundImage: "url(https://via.placeholder.com/500)"
  }
}));

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

  return (
    <div className={classes.root}>
      <Grid container spacing={1} className={classes.container}>
        <Grid container item xs={3} spacing={0} direction="column">
          <Avatar
            src={"https://via.placeholder.com/500"}
            variant="circle"
            className={clsx(classes.avatarRoot, {
              [classes.isLoading]: false,
              [classes.withTitle]: true,
              [classes.fallback]: false,
              [classes.fillContainer]: true
            })}
          />
          <Typography className={classes.title}>MUI Avatar</Typography>
          <Typography className={classes.content}>test test test</Typography>
        </Grid>
        <Grid container item xs={3} spacing={0} direction="column">
          <div className={classes.imgContainer} />
          <div>
            <Typography className={classes.title}>background image</Typography>
            <Typography className={classes.content}>test test test</Typography>
          </div>
        </Grid>
        <Grid container item xs={3} spacing={0} direction="column">
          <img
            className={classes.testImg}
            src={"https://via.placeholder.com/500"}
            alt={"test"}
          />
          <div>
            <Typography className={classes.title}>image el</Typography>
            <Typography className={classes.content}>test test test</Typography>
          </div>
        </Grid>
        <Grid container item xs={3} spacing={0} direction="column">
          <div className={classes.avatarSkeletonContainer}>
            <Skeleton variant="circle" className={classes.avatarLoader} />
          </div>
          <Skeleton className={clsx(classes.titleLoader, classes.title)} />
          <Skeleton className={clsx(classes.contentLoader, classes.content)} />
        </Grid>
      </Grid>
    </div>
  );
}



来源:https://stackoverflow.com/questions/59461615/a-good-way-to-handle-material-ui-skeleton-scaling-within-a-variable-height-grid

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!