React中使用useRef获取img高度动态设置SVG高度的方法
实现方案
你判断的方向没错,用useRef配合尺寸监听就能实现,核心逻辑是:
- 用ref绑定图片元素,避免直接操作DOM的时序问题
- 监听图片加载完成事件、图片元素尺寸变化事件,实时计算图片高度的90%
- 把计算出的高度传给SVG组件,利用SVG自带的
viewBox属性自动适配比例,不会变形 - 父容器设置相对定位,SVG层设置绝对定位叠在图片上层,不占用文档流
具体代码修改
1. 父组件Polska修改
import { useState, useRef, useEffect } from 'react' import { Row, Col, Tooltip } from 'antd' // 按你项目实际导入路径调整 import classNames from 'classnames' import Wisla from './Wisla' export const Polska = () => { const [riverVisible, setRiverVisible] = useState(false) // 绑定轮廓图片的ref const contourImgRef = useRef(null) // 存储计算后的SVG高度 const [wislaHeight, setWislaHeight] = useState(0) useEffect(() => { const imgNode = contourImgRef.current if (!imgNode) return // 尺寸同步逻辑 const syncSvgSize = () => { setWislaHeight(imgNode.clientHeight * 0.9) } // 图片加载完成后执行首次计算 if (imgNode.complete) { syncSvgSize() } else { imgNode.onload = syncSvgSize } // 监听图片尺寸变化(响应式断点切换、窗口缩放、容器变动都会触发) const observer = new ResizeObserver(syncSvgSize) observer.observe(imgNode) // 组件卸载时清理监听,避免内存泄漏 return () => { observer.disconnect() imgNode.onload = null } }, []) return ( <div> <Row> <Col className='list-of-landscapes' sm={12} md={2} > <ul> <li> <button onClick={() => setRiverVisible(!riverVisible)}>rzeki</button> </li> <li> <button>jeziora</button> </li> </ul> </Col> {/* 父容器设相对定位,作为SVG绝对定位的参照 */} <Col sm={12} md={10} className="rivers-box" style={{ position: 'relative' }}> <img ref={contourImgRef} className='contour' src="images/polska-kontur.jpg" alt='polska' style={{ width: '100%', display: 'block' }} /> {/* SVG层绝对定位,居中叠在图片上方 */} <Col className='rivers' style={{ position: 'absolute', top: '5%', left: 0, width: '100%', display: 'flex', justifyContent: 'center' }} > <Wisla riverVisible={riverVisible} height={wislaHeight} /> </Col> </Col> </Row> </div> ) } export default Polska
2. 子组件Wisla修改
只需要接收父组件传入的高度,直接绑定到SVG元素上即可:
export const Wisla = ({riverVisible, height}) => { return ( <svg id='wisla-id' className={classNames('wisla', riverVisible && "visible")} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 312.47 625.89" style={{ height: height }} > <g id="Warstwa_2" dataName="Warstwa 2"> <g id="Warstwa_2-2" dataName="Warstwa 2"> <Tooltip title='wisla wisla wisla wisla wisla wisla wisla wisla' followCursor> <path className="cls-wisla" d="M67.06,0V22.72l-6,6.14v52.8l-6.6,8.6v16l-6,11.67-13.8,6.75L4.06,162.1l45.6,19,9,17.19,15,19,28.2,10.43,48, 23.34,4.8,5.52h27l10.8-5.52,17.4,2.45,25.8,23.33,15,43.16,10.8,7.19,5.4,21.49,33,16,9.6,25.78-12,8.6,6.6,73.68-21, 25.79-31.2,19.65-21.6,8-7.2,8.59h-9l-13.2,14.74h-15.6l-9,8.6h-6.6l-2.4,7.36h-30.6l-5.4,6.32-12.6, 2.89-13.2-8.59-13.2-2.46-9.6.62-7.2,10.43-24,6.14.6,12.28,2.4,22.11,12.6,8.59"> </path> </Tooltip> </g> </g> </svg> ) }
注意事项
- 不要用
document.getElementById获取DOM,React中用useRef绑定元素可以避免渲染时序、id重复导致的取值错误 - 必须监听图片的
onload事件,否则图片未加载完成时取到的高度为0,会导致SVG不显示 - 用
ResizeObserver监听元素尺寸比监听window.resize更可靠,栅格断点切换、父容器大小变动等场景都能触发同步 - 因为SVG已经设置了
viewBox属性,只需要指定高度,宽度会自动按原始比例缩放,不会出现拉伸变形 - 代码里的内联样式可以迁移到对应的CSS文件中,不影响功能
内容的提问来源于stack exchange,提问作者Robo Krzw
相关产品推荐
相关产品推荐

