You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Express+React实现书籍封面随显示尺寸自适应调整(不裁剪)

书籍封面自适应显示问题(MUI实现)

我正在开发一个书籍阅读网站,首页需要展示多本书籍,希望实现书籍封面根据显示尺寸自适应调整大小且不被裁剪,当前使用MUI库构建UI,但不同尺寸的封面会互相挤占空间,当前效果显示封面布局混乱,元素互相挤压。

相关代码

Home.js

import { ImageList, ImageListItem, ImageListItemBar, 
    Typography, Link, Grid, Divider } from '@mui/material'
import {  useContext } from 'react'
import { useNavigate } from 'react-router-dom'

import { PageLayout, Context } from '../Components/PageLayout'

function Feed({ catg, catgTitle, routeName = "" }) {
    const navg = useNavigate();
    const bookCatg = useContext(Context)[0][catg];

    return (
        <>
            <Grid
                container
                direction="row"
                justifyContent="space-between"
                alignItems="center"
            >
                <Grid item>
                    <Typography variant="h6">{catgTitle}</Typography>
                </Grid>

                <Grid item>
                    <Link
                        href={`/book/${routeName}`}
                        variant="body1"
                        underline="none"
                        component="button"
                    >
                        More..
                    </Link>
                </Grid>
            </Grid>

            <Divider />

            <ImageList
                sx={{ pt: '10px' }}
                cols={4}
                spacing={1}
                rowHeight={250}
            >
                {bookCatg.map((book) => (
                    <ImageListItem key={book._id}>
                        <img
                            onClick={() => navg(`/book/${book._id}`)}
                            src={book.img}
                            sx={{ height: "50px" }}
                            alt='Not Found'
                            loading="lazy"
                        />
                        <ImageListItemBar
                            onClick={() => navg(`/book/${book._id}`)}
                            sx={{ width: '10vw' }}
                            title={book.title}
                            position="below"
                        />
                    </ImageListItem>
                ))}
            </ImageList>
        </>
    )
}

export default function Home(){
    var element = <>
        <Feed catgTitle={'New Arrival'}
            catg={'newArrival'} routeName={'newest'} />
        <Feed catgTitle={'Popular Books'}
            catg={'popRating'} routeName={'popular'} />
        <Feed catgTitle={'Hot Rated'}
            catg={'hotRating'} routeName={'hot'} />
        <Feed catgTitle={'Rank'}
            catg={'ranking'} routeName={'ranking'} />
    </>;
    
    return (
        <PageLayout 
            url="/book"
            gridElem={element} 
            failureMsg="Error"
        />
    )
} 

PageLayout.jsx

import { Grid, Typography } from '@mui/material'
import { useState, useEffect, createContext } from 'react'
import axios from 'axios'

import Navbar from './Navbar'
import AppFormNav from './AppFormNav'
import Loading from './Loading'

export const Context = createContext({});

export function PageLayout({ url, nav='normal', elem, gridElem, failureMsg }){
    const NavType = {
        'normal': <Navbar />,
        'form': <AppFormNav />
    };

    const [info, setInfo] = useState({});

    const fetchData = async() => {
        axios.get(url)
            .then((resp) => {
                if(resp.status === 200)
                    setInfo({status: 200, data: resp.data.data});
                else if(resp.status === 204) 
                    setInfo({ status: 204, data: resp.message });
            })
            .catch((err) => {
                setInfo({ status: 400, data: err.response.data.data })
                // console.log("Bad Request", err.resp.status);
            })
    };

    useEffect(() => {
        fetchData();
    }, []);

    var gridElement = <Loading />, element, message;
    if(info.status === 200){
        gridElement = gridElem;
        element = elem;
    }
    else if (info.status === 204){
        gridElement=<></>;
        message = failureMsg ? failureMsg : info.data;
    }

    return (
        <Context.Provider value={info?.data}>
            <Grid container sx={{ justifyContent: 'center' }}>
                <Grid item> { NavType[nav] } </Grid>
                <Grid item xs={12}> { element } </Grid>
                <Grid item xs={10}>
                    <Typography sx={{ justifyContent: "center", alignItems: "center" }}> {message} </Typography>
                </Grid>
                <Grid item xs={ 10 }> { gridElement } </Grid>
            </Grid>
        </Context.Provider>
    )
}

问题原因及解决方法

问题根源

  1. 图片设置了固定高度height: "50px",无法自适应容器
  2. ImageList使用固定rowHeight={250}和固定列数cols={4},响应性差
  3. ImageListItemBar设置了固定宽度width: '10vw',导致标题容器和图片容器尺寸不匹配,挤占空间

修改方案

  1. 优化ImageList配置:使用响应式列数,同时设置rowHeight='auto'让高度自适应内容
  2. 图片样式调整:设置objectFit: 'contain'保证图片完整不裁剪,同时让图片占满容器宽度和高度
  3. 移除固定尺寸限制:去掉ImageListItemBar的固定宽度,让它自适应父容器

修改后的Feed组件关键代码

<ImageList
    sx={{ pt: '10px' }}
    // 响应式列数:移动端2列,平板3列,桌面4列
    cols={{ xs: 2, sm: 3, md: 4 }}
    spacing={2}
    rowHeight="auto"
>
    {bookCatg.map((book) => (
        <ImageListItem key={book._id} sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
            <img
                onClick={() => navg(`/book/${book._id}`)}
                src={book.img}
                sx={{ 
                    width: '100%', 
                    height: 'auto', 
                    maxHeight: 200, // 可选:限制图片最大高度保持布局一致
                    objectFit: 'contain',
                    cursor: 'pointer'
                }}
                alt='Not Found'
                loading="lazy"
            />
            <ImageListItemBar
                onClick={() => navg(`/book/${book._id}`)}
                title={book.title}
                position="below"
                sx={{ 
                    width: '100%', 
                    textAlign: 'center',
                    cursor: 'pointer'
                }}
            />
        </ImageListItem>
    ))}
</ImageList>

说明

  • objectFit: 'contain'会让图片保持原始比例,完整显示在容器内,不会被裁剪
  • 响应式列数cols={{ xs:2, sm:3, md:4 }}确保在不同屏幕尺寸下都有合适的布局
  • rowHeight="auto"让每个列表项的高度根据图片和标题自动调整,避免固定高度导致的空间浪费或挤压

内容的提问来源于stack exchange,提问作者Fallen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 11:55:26