How to Implement a Card Stack

527 阅读3分钟

GUI-Challenges 系列之 Card-Stack

card-stack.apng

简介

卡片堆叠,是一个比较有意思的组件,并且其实现也很简洁。布局上主要通过 grid 实现。 卡片堆叠和旋转效果通过rotate 以及 translate 等变换函数实现,最后加上transition平滑过渡就可以得到一个好看的 card stack UI 组件啦 ^^

结构

由两部分组成

  1. 左边属性设置区域
<form>
  <fieldset>
    <legend>Transform Origin</legend>
    <input type="radio" name="transform-origin" id="origin-center" value="center" />
    <label for="origin-center">center</label>
    ...
    <input type="radio" name="transform-origin" id="top-right" value="top right" />
    <label for="radio-top-right">top right</label>
    <br />
  </fieldset>
  ...
</form>
  1. 右边卡片堆叠区域
<div class="card-stack">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>

实现 Card Stack

要实现卡片堆叠的效果,我们需要将卡片堆叠在一起,然后将卡片旋转一定角度,这里的旋转角度是通过--scalar这个 css 变量来控制的,所以我们需要在父元素.card-stack上设置这个变量的值

<div class="card-stack" style="--scalar: 10;">
  ...
</div>

堆叠

首先通过 grid 布局,将所有卡片堆叠在一起,所有的卡片都通过grid-area属性设置在同一个区域,这样卡片就会从上至下依次堆叠在一起

.card-stack {
  display: grid;
}

.card {
  grid-area: 1 / 1 / 2 / 2;

  width: 200px;
  aspect-ratio: 2 / 2.5;
}

旋转

左右两边的卡片分别向两边旋转了一定角度,左边卡片的旋转度数是负数,右边卡片旋转度数是正数,一共有五张卡片。两两相邻的卡片,旋转的相对度数是固定的,这个固定的度数通过 Gap 范围选择器来控制,将这个度数用变量 --scalar 表示,那么每张卡片的旋转度数就是

  1. 第一张卡片 2×(scalar)deg-2 \times (--scalar) \deg
  2. 第二张卡片 1×(scalar)deg-1 \times (--scalar) \deg
  3. 第三张卡片 0×(scalar)deg0 \times (--scalar) \deg
  4. 第四张卡片 1×(scalar)deg1 \times (--scalar) \deg
  5. 第五张卡片 2×(scalar)deg2 \times (--scalar) \deg

这里的--scalar 变量的大小由 Gap 范围选择器来控制,我们可以通过 JS 来动态的设置这个变量的值

const scalar = document.querySelector("#scalar")
const cardStack = document.querySelector(".card-stack")

scalar.addEventListener("input", (e) => {
  const value = e.target.value
  // 设置对应的 css 变量值
  cardStack.style.setProperty("--scalar", value)
})

由于每张卡片旋转的度数是不一样的,所以我们需要分别设置每张卡片的旋转度数,可以通过 nth-of-type 来选择对应卡片

.card:nth-of-type(1) {
  --r: calc(-2 * var(--scalar) * 1deg);
}

.card:nth-of-type(2) {
  --r: calc(-1 * var(--scalar) * 1deg);
}

.card:nth-of-type(3) {
  --r: calc(0 * var(--scalar) * 1deg);
}

.card:nth-of-type(4) {
  --r: calc(1 * var(--scalar) * 1deg);
}

.card:nth-of-type(5) {
  --r: calc(2 * var(--scalar) * 1deg);
}

然后在 .card上通过rotate函数进行旋转,应用上面设置的旋转度数

.card {
  transform: rotate(var(--r));
}

至此旋转卡片已经完成,并且可以设置卡片的旋转角度。需要注意的是,如果要设置旋转的中心点,需要用到transform-origin属性,这个属性可以接受 "top bottom center left right"关键词来指定变换的原点,也就是旋转的中心点

平移

在Hover卡片时,会有一个向上的平移效果,也是通过transform来实现。指定一个 css 变量--t来代表在 Y 轴上平移的距离。正常情况下不平移,hover的时候设置变量为-50px,也就是向上平移50px

.card {
  transform: rotate(var(--r)) translateY(var(--t), 0px);
}

.card:hover {
  --t: -50px;
}

这里的旋转→平移和平移→旋转的顺序不一样,最后产生的效果也是不同的

知识点

[1] grid-layout

[2] transform-origin

[3] transform

[4] css custom properties

Github 仓库

github.com/lxxorz/gui-…