写点什么

图片与标题的 Ken Burns 动效

用户头像
寇云
关注
发布于: 2020 年 05 月 29 日
图片与标题的Ken Burns动效

原文地址: 《Animated Image Gallery Captions with Bonus Ken Burns Effect"

我从事前端开发13年有余,一直致力于研究现代CSS解决方案,这是我该系列文章的第6篇

本文主要讨论:

  • object-fit用于响应式图像缩放

  • CSS Grid "hack" 升级 position absolute方式

  • CSS transforms 动画效果

如果你曾经使用jQueryposition: absolute来解决图像标题文字动画的问题,或者通过处理元素的宽高动画来实现,如果你想在响应式的图片上实现,这次的升级非常适合你。

有关object-fit的介绍请看这篇文章(系列02):

https://dev.to/5t3ph/css-only-full-width-responsive-images-2-ways-2njm

基础HTML

下面是我们基础的HTML,它是一个ul,其中每个li"card"包含了图片和标题。

<ul class="gallery">
<li class="gallery-card">
<img class="gallery-card__img" alt="" src="https://picsum.photos/400" />
<p class="gallery-card__caption">Candy canes ice cream
</p>
</li>
<li class="gallery-card">
<img class="gallery-card__img" alt="" src="https://picsum.photos/600" />
<p class="gallery-card__caption">Ice cream
biscuit
</p>
</li>
<li class="gallery-card">
<img class="gallery-card__img" alt="" src="https://picsum.photos/650" />
<p class="gallery-card__caption">Cream
biscuit marzipan
</p>
</li>
</ul>

以上是原作者的代码,笔者建议图片使用 <figure> 和 <figcaption> 标签 可附标题内容元素

我使用不同大小的图片,既是为了展示object-fit如何适配它的容器,也是为了减少picsum服务中重复图片。

基础样式

由于我们使用了列表样式,所以我们需要删除默认的列表样式,同时我们还要将列表设置为grid容器。

.gallery {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(20ch, 1fr));
width: 100%;
max-width: calc(40ch * 3 + 6rem);
}


这样初始的样式就完成了,首先将我们的列表项放在一行中,先不设置图片的样式





card 和 Image 样式

如果你和我一样,并且在过去几年中尝试过这样做,你可能会把鼠标滚过房间,试图找出为什么position:absolute不能很好地处理jQuery动画。

CSS GridCSS transforms在这里来拯救你了! 🎉

我们将card设置grid样式,并设置一个高度。而我们仍然会使用overflow: hidden, 来确保标题最初隐藏image内容中。

.gallery-card {
display: grid;
overflow: hidden;
height: 40vh;

// Purely for visual appeal
border: 1px solid #c9c9c9;
border-radius: 7px;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.15);
}


接下来,我们在image上设置object-fitwidth: 100%height: 40vh,使之与card的尺寸相适应。object-fit: cover的神奇之处在于不会使图片变形。

.gallery-card__img {
object-fit: cover;
width: 100%;
height: 40vh;

// 去除了图像底部多余的默认间距
display: block;
}






图片标题定位

现在,根据Grid布局的作用,标题已经自然地流到了图像下方,假定它应该是在自己的 "单元格"里,而默认的grid items是沿着Y轴流动的。





利用 CSS transforms 属性来设置标题的初始位置:

.gallery-card__caption {
margin: 0;
transform: translateY(100%);
}


由于我们使用的是p标签,所以我们也去掉了margin,以防止它对标题的高度产生不利影响。translate的值为100%,会使元素相对于它所放置的轴线100%移动。所以,translateY(100%)会有效地将标题 "向下"移出初始视图。

添加标题动画

我们在hover时触发动画,我们希望的看到的效果是可以顺利的触发显示动画,然后在返回到外面初始状态,有两个属性可以帮助实现这个目标:transition 和 will-change

.gallery-card__caption {
// ... existing styles
transition: transform 800ms ease-in;
will-change: transform;

// other
text-align: right;
padding: 0.25em 0.5em;
border-radius: 4px 0 0 0;
background-color: rgba(#fff, 0.87);
}


我们定义了一个800ms过渡时间,并且设置ease-in过渡效果,然后我们使用will-change属性作为一个额外的提示,如果浏览器能够对该属性进行优化的话,我们会对变换属性进行修改,以实现更平滑的过渡。

:hover样式实际上将被放置在.gallery-card上,因为它是包含的元素,所以我们将添加一个 transform属性,用来将标题还原到初始状态

.gallery-card:hover {
.gallery-card__caption {
transform: translateY(0);
}
}


现在我们标题有了基本动画样式:





image Ken Burns 动画效果

你可能不知道这个名字,但你看到过这样的效果:慢速、流畅的平移和变焦组合的静止画面,由于被纪录片制作人肯-伯恩斯(Ken Burns)推广而得名。

利用我们已经介绍过的transitiontranform属性的原理,我们可以在.gallery-card__img上再次将它们结合起来,在悬停时也可以添加这种效果。

我们添加了额外的transform属性,将初始图像按比例放大1.2倍,以确保图像可以平移,如果不这样设置的话,一旦我们平移图片将无法覆盖原来的内容。

.gallery-card__img {
transform: scale(1.2) translate(5%, 0);
transition: transform 1200ms ease-in;
will-change: transform;
}


此外,我们首先设置一个5%的x偏移量,也就是将图像向右拉5%。这被应用到图片的缩放大小上。

接下来,我们在.gallery-card中添加了:hover transform,除了在x轴上向左拉回-1%,在y轴上向右拉回-3%之外,来实现缩放效果。

.gallery-card:hover {
.gallery-card__img {
transform: scale(1.3) translate(-1%, -3%);
}
}


大家可以根据自己的情况来设置值的大小

还有一点,就是我们把过渡时长设置成了400ms的差值。我们可以把这个值作为标题的延时来添加。要注意的是,这个延迟适用于悬停过渡之前的过渡,而在过渡结束的时候,则是在悬停的时候。我个人很喜欢这种效果,因为这意味着两个地方的动画都会一起结束。

.gallery-card__caption {
// 更新,添加延迟作为第一个值
transition: 400ms transform 800ms ease-in;
}


不要忘了 :focus

Hover对于使用鼠标的人来说是没问题,但是对于那些因为各种原因主要使用键盘导航的人来说呢?

li元素本身并不是一个可聚焦的元素,所以仅仅添加:focus样式并不能改变行为。

我们首先需要给每个li元素添加tabindex="0",这将使它们成为可聚焦元素。

<li class="gallery-card" tabindex="0">


你可以通过tab来测试触发动画效果

我们会删除tabindex="0"而是用 :focus达到同样的效果

.gallery-card:focus {
outline: none;
}

.gallery-card:hover,
.gallery-card:focus {
// ...
}


Optional

这种风格还有一种效果--在图像的边框上有柔和的黑色渐变,我们可以使用box-shadowinset的属性完成此操作。box-shadow无法直接作用于图像元素,因此我们将其应用于.gallery-card:after伪元素上

.gallery-card:after {
content: "";
grid-area: card;
width: 100%;
height: 100%;
box-shadow: inset 0 0 2rem 1rem rgba(black, 0.5);
z-index: 1;
}


设置 grid-area: card;来确保它的高度和宽度占据整个卡片的高度和宽度,此外还有一个z-index确保图片在容器上方。

这样做的副作用是,它覆盖了标题,但我们要多加一个z-index: 2;来提高标题的优先级。

.gallery-card__caption {
z-index: 2;
}


codepen:

https://codepen.io/5t3ph/pen/xxwEXmE



发布于: 2020 年 05 月 29 日阅读数: 46
用户头像

寇云

关注

前端工程师,远程工作者 2012.06.12 加入

还未添加个人简介

评论

发布
暂无评论
图片与标题的Ken Burns动效