html - 使用动画属性模拟对象拟合
问题描述
我正在尝试制作一个需要从缩略图开始到全屏图像的动画的灯箱。
在缩略图状态下,图像将保存在具有固定纵横比(例如 3:2)的容器内。图库上的每张图片都必须适合这个分配的空间,覆盖它的整个表面(通常是object-fit: cover
)。
在全屏状态下,容器将扩展为 100vw:100vh,并且图像可以扩展(甚至收缩,对于一些异常高的图像的情况)以适应 ( object-fit: contain
)。
必须始终保持其<img>
纵横比不变(在两种状态下以及在动画期间)。
我可以为容器的width
and设置动画height
(它们具有绝对值)。
但是object-fit
动画是离散的。
这是一个同时编码状态和容器动画的笔:https ://codepen.io/rslemos/pen/PoNMymR (要查看动画,应该从元素中删除expanded
类)。container
是否有任何 CSS 技巧可以object-fit
使用线性属性进行模拟?
编辑
在动画过程中,图像本身——我的意思是,被位图覆盖的表面,而不是<img>
元素,它可能比<img>
任何方向都大或小——应该从它的初始大小变形到最终大小。
我还编辑了笔以显示容器纵横比(宽度:高度)< 1 时的初始状态(使用相同的图像,纵横比 > 1)。
解决方案
解决方案看起来很复杂但不是很复杂,它只是一堆数学......
首先,您需要弄清楚图像的比例与您想要将其放入的框(div 或整个屏幕)的比例之间的差异。
然后,您需要根据比率差异(参见步骤 1)以及是否要覆盖(最大化最小尺寸)或包含(最小化最大尺寸)来确定尺寸是否受高度或宽度的约束
最后,您需要在不同的框(div 或整个屏幕)之间切换。这里我没有考虑 div 的位置(总是左上角),因为我希望这个演示的数学保持简单。
const img = document.querySelector('img')
const div = document.querySelector('.box')
// initialize some stuff
const {offsetHeight: boxHeight, offsetWidth: boxWidth} = div
let naturalHeight, naturalWidth
img.addEventListener('load', () => {
naturalHeight = img.naturalHeight
naturalWidth = img.naturalWidth
scaleToBox()
requestAnimationFrame(
() => img.classList.remove('initial')
)
})
function scaleToBox() {
let scale
let x = 0, y = 0
// is image ratio wider than container ratio
if (boxWidth / boxHeight < naturalWidth / naturalHeight) {
// width is the largest dimension, maximize height to cover
scale = boxHeight / naturalHeight
// center along x axis
x = -(naturalWidth * scale - boxWidth) / 2 / scale
} else {
// height is the largest dimension, maximize width to cover
scale = boxWidth / naturalWidth
// center along y axis
y = -(naturalHeight * scale - boxHeight) / 2 / scale
}
img.style.setProperty('transform', `scale(${scale}) translate(${x}px, ${y}px)`)
}
function scaleToScreen() {
let scale
let x = 0, y = 0
// is image ratio wider than container ratio
if (innerWidth / innerHeight < naturalWidth / naturalHeight) {
// width is the largest dimension, maximize width to contain
scale = innerWidth / naturalWidth
// center along y axis
y = -(naturalHeight * scale - innerHeight) / 2 / scale
} else {
// height is the largest dimension, maximize height to contain
scale = innerHeight / naturalHeight
// center along x axis
x = -(naturalWidth * scale - innerWidth) / 2 / scale
}
img.style.setProperty('transform', `scale(${scale}) translate(${x}px, ${y}px)`)
}
let state
div.addEventListener('click', () => {
if (!naturalHeight || !naturalWidth) {
return
}
div.classList.toggle('full')
if (!state) {
scaleToScreen()
} else {
scaleToBox()
}
state = !state
})
body {
margin: 0;
}
.box {
position: relative;
height: 200px;
width: 300px;
transition: clip-path 1s;
clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
}
.box.full {
clip-path: polygon(0 0, 100vw 0, 100vw 100vh, 0 100vh);
}
img {
position: absolute;
top: 0;
left: 0;
transform-origin: 0 0;
transition: transform 1s;
}
img.initial {
height: 100%;
width: 100%;
object-fit: cover;
transition-duration: 0s;
}
<div class='box'>
<img class='initial' src="https://picsum.photos/1000/400"/>
</div>
即使父 div 不完全位于左上角,您也可以通过动画转换图像来改进此演示(我不想混淆此演示的工作原理)。
推荐阅读
- regex - Perl 一个使用正则表达式替换的衬里
- sql - 将 LIKE CASE WHEN 与子查询一起使用时出现 SQL 错误
- javascript - 从 json 制作 javascript 中的字典列表
- react-native - React Native 重构与 Flutter 性能比较
- java - 使用新的 Java 14 Record 功能,是否可以为同一个 Record 创建多个构造函数?
- c++ - 带有 HWND 的 Visual Studio C26462 代码分析警告
- javascript - 将变量传递给函数时,我得到“无效参数”,但是当我硬编码时,它可以在 Apps 脚本中使用
- magento2 - TypeError:无法读取未定义的属性“addMethod”
- javascript - Expo FileSystem.readAsStringAsync 返回错误的 base64
- flutter - 如何使用带有 Flutter 的 BloC 访问登录时获得的身份验证令牌