使用object-fit: contain时如何让图片悬停动画仅作用于图片周边区域
解决object-fit:contain下悬停动画仅作用于图片周边的问题
这个问题我碰到过!核心原因是:当你给<img>加上object-fit: contain后,元素本身的盒子是占满了父容器的高度和84%宽度,但实际图片内容是在这个盒子里居中缩放的——而你的动画span是基于<img>的盒子尺寸来定位的,自然就覆盖了整个区域,而不是只围绕图片转。
要解决这个问题,关键是让动画元素的容器尺寸匹配实际显示的图片内容的尺寸,而不是<img>元素的盒子尺寸。下面给你两种实用方案:
方案一:已知图片宽高比时用纯CSS实现
如果你的图片都是固定宽高比(比如示例里的图片接近4:3),可以用aspect-ratio属性创建一个和图片比例一致的容器,把图片和动画元素都放在这个容器里,动画就能精准匹配图片的显示区域:
修改后的CSS代码
body { height: 100%; margin: 0; } .container { max-width: 600px; position: relative; text-align: center; width: 100%; height: 100%; min-height: 700px; /* 让容器内部元素垂直居中 */ display: flex; align-items: center; justify-content: center; } .product { /* 设置和图片一致的宽高比,示例用4/3 */ aspect-ratio: 4/3; width: 84%; position: relative; } .content { background: white; width: 100%; height: 100%; object-fit: contain; z-index: 5000; } .product:hover .effect-1, .product:hover .effect-2 { display: block; } .effect-1, .effect-2 { border-radius: 30%; display: none; mix-blend-mode: multiply; height: 100%; opacity: 1; position: absolute; width: 100%; z-index: 3000; } .effect-1 { animation: rotate 1.8s linear infinite; background: cyan; } .effect-2 { animation: rotate 1.2s linear reverse infinite; background: #e7a9ff; } @keyframes rotate { 0% { top: 0; left: 0; } 25% { top: 8%; left: -8%; } 50% { top: 16%; left: 0; } 75% { top: 8%; left: 8%; } 100% { top: 0; left: 0; } }
修改后的HTML代码
<body> <div class="container"> <div class="product"> <img class="content" src="http://www.petsworld.in/blog/wp-content/uploads/2014/09/Golden-Retriever-Puppies-in-basket.jpg"> <span class="effect-1"></span> <span class="effect-2"></span> </div> </div> </body>
说明:
.product容器的aspect-ratio和图片一致,刚好匹配图片显示区域- 图片在容器内用
object-fit: contain居中,动画元素基于这个容器定位,完美贴合图片周边
方案二:未知图片宽高比时用JS动态计算
如果你的图片比例不固定,就需要用JS动态计算图片实际显示的尺寸,再调整动画元素的大小和位置:
修改后的CSS代码
body { height: 100%; margin: 0; } .container { max-width: 600px; position: relative; text-align: center; width: 100%; height: 100%; min-height: 700px; display: flex; align-items: center; justify-content: center; } .product { width: 84%; position: relative; } .content { background: white; width: 100%; height: 100%; object-fit: contain; z-index: 5000; } .effect-1, .effect-2 { border-radius: 30%; display: none; mix-blend-mode: multiply; opacity: 1; position: absolute; z-index: 3000; } .product:hover .effect-1, .product:hover .effect-2 { display: block; } .effect-1 { animation: rotate 1.8s linear infinite; background: cyan; } .effect-2 { animation: rotate 1.2s linear reverse infinite; background: #e7a9ff; } @keyframes rotate { 0% { top: 0; left: 0; } 25% { top: 8%; left: -8%; } 50% { top: 16%; left: 0; } 75% { top: 8%; left: 8%; } 100% { top: 0; left: 0; } }
修改后的HTML代码(含JS)
<body> <div class="container"> <div class="product"> <img class="content" src="http://www.petsworld.in/blog/wp-content/uploads/2014/09/Golden-Retriever-Puppies-in-basket.jpg" onload="adjustEffects(this)"> <span class="effect-1"></span> <span class="effect-2"></span> </div> </div> <script> function adjustEffects(img) { const product = img.parentElement; const effects = product.querySelectorAll('.effect-1, .effect-2'); // 计算图片原始比例和容器比例 const imgRatio = img.naturalWidth / img.naturalHeight; const containerRatio = product.clientWidth / product.clientHeight; let imgDisplayWidth, imgDisplayHeight, offsetTop, offsetLeft; if (imgRatio > containerRatio) { // 图片更宽,按容器宽度适配 imgDisplayWidth = product.clientWidth; imgDisplayHeight = product.clientWidth / imgRatio; offsetTop = (product.clientHeight - imgDisplayHeight) / 2; offsetLeft = 0; } else { // 图片更高,按容器高度适配 imgDisplayHeight = product.clientHeight; imgDisplayWidth = product.clientHeight * imgRatio; offsetLeft = (product.clientWidth - imgDisplayWidth) / 2; offsetTop = 0; } // 同步动画元素的尺寸和位置 effects.forEach(effect => { effect.style.width = `${imgDisplayWidth}px`; effect.style.height = `${imgDisplayHeight}px`; effect.style.top = `${offsetTop}px`; effect.style.left = `${offsetLeft}px`; }); } // 窗口大小变化时重新计算 window.addEventListener('resize', () => { const imgs = document.querySelectorAll('.content'); imgs.forEach(img => adjustEffects(img)); }); </script> </body>
说明:
- 图片加载完成后,通过
naturalWidth/naturalHeight获取原始比例,对比容器比例计算出图片实际显示的尺寸和偏移量 - 动画元素的尺寸和位置会匹配图片的实际显示区域,窗口大小变化时也会自动更新
这样两种方案都能保证图片用object-fit: contain垂直居中的同时,悬停动画只作用在图片的周边区域啦。
内容的提问来源于stack exchange,提问作者Mitchell Andrew Abbott




