最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin5浏览0评论

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 ponent.

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: .

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()"
  }
}));

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={""}
            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={""}
            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>
  );
}

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 ponent.

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./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./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./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>
  );
}
Share Improve this question edited Dec 23, 2019 at 22:52 Ryan Cogswell 81.1k9 gold badges241 silver badges212 bronze badges asked Dec 23, 2019 at 21:45 colemarscolemars 1,0983 gold badges14 silver badges29 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

The solution below is based on the article here: https://css-tricks./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./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./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./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>
  );
}

Hello in my case I was able to fix it by adding sx={{ transform: "unset"}}, just removing the transformation will fix it.

<Skeleton width={width} height={height} sx={{ transform: "unset" }} />
发布评论

评论列表(0)

  1. 暂无评论