如何基于传入图片为MUI Modal组件动态设置宽高
问题场景
需求如下:
- 点击
open modal按钮打开弹窗 - 打开的弹窗完整展示图片,无margin、padding,仅展示图片本身
需要获取不同传入图片对应的width和height属性,目前已实现图片宽高获取逻辑:
function getMeta(url) { const img = new Image(); img.addEventListener("load", function () { console.log(this.naturalWidth + " " + this.naturalHeight); setWidth(this.naturalWidth); setHeight(this.naturalHeight); }); img.src = url; }
调用方式:
getMeta("https://cdn.pixabay.com/photo/2022/03/08/07/08/water-7055153_1280.jpg");
当前问题是无法将获取到的width和height动态设置到Box组件上,初始静态样式代码如下:
const style = { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: 500, // 此处需要动态设置 height: 500, // 此处需要动态设置 bgcolor: "background.paper", boxShadow: 24, p: 4 }; export default function BasicModal() { const [open, setOpen] = React.useState(false); const handleOpen = () => { setOpen(true); getMeta( "https://cdn.pixabay.com/photo/2022/03/08/07/08/water-7055153_1280.jpg" ); }; const handleClose = () => setOpen(false); const [width, setWidth] = React.useState(0); console.log("width", width); const [height, setHeight] = React.useState(0); console.log("height", height); function getMeta(url) { const img = new Image(); img.addEventListener("load", function () { console.log(this.naturalWidth + " " + this.naturalHeight); setWidth(this.naturalWidth); setHeight(this.naturalHeight); }); img.src = url; } return ( <div> <Button onClick={handleOpen}>Open modal</Button> <Modal open={open} onClose={handleClose} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description" > <Box sx={style}> <div>This is an image</div> </Box> </Modal> </div> ); }
尝试方案1
直接使用setState存储width、height值后赋值给style对象:
const style = { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: width, // 替换为state值 height: height, // 替换为state值 bgcolor: "background.paper", boxShadow: 24, p: 4 }; export default function BasicModal() { const [open, setOpen] = React.useState(false); const handleOpen = () => { setOpen(true); getMeta( "https://cdn.pixabay.com/photo/2022/03/08/07/08/water-7055153_1280.jpg" ); }; const [width, setWidth] = React.useState(0); const [height, setHeight] = React.useState(0); function getMeta(url) { const img = new Image(); img.addEventListener("load", function () { console.log(this.naturalWidth + " " + this.naturalHeight); setWidth(this.naturalWidth); // 设置宽度 setHeight(this.naturalHeight); // 设置高度 }); img.src = url; } ...
该方案无法生效。
尝试方案2
使用makeStyles实现动态样式可以部分生效,但需要维护两份样式变量:
const useStyles = makeStyles({ root: (props) => ({ position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: props.width, // 此处需要动态设置 height: props.height, // 此处需要动态设置 bgcolor: "background.paper", boxShadow: 24 }) }); const style = { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: 500, // 此处需要动态设置 height: 500, // 此处需要动态设置 bgcolor: "background.paper", boxShadow: 24 }; . . . return ( <div> <Button onClick={handleOpen}>Open modal</Button> <Modal open={open} onClose={handleClose} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description" > <Box sx={style} // 静态样式 className={classes.root} // 动态样式 component="img" src="https://cdn.pixabay.com/photo/2022/06/13/15/36/grain-7260250__340.jpg" ></Box> </Modal> </div> );
该方案下必须同时引入useStyles生成的类和静态style对象,移除任意一个都会导致样式异常。
解决方案
方案1失效原因
style对象定义在组件函数外部,组件内部声明的width、height state属于组件作用域,外部style对象无法访问这两个变量,因此赋值不生效。
正确实现代码
MUI的sx属性原生支持动态读取组件内state值,无需引入makeStyles,也不需要维护两份样式,核心修改点如下:
- 将style对象移入组件内部,使其可以直接访问width、height state
- 移除原样式中的
p:4内边距配置,改为p:0 - 直接将Box设置为
img组件,不需要额外嵌套div包裹图片,避免多余边距
完整代码:
export default function BasicModal() { const [open, setOpen] = React.useState(false); const [width, setWidth] = React.useState(0); const [height, setHeight] = React.useState(0); const imgUrl = "https://cdn.pixabay.com/photo/2022/03/08/07/08/water-7055153_1280.jpg"; function getMeta(url) { const img = new Image(); img.addEventListener("load", function () { setWidth(this.naturalWidth); setHeight(this.naturalHeight); }); img.src = url; } const handleOpen = () => { setOpen(true); getMeta(imgUrl); }; const handleClose = () => setOpen(false); // 样式定义在组件内部,可直接读取state const style = { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: width, height: height, bgcolor: "background.paper", boxShadow: 24, p: 0 // 移除内边距 }; return ( <div> <Button onClick={handleOpen}>Open modal</Button> <Modal open={open} onClose={handleClose} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description" > <Box component="img" src={imgUrl} sx={style} /> </Modal> </div> ); }
可选优化:如果担心图片加载前弹窗宽高为0出现闪烁,可以在width和height都大于0时再渲染Box内的图片,或者增加简单的加载提示。
内容的提问来源于stack exchange,提问作者Happy1234
相关产品推荐
相关产品推荐

