魔术翻转卡:解决常见的尺寸问题
已发表: 2022-03-10您的下一位客户在介绍他们的项目时使用“互动”这个词的机会有多大? 以我的经验,答案是100% ,所以我一直在寻找强大的 CSS 技术来帮助我提供在讨论这个目标时出现的各种特性和效果。
我一次又一次地被要求实现的一点交互性是翻转卡片——内容块在悬停或点击时会翻转以显示其背面的内容。 这是一种鼓励有趣浏览的简洁效果,也是无需离开页面即可显示更多信息的另一种方式。 但标准方法在适应不同的卡片内容长度时存在问题。
在本教程中,我们将构建一个翻转卡片网格,它使用一些 CSS 基础知识来解决这个问题——transforms、flex 和 grid。 你需要熟悉这些,这将有助于很好地掌握 CSS 定位技术。 我们将涵盖:
- 翻转卡片通常如何使用绝对定位来实现;
- 绝对定位引入的尺寸问题; 和
- 覆盖内容自动调整大小的通用解决方案。
创建基本翻转卡
由于现代浏览器对 3D 变换的良好支持,创建基本的翻转卡片相对简单。 通常的方法是将正面和背面卡片面放在一个父容器中,并将背面绝对定位,使其可以匹配正面的大小。 在背面添加一个 x 轴变换以使其看起来反转,在悬停时向卡片本身添加另一个,我们就开始了。
.cards { display: grid; } .card { perspective: 40rem; } .card-body { transform-style: preserve-3d; transition: var(--time) transform; .card:hover & { transform: rotateX(-180deg); } } .card-front, .card-back { backface-visibility: hidden; } .card-back { position: absolute; top: 0; right: 0; bottom: 0; left: 0; transform: rotateX(-180deg); }
会出什么问题?
但是,我们的标准解决方案有一个大问题:当背面需要的空间比正面提供的空间大时,它就不起作用了。 给卡一个大的、固定的尺寸是一种解决方案,但这种方法也保证在某些时候对于某些屏幕尺寸集会失败。
设计组合自然具有外观整洁的框和完美契合的文本。 但是在开始开发时,很难获得适用于真实内容的页面和卡片布局。 当显示来自 CMS 的动态内容时,这是不可能的! 即使有字数或字符数限制,通常也没有能够在所有设备上可靠运行的解决方案。
我们应该始终努力创建能够容忍各种内容长度的布局实现。 但这并不容易! 我经常有机会回退到使用固定的大小和位置,无论是由于时间限制、浏览器支持不足、参考设计薄弱还是我自己的经验不足。
多年来,我了解到,在解决这些问题时,良好的迭代过程和与设计师的健康对话会大有帮助,而且您通常可以在中间的某个地方会面,以获得具有一定交互性的稳健布局。 但回到手头的任务——它可以完成吗?
外箱思考
事实上,可以根据正面和背面的内容来调整卡片的大小,而且并不像最初看起来那么难。 我们只需要有条不紊和坚持不懈!
约束问题
让我们首先列出我们的布局要求。 试图准确地写下你想要的东西可能看起来像是一件苦差事,但这是发现可以简化以解决问题的约束的好方法。 比方说:
- 我们希望看到一张或多张矩形卡片,排列成单列或多列网格;
- 我们希望卡片在悬停或点击时翻转以在背面显示第二组内容;
- 我们希望卡片始终足够大,以显示其所有正面和背面内容,无论内容长度或样式如何; 和
- 在多列的情况下,理想情况下,我们希望所有卡片的大小相同,以便行对齐。
考虑到这些要求,我们可以注意到一些可以简化问题的事情:
- 如果卡片要在网格中呈现,我们对它们的宽度有一个限制——也就是说,它们的宽度是视口或网格容器的函数,而不是它们自己的内容;
- 鉴于我们知道卡片的宽度(至少是其父卡片的百分比),我们已经解决了水平尺寸问题,我们只需要扩展卡片的高度以适应其正面或背面中较高的一个; 和
- 如果我们可以做到这一点并且每张卡片都是垂直大小的,我们可以使用 CSS Grid 的
grid-auto-rows
使所有卡片行都与最高卡片一样高。
弄清楚卡片技巧
那么,我们如何自行调整卡片大小? 现在我们已经简化了我们的问题,我们可以得到解决方案。
暂时忘记将内容放在其他内容之上的想法,并专注于我们的新要求:父母与其最高的孩子一样高。 这很简单! 使用列,我们可以使父级扩展到其最高子级的高度。 然后,我们只需要使用一点花招来让孩子们对齐:
- 将子项设置为与其父项相同的宽度
- 让第二个孩子向右溢出
- 将其向左转换回其正确位置
.cards { display: grid; } .card-body { display: flex; } .card-front, .card-back { min-width: 100%; mix-blend-mode: multiply; // Preview both faces } .card-back { transform: translate(-100%, 0); }
如果这种方法看起来很明显,请放心,我花了很多时间研究了一些非常糟糕的想法,然后才想到它。 起初,我计划在正面打印一个隐藏的重复版本的背面文本,以将卡片扩大到正确的尺寸。 当我确实想到使用列溢出时,我最初是使用overflow:hidden
裁剪右侧列,并仅在悬停开始的最后一刻对其进行转换,因为我还没有意识到我可以保持它的转换从头开始并使用另一种方法(例如opacity
或backface-visibility
)根据需要打开和关闭它。
换句话说,显而易见的解决方案是努力工作的结果! 如果您觉得自己已经为一个布局问题头疼了好几个小时,重要的是退后一步,决定您是否明智地花费了客户的时间:是否建议他们改变设计,以及是否当压力消失时,在自己的时间里寻求解决方案作为学习练习。 但是当你想出简单的方法时,永远不要因为花了很长时间而感到愚蠢。 现在,让我们回顾一下我们的完整解决方案。
.cards { display: grid; } .card { perspective: 40rem; } .card-body { display: flex; transform-style: preserve-3d; transition: var(--time) transform; .card:hover & { transform: rotateX(-180deg); } } .card-front, .card-back { backface-visibility: hidden; min-width: 100%; } .card-back { transform: rotateX(-180deg) translate(-100%, 0); }
有什么注意事项吗?
该解决方案通常运行良好,只需记住一些小警告:
- 卡片必须出现在网格布局或其他宽度不依赖于内容的上下文中。
- 卡片需要某种内容包装器(我们的
card-body
),以便悬停区域在动画期间不会改变。 如果卡片本身是动画的,您会看到一些小故障,因为动画会快速停止并重新启动。 - 背景和盒子阴影等样式最好直接放在正面和背面,因为卡片本身的任何效果都不会被动画化。 注意卡体上的样式,例如盒子阴影,因为它们自然会被颠倒过来。
- 卡片的正面和背面需要它们的
box-sizing
属性设置为border-box
,如果它们有自己的填充,由于它们的min-width
要求,否则它们会溢出。 - Safari 仍然需要
-webkit-backface-visibility
,以供应商前缀的形式。
添加一些波兰语
现在我们已经解决了难题,让我们看看我们可以进行的一些调整,以使整个交互尽可能顺利地工作。
首先,检查翻转时卡片是否重叠。 这将取决于您是否使用多列、列间距的宽度、翻转的方向以及卡片的透视值,但这很可能会发生。 您可以增加动画的持续时间以更清楚地看到事物。 悬停时,悬停的卡片在其后面的邻居下方翻转看起来不自然,因此我们需要使用z-index
将其放在顶部。 很简单,但要小心! 在恢复z-index
之前,我们需要等到传出动画完成。 输入transition-delay
:
.card { transition: z-index; transition-delay: var(--time); z-index: 0; &:hover { transition-delay: 0s; z-index: 1; } }
接下来,考虑为卡片创建一个活动状态。 我通常会尝试将这样的卡片链接到相关的地方——即使设计师没有指定——因为像这样具有悬停效果的元素感觉非常容易点击,所以为尝试运气的读者提供一个目的地是很好的。 我喜欢一个简短而微妙的比例变换,因为无论动画的后半部分是否被目标页面的加载切断,它都能很好地工作(我希望浏览器在导航之前干净地完成飞行中的动画,不过我敢肯定,在实践中实施起来比听起来要困难得多)。
这也是一个很好的机会来思考我们卡片背面内容的可访问性。 我们的标记简洁且有序,因此我们涵盖了屏幕阅读器和其他忽略样式的用例,但是键盘用户呢? 如果我们要让卡片自己锚定,它们将在键盘用户通过页面标签时获得焦点。 让我们重用卡片的悬停状态作为焦点状态,在键盘浏览过程中,后面的内容自然会出现。
.card { transition: z-index, transform calc(var(--time) / 4); transition-delay: var(--time), 0s; z-index: 0; &:hover { transition-delay: 0s; z-index: 1; } &:active { transform: scale(0.975); } } .card-body { .card:hover &, .card:focus & { transform: rotateX(-180deg); } }
最后,不要忘记现在卡片会自动缩放以适应其内容,您可以在前后容器内使用几乎任何您喜欢的对齐和间距技术。 使用 flex 对齐来居中标题,添加填充,甚至在卡片内放置另一个网格。 这就是随内容扩展的良好布局解决方案的美妙之处——减少了孩子与父母的耦合,以及允许您一次专注于一件事的模块化。
.card-front, .card-back { display: flex; align-items: center; background-color: white; box-shadow: 0 5px 10px black; border-radius: 0.25rem; padding: 1.5rem; }
包起来
我希望你发现这个 CSS 技术很有用! 为什么不尝试一些动画变化,例如缩放效果或简单的交叉淡入淡出? 该技术也不限于卡的外形尺寸。 它可以在垂直尺寸的责任落在一个以上元素的任何地方使用。 想象一个杂志网站,其中包含带有重叠标题的大照片——您可以使用它来容纳具有高纵横比的图像和长动态文本。
最重要的是,请记住花一些时间认真思考是否有一种方法可以实现看起来好像只能在固定尺寸和位置下工作的设计的好处。 通常,不管问题一开始看起来多么棘手,写下你的所有需求,留出一些时间来创建一个最小的测试用例,并有条不紊地逐步完成它总是最好的选择。