CSS `Geometry Functions` (`round()`, `mod()`) (提案) 在布局中的应用

各位前端的靓仔靓女们,早上好/下午好/晚上好!今天咱们来聊聊CSS里即将加入的两位新朋友:round()mod()。别听到函数就害怕,这俩家伙其实挺接地气的,能帮咱们在布局上耍出不少新花样。

第一部分:round() – 四舍五入界的扛把子

round() 函数,顾名思义,就是用来四舍五入的。但是CSS的round()可比你想象的要强大,它不只是简单的取整,而是可以指定精度和舍入策略。

1. 基本语法

round(value, rounding-increment)

  • value: 需要进行舍入的数值。
  • rounding-increment: 舍入的增量,也就是精度。

2. 舍入策略(Rounding Strategies)

CSS的round()默认的舍入策略是nearest,也就是最接近的原则。除此之外,还有其他的策略,不过目前浏览器支持情况还不太好,咱们先了解一下,以后说不定就能用上了。

  • nearest: 舍入到最接近的倍数。
  • up: 向上舍入到最接近的倍数。
  • down: 向下舍入到最接近的倍数。
  • to-zero: 向零方向舍入到最接近的倍数。
  • from-zero: 远离零方向舍入到最接近的倍数。

这些策略可以作为第三个参数传递给round()函数,比如 round(12.6, 5, up)

3. 实战演练

  • 等分网格:

    假设我们要创建一个平均分成5列的网格,但是容器的宽度不是5的倍数,怎么办?用round()

    .container {
      width: 307px; /* 容器宽度不是5的倍数 */
      display: flex;
    }
    
    .item {
      width: round(100%/5, 1px); /* 将每列的宽度四舍五入到最接近的1px */
      height: 50px;
      background-color: lightblue;
      border: 1px solid #ccc;
    }

    这样,即使容器宽度不是5的倍数,也能保证每列的宽度尽量接近平均值,并且总宽度不超过容器宽度。

  • 响应式间距:

    根据屏幕宽度调整元素之间的间距,但间距必须是某个值的倍数。

    .container {
      display: flex;
      justify-content: space-between;
      width: 100%;
    }
    
    .item {
      width: 20%;
      height: 100px;
      background-color: lightgreen;
    }
    
    .container {
      margin-left: calc(round(100vw * 0.05, 10px) * -1);
      margin-right: calc(round(100vw * 0.05, 10px) * -1);
    }
    
    .item {
      margin-left: round(100vw * 0.05, 10px);
      margin-right: round(100vw * 0.05, 10px);
    }

    这段代码的意思是,间距是屏幕宽度的5%,但要四舍五入到最接近的10px。这样可以避免出现非常细小的间距,让布局更加整齐。

  • 控制动画步长:

    让动画的步长是某个值的倍数,避免动画出现抖动。

    .element {
      width: 100px;
      height: 100px;
      background-color: red;
      position: relative;
      animation: move 5s linear infinite;
    }
    
    @keyframes move {
      0% {
        left: 0;
      }
      100% {
        left: round(300px, 10px); /* 移动300px,步长为10px */
      }
    }

    这样,元素会以10px为单位移动,避免出现小数像素导致的抖动。

4. round()的优势

  • 精确控制: 可以精确控制数值的精度,避免出现小数像素问题。
  • 灵活性: 可以根据不同的需求选择不同的舍入策略。
  • 可读性: 相比复杂的计算,round()函数更易于理解和维护。

第二部分:mod() – 求余界的清道夫

mod() 函数,就是用来求余数的。在CSS中,它可以帮助我们实现一些循环和重复的布局效果。

1. 基本语法

mod(dividend, divisor)

  • dividend: 被除数。
  • divisor: 除数。

2. 应用场景

  • 循环背景:

    让背景图片以一定的规律循环显示。

    .element {
      width: 200px;
      height: 200px;
      background-image: url('image1.png'), url('image2.png'), url('image3.png');
      background-position: calc(mod(var(--index), 3) * 100%) 0;
      background-size: 300% 100%;
      --index: 0;
      animation: cycle 9s infinite steps(1);
    }
    
    @keyframes cycle {
      to {
        --index: 1;
      }
    }

    这里使用了CSS变量 --index 来控制背景图片的偏移量。mod(var(--index), 3) 的结果会循环在 0, 1, 2 之间,从而实现背景图片的循环显示。

  • 交替颜色:

    让列表项的背景颜色交替显示。

    ul {
      list-style: none;
      padding: 0;
    }
    
    li {
      padding: 10px;
      background-color: hsl(0, 0%, calc(mod(var(--i), 2) * 20 + 90%));
      --i: calc(var(--n) + 1);
    }
    
    li:nth-child(1) { --n: 0; }
    li:nth-child(2) { --n: 1; }
    li:nth-child(3) { --n: 2; }
    li:nth-child(4) { --n: 3; }
    li:nth-child(5) { --n: 4; }
    li:nth-child(6) { --n: 5; }

    这个例子中,使用了CSS变量 --i--n 来记录列表项的索引。mod(var(--i), 2) 的结果会交替在 0 和 1 之间,从而控制背景颜色的交替显示。

  • 创建复杂的图案:

    结合其他CSS属性,可以创建出一些复杂的图案。

    .container {
      width: 300px;
      height: 300px;
      background-color: #fff;
      position: relative;
    }
    
    .dot {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      background-color: #000;
      position: absolute;
      top: calc(sin(var(--angle)) * 100px + 150px);
      left: calc(cos(var(--angle)) * 100px + 150px);
      --angle: calc(mod(var(--i), 12) * 30deg);
      --i: calc(var(--n) + 1);
    }
    
    .dot:nth-child(1) { --n: 0; }
    .dot:nth-child(2) { --n: 1; }
    .dot:nth-child(3) { --n: 2; }
    .dot:nth-child(4) { --n: 3; }
    .dot:nth-child(5) { --n: 4; }
    .dot:nth-child(6) { --n: 5; }
    .dot:nth-child(7) { --n: 6; }
    .dot:nth-child(8) { --n: 7; }
    .dot:nth-child(9) { --n: 8; }
    .dot:nth-child(10) { --n: 9; }
    .dot:nth-child(11) { --n: 10; }
    .dot:nth-child(12) { --n: 11; }
    

    这个例子创建了一个圆形排列的12个点。mod(var(--i), 12) 的结果会循环在 0 到 11 之间,从而控制每个点的角度。

3. mod()的优势

  • 循环控制: 可以方便地实现循环和重复的效果。
  • 可读性: 相比复杂的计算,mod()函数更易于理解和维护。
  • 灵活性: 可以结合其他CSS属性,创建出各种各样的布局效果。

第三部分:round() + mod() = 无限可能

这两个函数单独使用已经很强大了,如果结合起来使用,那简直是如虎添翼,可以实现一些更加复杂的布局效果。

1. 实例:创建动态的仪表盘

<div class="dashboard">
  <div class="needle" style="--value: 65;"></div>
</div>
.dashboard {
  width: 200px;
  height: 100px;
  border: 1px solid #ccc;
  position: relative;
  overflow: hidden;
}

.needle {
  width: 100px;
  height: 2px;
  background-color: red;
  position: absolute;
  left: 50%;
  bottom: 0;
  transform-origin: left center;
  /* 将value值限制在0-100之间 */
  --safe-value: clamp(0, var(--value), 100);
  /* 根据value值计算旋转角度,限制在0-180度之间 */
  transform: translateX(-50%) rotate(calc(round(var(--safe-value) / 100 * 180, 1deg)));
}

这个例子创建了一个简单的仪表盘。通过round()函数,我们可以将旋转角度精确到1度,避免出现抖动。

2. 实例:创建可配置的网格系统

:root {
  --grid-columns: 12; /* 总列数 */
  --grid-gap: 10px; /* 列间距 */
}

.container {
  display: flex;
  flex-wrap: wrap;
  margin-left: calc(var(--grid-gap) * -1); /* 抵消第一列的间距 */
}

.grid-item {
  /* 使用grid-column-span变量控制列的跨度 */
  --grid-column-span: 4;
  width: calc(round((100% / var(--grid-columns) * var(--grid-column-span)), 0.01%) - var(--grid-gap)); /* 计算每列的宽度,精确到0.01% */
  margin-left: var(--grid-gap);
  margin-bottom: var(--grid-gap);
  box-sizing: border-box;
  border: 1px solid #ccc;
  padding: 10px;
}

/* 使用示例 */
.grid-item:nth-child(1) { --grid-column-span: 6; } /* 第一列跨6列 */
.grid-item:nth-child(2) { --grid-column-span: 3; } /* 第二列跨3列 */
.grid-item:nth-child(3) { --grid-column-span: 3; } /* 第三列跨3列 */

这个例子创建了一个可配置的网格系统。通过CSS变量,我们可以轻松地控制网格的列数和间距。round()函数保证了每列的宽度计算精确,避免出现布局错乱。

第四部分:注意事项

  • 兼容性: round()mod() 还是提案阶段,目前浏览器支持情况还不太好,使用时要注意兼容性处理。可以使用Polyfill或者PostCSS插件来提供支持。
  • 性能: 过度使用复杂的计算可能会影响页面性能,要注意优化。
  • 可读性: 虽然函数可以简化代码,但也要注意代码的可读性,添加必要的注释。

第五部分:总结

round()mod() 是非常有用的CSS函数,可以帮助我们实现更加灵活和精确的布局效果。虽然目前兼容性还不太好,但相信未来一定会得到广泛应用。

今天就讲到这里,希望大家能够掌握这两个新朋友的用法,并在实际项目中灵活运用。 祝大家编码愉快,早日成为前端大牛! 感谢各位的聆听! 咱们下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注