使用新的 CSS sin() 和 cos() 三角函数创建一个时钟

使用新的 CSS sin() 和 cos() 三角函数创建一个时钟

源节点: 1999799

CSS 三角函数来了! 好吧,如果您使用的是最新版本的 Firefox 和 Safari,那就是。 在 CSS 中拥有这种数学能力开辟了一大堆可能性。 在本教程中,我认为我们应该将脚趾浸入水中以感受几个新功能: sin()cos().

管道中还有其他三角函数——包括 tan() - 那么为什么只关注 sin()cos()? 它们恰好非常适合我的想法,即沿着圆圈的边缘放置文本。 当 Chris 分享了一种使用 Sass mixin 的方法. 那是六年前的事了,所以让我们给它最前沿的治疗。

这就是我的想法。 同样,目前它仅在 Firefox 和 Safari 中受支持:

所以,这并不完全像单词形成一个圆形,而是我们将文本字符沿着圆圈放置以形成一个钟面。 这是我们可以用来开始的一些标记:

<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 容器。 我决定使用 <time> 用一个标签 datetime 属性。 

.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);
}

我在那里做了一些装饰,但只是为了获得基本的形状和背景颜色,以帮助我们了解我们在做什么。 注意我们如何保存 width 中的值 CSS 变量. 我们稍后会用到它。 到目前为止没什么可看的:

大番茄色圆圈,左侧有垂直的数字 1-12 列表。

看起来像是某种现代艺术实验,对吧? 让我们引入一个新变量, --_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; }

好的,现在是时候让我们亲自动手了 sin()cos() 功能! 我们想要做的是使用它们来获取每个数字的 X 和 Y 坐标,以便我们可以将它们正确地放置在钟面上。

X坐标的公式是 radius + (radius * cos(degree)). 让我们把它插入我们的新 --_x 变量:

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

Y坐标的公式是 radius + (radius * sin(degree)). 我们有我们需要计算的东西:

--_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,我们将用于 widthheight 一会儿的数字。 让我们看看到目前为止我们有什么。

大番茄色圆圈,边缘有偏心小时数字标签。

这绝对看起来更像一个时钟! 看到每个数字的左上角如何定位在圆圈周围的正确位置了吗? 在计算每个数字的位置时,我们需要“缩小”半径。 我们可以 扣除 数字的大小(--_sz) 从圆的大小 (--_w), 在我们计算半径之前:

--_r: calc((var(--_w) - var(--_sz)) / 2);
大番茄色圆圈,圆形边缘带有小时数字标签。

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

深灰色背景下带有数字的白色钟面。 时钟没有指针。

我们可以在这里停下来! 我们完成了围绕一个圆圈放置文本的目标,对吧? 但是没有指针显示小时、分钟和秒的时钟是什么?

让我们为此使用一个 CSS 动画。 首先,让我们在标记中再添加三个元素,

<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 属性只接受以毫秒和秒为单位的值。 让我们坚持使用秒,即 43,200 秒(60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

它需要 1小时 等加工。为 分臂 做一个完整的转弯。 但我们希望这是一个 多步动画 所以手臂之间的运动是交错的而不是线性的。 我们需要 60 步,每分钟一步:

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

秒臂 is 差不多一样 作为分钟手臂,但持续时间是 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 所做的那样 当他分享一个纯 CSS 的时钟时。 该 getHours() JavaScipt的方法 Date 对象使用 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 中受支持。

我们还能做什么?

只是在这里胡闹,但我们可以通过更换 <time> 标签与 <img> 然后更新宽度(--_w) 和半径 (--_r) 值:

让我们再试一次。 我之前提到过时钟看起来有点像现代艺术实验。 我们可以利用它并重新创建我前几天在艺术画廊的海报上看到的图案(不幸的是我没有买)。 我记得,它被称为“月亮”,由一串圆点组成。

一个大圆圈由一堆不同土色的小实心圆圈组成。

这次我们将使用无序列表,因为圆圈不遵循特定的顺序。 我们甚至不会将所有列表项都放在标记中。 相反,让我们用 JavaScript 注入它们并添加一些我们可以用来操纵最终结果的控件。

控件是范围输入(<input type="range">) 我们将把它包装在 <form> 并倾听 input 事件。

<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>

我们将在“输入”上运行这个方法,这将创建一堆 <li> 度数为 (--_d) 我们之前使用的变量应用于每个变量。 我们还可以重新调整半径变量的用途(--_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() 功能帮助我们将所有的点放在正确的位置。

最后的思考

围绕一个圆圈放置东西是一个非常基本的例子,可以展示三角函数的强大功能,例如 sin()cos()。 但这是 很酷的是,我们正在获得现代 CSS 功能,为旧的解决方法提供新的解决方案我相信我们会看到更多有趣、复杂和创造性的用例,尤其是当浏览器支持 Chrome 和 Edge 时。

时间戳记:

更多来自 CSS技巧