0
点赞
收藏
分享

微信扫一扫

使用 CSS 中 sin() 和 cos() 三角函数创建时钟

原文链接 Creating a Clock with the New CSS sin() and cos() Trigonometry Functions -- 作者 Mads Stoumann 

本文为译文,采用意译方式

正文

CSS 三角函数在这!如果你使用最新版的 Firefox 或者 Safari(当然 Chrome 也可以)浏览器,那就可以使用它们。在 CSS 中拥有这种数学能力,将开辟一大推可能性。在本教程中,我们尝试感受下这两个较新的函数:sin() 和 cos()。

还有其他的三角函数 -- 包括 tan()。为什么我们只关注 sin()cos() 呢?因为这很符合我的想法,它们可以把文本放在圆圈的边缘。当 Chris 分享了一种使用 Sass mixin 方法的时候,在 CSS-Tricks 上做了介绍。那已经是六年前的事了,我们来给予前言的应用。

如下。当前仅支持最新版的 Firefox 或者 Safari(当然 Chrome 也可以)浏览器。

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_三角函数

所以,它并不完全是像单词形成一个圆形,而是我们将文本字符沿着圆形形成一个时钟面。下面是一些我们开始的标记。

<div class="clock">
  <div class="clock-face">
    <time datetime="12:00">12</time>
    <time datetime="1:00">1</time>
    <time datetime="2:00">2</time>
    <time datetime="3:00">3</time>
    <time datetime="4:00">4</time>
    <time datetime="5:00">5</time>
    <time datetime="6:00">6</time>
    <time datetime="7:00">7</time>
    <time datetime="8:00">8</time>
    <time datetime="9:00">9</time>
    <time datetime="10:00">10</time>
    <time datetime="11:00">11</time>
  </div>
</div>

接着,这里对 .clock-face 容器有些基本样式。我决定使用带有 datetime 属性的 <time> 标签。

.clock {
  --_ow: clamp(5rem, 60vw, 40rem);
  --_w: 88cqi;
  aspect-ratio: 1;
  background-color: tomato;
  border-radius: 50%;
  container-type: inline;
  display: grid;
  height: var(--_ow);
  place-content: center;
  position: relative;
  width var(--_ow);
}

这里我稍微装饰了下,但是我们只是得到了基本的形状和背景颜色。留意我们是怎么使用 CSS 变量 这是宽度值的。我们不久后会使用到它:

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_前端_02

它看起来有点现代艺术体验,是吧?我们来介绍下一个变量,--_r,其存储圆形半径,也就是等于圆形宽度的一半。如果宽度(--_w)改变,那么半径值(--_r)也会更新 -- 这归功于另外一个 CSS 数学函数 calc()

.clock {
  --_w: 300px;
  --_r: calc(var(--_w) / 2);
  /* rest of styles */
}

现在是一些数学计算。一个圆形是 360 度。我们的时钟有 12 个标记,所以我们每 30 度(360 / 12)放置一个数字。在数学领域,一个圆形开始于 3 点钟方向,所以中午时分要减去 90 度,也就是 270 度(360 - 90)。

我们添加另一个变量,--_d,我们可以使用它在时钟表面为每个数字设置角度。我们将以每 30 度递增,以完善圆形:

.clock time:nth-child(1) { --_d: 270deg; }
.clock time:nth-child(2) { --_d: 300deg; }
.clock time:nth-child(3) { --_d: 330deg; }
.clock time:nth-child(4) { --_d: 0deg; }
.clock time:nth-child(5) { --_d: 30deg; }
.clock time:nth-child(6) { --_d: 60deg; }
.clock time:nth-child(7) { --_d: 90deg; }
.clock time:nth-child(8) { --_d: 120deg; }
.clock time:nth-child(9) { --_d: 150deg; }
.clock time:nth-child(10) { --_d: 180deg; }
.clock time:nth-child(11) { --_d: 210deg; }
.clock time:nth-child(12) { --_d: 240deg; }

OK,现在是时候让我们对 sin()cos() 函数尝尝鲜了。我们使用它们获取每个数字的坐标信息,以便我们将它们放置在时钟面的正确位置。

X 坐标点的位置公式是 半径 + (半径 * cos(度数)),我们将其存放在新的变量 --_x 中:

--_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));

Y 坐标点的位置公式是 半径 + (半径 * sin(度数)),我们将其存放在新的变量 --_y 中:

--_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));

设置数字还有一些其他需要做的事情,所以添加一些基本的样式,确保它们正确定位,放置在对应的坐标上:

.clock-face time {
  --_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));
  --_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));
  --_sz: 12cqi;
  display: grid;
  height: var(--_sz);
  left: var(--_x);
  place-content: center;
  position: absolute;
  top: var(--_y);
  width: var(--_sz);
}

注意 -- 变量 --_sz,我们稍后会用于设置数字的宽度和高度。目前为止,我们得到的结果:

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_前端_03

这绝对看起来像一个时钟!想想怎么将每个数字的 top-left 角环绕圆形放置在正确的位置?当计算每个数字位置,我们需要收缩半径。在计算半径之前,我们可以从圆形的宽度 --_w 中减去 --_sz 值:

--_r: calc((var(--_w) - var(--_sz)) / 2);

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_Chrome_04

好多了!我们更改下颜色,让它看起来更优雅:

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_CSS_05

我们可以止步于此了!我们实现了将文本环绕于圆圈的目标,是吧?但是,哪有没有时分秒指针的时钟呢?

我们使用简单的 CSS 动画实现它。首先,在 HTML 标记中我们多添加三个元素。

<div class="clock">
  <!-- after <time>-tags -->
  <span class="arm seconds"></span>
  <span class="arm minutes"></span>
  <span class="arm hours"></span>
  <span class="arm center"></span>
</div>

然后,为这三个指针添加些公有的样式。其中大部分只是确保指针正确定位并存放于坐标上:

.arm {
  background-color: var(--_abg);
  border-radius: calc(var(--_aw) * 2);
  display: block;
  height: var(--_ah);
  left: calc((var(--_w) - var(--_aw)) / 2);
  position: absolute;
  top: calc((var(--_w) / 2) - var(--_ah));
  transform: rotate(0deg);
  transform-origin: bottom;
  width: var(--_aw);
}

我们将为三个指针应用一样的动效

@keyframes turn {
  to {
    transform: rotate(1turn);
  }
}

唯一不同的是,不同的指针转过一圈所需要的时间。时针转过一圈需要12小时。animation-duration 属性只接受毫秒或者秒的值。我们以秒计算,那就是 43200 秒(60 秒 * 60 分 * 12 小时)。

animation: turn 43200s infinite;

分针绕一圈需要一个小时。但是,我们希望是 multi-step animation。所以指针之间的移动是交错的,而不是线性的。我们将需要 60 步,一步表示一分钟:

animation: turn 3600s steps(60, end) infinite;

秒针和分针类似,但是持续时间是 60 秒而不是 60 分钟。

animation: turn 60s steps(60, end) infinite;

我们更新下创建的公共样式的属性:

.seconds {
  --_abg: hsl(0, 5%, 40%);
  --_ah: 145px;
  --_aw: 2px;
  animation: turn 60s steps(60, end) infinite;
}
.minutes {
  --_abg: #333;
  --_ah: 145px;
  --_aw: 6px;
  animation: turn 3600s steps(60, end) infinite;
}
.hours {
  --_abg: #333;
  --_ah: 110px;
  --_aw: 6px;
  animation: turn 43200s linear infinite;
}

我们想以现在的时间开始怎么办?我们需要些许 JavaScript 辅助下:

const time = new Date();
const hour = -3600 * (time.getHours() % 12);
const mins = -60 * time.getMinutes();
app.style.setProperty('--_dm', `${mins}s`);
app.style.setProperty('--_dh', `${(hour+mins)}s`);

我们添加 id="app" 在钟表,然后设置两个新的自定义属性,设置负值 animation-delay,正如 Mate Marschalko 在 when he shared a CSS-only clock 中所做那样。JavaScript 的时间对象中的 getHours() 方法是使用 24 小时格式,所以我们使用 remainder 将其转换成 12 小时形式。

CSS 中,我们也需要添加 animation-delay:

.minutes {
  animation-delay: var(--_dm, 0s);
  /* other styles */
}
.hours {
  animation-delay: var(--_dh, 0s);
  /* other styles */
}

还有一件事。使用 CSS@supports 结合我们创建的属性,我们可以为不支持 sin()cos() 浏览器提供一个回调。(感谢 Temani Afif!):

@supports not (left: calc(1px * cos(45deg))) {
  time {
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%,-50%) rotate(var(--_d)) translate(var(--_r)) rotate(calc(-1*var(--_d)))
  }
}

至此,我们的时钟已经完成了!再次放出最终的案例。目前,它仅支持最新版的 Firefox 或者 Safari(当然 Chrome 也可以)浏览器。

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_Chrome_06

我们还可以做什么?

这里仅是个思想碰撞,我们可以时钟更改为环形图片展,通过将 <time> 标签替换成 <img> 标签,然后更新宽度 (--_w) 和 半径 (--_r)值:

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_前端_07

我们再尝试一个。之前我就提及时钟看起来像现代艺术体验。我们可以利用它并重新创建前几天我在画廊的海报上看到的图案(不幸的是我当初没买)。现在回忆,它被称为 Moon,是由一堆的点形成的圆形。

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_前端_08

这次我们将使用无序列表,因为圆形并不遵循特定的顺序。我们不打算再一个标签中添加所有的列表项。而是通过 JavaScript 将其注入,并且添加一些控件以便我们操控最终的结果。

控件包括范围输入框(<input type="range">),我们将其放置在 <form> 元素内,然后监听输入框事件。

<form id="controls">
  <fieldset>
    <label>Number of rings
      <input type="range" min="2" max="12" value="10" id="rings" />
    </label>
    <label>Dots per ring
      <input type="range" min="5" max="12" value="7" id="dots" />
    </label>
    <label>Spread
      <input type="range" min="10" max="40" value="40" id="spread" />
    </label>
  </fieldset>
</form>

运行下面 input 中的方法,我们将创建一堆带有角度(--_d)变量的 <li> 元素,那些角度像我们之前应用那样。我们也可以调整我们的半径变量(--_r)。

我还想不同的点有不同的颜色。所以,我们为每个列表项随机生成(也不是完全随机) HSL 颜色,并将其存放在一个新的 CSS 变量,--_bgc

const update = () => {
  let s = "";
  for (let i = 1; i <= rings.valueAsNumber; i++) {
    const r = spread.valueAsNumber * i;
    const theta = coords(dots.valueAsNumber * i);
    for (let j = 0; j < theta.length; j++) {
      s += `<li style="--_d:${theta[j]};--_r:${r}px;--_bgc:hsl(${random(
        50,
        25
      )},${random(90, 50)}%,${random(90, 60)}%)"></li>`;
    }
  }
  app.innerHTML = s;
}

random() 函数根据给定的数字范围生成值:

const random = (max, min = 0, f = true) => f ? Math.floor(Math.random() * (max - min) + min) : Math.random() * max;

完成了。我们使用 JavaScript 来渲染标签,一旦渲染完成,我们就不需要它了。sin()cos() 函数帮助我们将所有点定位到正确位置。

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_CSS_09

总结

将东西环绕圆形放置是一个很基础的例子,用于展示三角函数,比如 sin()cos() 的强大。但是,这真是太棒了,我们将获取现代 CSS 特性,为旧的解决方法提供新的的解决方案。我确信,我们在看到更加有趣、复杂和创造性的使用案例,特别是当 ChromeEdge 对特性支持的时候。(截止目前,Chrome 最新版已经支持)。

使用 CSS 中 sin() 和 cos() 三角函数创建时钟_Chrome_10

举报

相关推荐

0 条评论