使用三角函数在CSS中的随机性

简介: 使用三角函数在CSS中的随机性

本文翻译自 Randomness in CSS using trigonometry,作者:Kacper 略有删改

在过去,我已经介绍了CSS中使用模运算的伪随机性的主题,我使用素数创建了一个自动计数器,可以用来为每个对象生成不同的值。因此,我们可以独立地计算每个元素的伪随机值。

尽管这个解决方案很强大,但它几乎没有缺点:

  • 模函数不是连续的
  • 它过于复杂:需要3个变量和@property定义每个我们想要生成的随机值
  • 需要使用尚未广泛支持的@property

幸运的是,我们可以做得更好!在本文中提出一个更好的解决方案,使用三角函数。

更好的方法

自从我上次探索这个主题以来,CSS中出现了一些令人惊叹的新特性。其中最令人兴奋的是三角函数。他们解开了许多以前不可能完成的任务。它们也是CSS中第一个原生支持的有界连续函数,使它们成为创建伪随机生成器的工具。

随机性vs伪随机性

显然,这里提出的解决方案仅生成伪随机值。使用预定常数计算所有值。正如我在上一篇文章中所描述的,我们可以添加一个额外的--seed变量,并从系统外部更改它(例如在加载时在JavaScript中设置它),以提供较少确定性的结果,但CSS不提供任何非确定性的方法。也就是说,该解决方案应该足以获得用于动画、位置等的足够的伪随机值。

正弦函数的特征

正弦函数和余弦函数有趣的原因有很多,它们可以在涉及圆和旋转的各种操作中非常有用。在我们的例子中,我们可以将它们的属性用于其他目的。

有界函数

正弦和余弦的一个重要性质是,结果值总是在-1和1之间有界。这意味着,无论传递给它的值有多大或小,结果总是在这个范围内的值。然后,我们可以对范围[0,1]执行简单的归一化。有了归一化的值,我们可以使用它来表示使用简单的线性映射的任何值。

--x: calc(0.5 + 0.5 * sin(var(--n) * 342.06184 + 23.434));

/* Then we can use it as following */
background: rgb(calc(50 + var(--x) * 100), 0, 0);
/* Red will be in the range of 50-150.

上面的代码使用的是我在上一篇文章中介绍的counter var(--n),我使用素数创建了一种在CSS中自动创建计数器变量的有效方法。

然后将该值乘以一些任意值并偏移以提供伪随机大数(这些值实际上并不重要,您可以根据需要更改它们以获得不同结果)。之后我们使用正弦函数将其映射到范围[-1, 1]。最后如下面的动画所示,我们可以通过应用简单的代数变换将其映射到范围[0, 1]。一旦我们从范围[0, 1]中获得值,我们可以使用线性映射将其映射到任何其他期望值。

连续性

正弦函数的另一个特性是连续性。简单理解,你可以认为正弦或余弦函数输入的微小变化最终会导致输出的微小变化。由于这一特性,我们可以在动画中实现逐渐变化的值,同时使系统表现出随机行为。

示例

下面是一些示例,展示了使用三角函数生成伪随机值的潜力。

圆网格动画

第一个示例显示了正弦属性的作用。生成的值是随机的,但我们仍然可以保持顺序和连续性的感觉,示例涉及到颜色和大小动画。

代码的关键部分是x,y,z和w变量的计算,这些变量分别用于表示红色,绿色,蓝色和宽度。

div::before {
   
   
  --x: calc(0.5 + 0.5 * sin(4.284 * var(--n)));
  --y: calc(0.5 + 0.5 * sin(7.284 * var(--n)));
  --z: calc(0.5 + 0.5 * sin(4 * var(--n) + 2 * var(--t)));
  --w: calc(0.5 + 0.5 * sin((0.2 * cos(var(--t)/100) + 0.8) * 49.123 * var(--n) + var(--t)));

  background: rgb(
    calc(50 +  100 * var(--x)),
    calc(200 + 30 * var(--y)),
    calc(120 + 100 * var(--z))
  );
  width: calc(50% + var(--w)*50%);
}

最后2个变量,除了我们的计数器--n之外,使用时间变量--t,其通过运行逐渐改变变量的动画来获得:

@property --t {
   
   
  syntax: '<number>'; /* <- defined as type number for the transition to work */
  initial-value: 0;
  inherits: true;
}
:root {
   
   
  --t: 0;
}

@keyframes timeOn {
   
   
  50% {
   
   --t: 30}
}

html {
   
   
  animation: 30s timeOn infinite linear;
}

这是代码中唯一使用@property的部分。为了让它在所有浏览器中工作,我们可以简单地在JavaScript中更新这个变量,而不会失去在普通CSS中计算其他所有内容的能力。

斑点动画

随机性也可以与SVG元素一起使用,使其与SVG过滤器结合使用时成为一个强大的工具。下面的演示灵感来自于一篇令人惊叹的CSS技巧文章The Gooey Effect【https://css-tricks.com/gooey-effect/】。

使用简单公式确定每个单独的斑点的位置。唯一的区别是我们使用cx、cy、r和fill来设置它们的样式,因为它们是SVG元素。

.blobs > circle {
   
   
  --x: calc(sin(var(--t) + var(--n) * 74.543 + 432.43312));
  --y: calc(cos(var(--t) + var(--n) * 2.34 + 1.432));
  --v: calc(0.5 + 0.5 * sin(var(--n) * var(--n) * 4.343 + 2.673));

  cx: calc(10vw + var(--x) * 80vw);
  cy: calc(10vh + var(--y) * 80vh);
  r: calc(var(--v) * 5vw + 1vw);
}

为了实现粘性效果,我们使用以下SVG过滤器:

<filter id="goo">
    <feGaussianBlur in="SourceGraphic" result="blur" stdDeviation="15" />
    <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -11" result="goo" />
    <feBlend in2="goo" in="SourceGraphic" result="mix" />
</filter>

孟菲斯风格

最后一个演示是一个更新版本的例子,我在之前尝试在CSS中实现随机性,在那里我使用了模运算符。使用新的解决方案计算更容易理解和修改。

核心代码:

div.obj {
   
   
  --v: calc(0.5 + 0.5 * sin(var(--n) * 0.3141 + var(--n) * var(--n) * 0.9125 * var(--seed) + 0.531));
  --x: calc(0.5 + 0.5 * cos(var(--seed) * var(--n) * 0.234 + var(--seed)));
  --y: calc(0.5 + 0.5 * cos(var(--seed) * var(--n) * 432.32432 + var(--seed)));
  --viz: calc(0.5 + 0.5 * sin(var(--seed) * var(--n) * 6901.525213542 + 432.5342));
  --visible: calc(min(1, max(0, var(--viz) * 1)));

  opacity: var(--visible);
  display: inline-block;
  position: absolute;
  transform: rotate(calc(360deg * var(--v)));
  top: calc(var(--y) * 100vh);
  left: calc(var(--x) * 100vw);
}

最后

解析一下各案例中用到的公共代码:

判断一个数字是否为素数(质数):

@function isPrime({
   
   mathJaxContainer[0]}primesList) {
   
   
  @each {
   
   mathJaxContainer[1]}primesList {
   
   
    @if ({
   
   mathJaxContainer[2]}prime == 0) {
   
   
      @return false;
    }
  }
  @return true;
}

函数接受两个参数:$n` 和 `$primesList$n`是要判断是否为素数的数字,`$primesList 是一个包含已知素数的列表。

循环体内部,通过 % (取模) 运算符判断 $n` 是否能被当前的素数 `$prime 整除。如果 $n` 能整除,说明 `$n 不是素数,函数会立即返回 false 终止执行。否则则表示 $n` 不可被任何素数整除,因此 `$n 是素数,函数返回 true

计算素数并在CSS中生成相应的变量和样式规则

@mixin primeCounter({
   
   mathJaxContainer[8]}upperBound: 2000) {
   
   

  $curr: 2;
  $primesList: [];

  @while {
   
   mathJaxContainer[9]}upperBound {
   
   
    @if isPrime({
   
   mathJaxContainer[10]}primesList) {
   
   
      {
   
   mathJaxContainer[11]}primesList, $curr);
    }
    {
   
   mathJaxContainer[12]}curr + 1;
  }
  :root {
   
   
    @each {
   
   mathJaxContainer[13]}primesList {
   
   
        --p#{
   
   $prime}: 1;
    }
  }
  $mult: "1";
  @each {
   
   mathJaxContainer[14]}primesList {
   
   
    {
   
   mathJaxContainer[15]}mult " * var(--p#{
   
   $prime})";
    {
   
   mathJaxContainer[16]}prime;
    @while {
   
   mathJaxContainer[17]}upperBound {
   
   
      #{
   
   {
   
   mathJaxContainer[18]}val}n) {
   
   
        --p#{
   
   {
   
   mathJaxContainer[19]}val};
      }
      {
   
   mathJaxContainer[20]}val * $prime;
    }
  }

  #{
   
   $selector} {
   
   
    --n: calc(#{
   
   $mult});
  }
}

这个mixin接受两个参数:$selector` 和 `$upperBound$selector` 是要添加素数样式的选择器,`$upperBound 是生成素数的上限,默认为 2000。

首先定义了两个变量 $curr` 和 `$primesList,分别用于追踪当前数字和保存素数列表。

使用 @while 循环来遍历从 2 到 $upperBound` 的所有数字。在每次循环迭代中,通过调用外部函数 `isPrime` 来检查当前数字是否为素数。如果是素数,则将其添加到 `$primesList 列表。

接下来,定义了一个 :root 选择器,在其中使用 @each 循环遍历 $primesList 中的每个素数,并为每个素数生成一个CSS变量。生成的变量名称以 --p 开头,后面跟着素数的值。例如,对于素数 2,生成的变量名为 --p2

接下来是一个 $mult 变量和第二个 @each 循环。这个循环用于生成素数相关的样式规则。

在每次循环迭代中,将当前素数赋值给变量 $prime。然后,使用 :nth-child() 伪类选择器来为每个素数的倍数生成样式规则。这些样式规则将设置对应素数的CSS变量值,以便在页面中应用相应的样式。

最后,在mixin末尾,为传入的选择器生成一个总计数变量 --n。这个变量通过将所有素数的CSS变量值乘积计算而来。

可以在CSS中使用此mixin来生成素数相关的变量和样式规则。例如:

@include primeCounter(".container", 20);

这会将 .container 元素内部的所有素数倍数设置为相应的CSS变量值,并生成总计数变量 --n

到此本文结束,使用CSS三角函数产生随机值并结合mixin来生成素数相关的变量和样式规则达成最终的效果,有兴趣的可以看看原文体验看看。


看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)

目录
相关文章
|
6月前
|
前端开发 JavaScript 算法
JavaScript实现伪随机正态分布
JavaScript实现伪随机正态分布
93 0
|
11天前
|
前端开发
CSS权重计算
【10月更文挑战第28天】CSS权重计算是CSS样式应用中的一个重要概念,了解和掌握权重计算规则有助于更好地控制页面的样式表现,避免样式冲突和意外的显示效果,从而提高网页开发的效率和质量。
|
2月前
|
前端开发 UED
css3的禁忌
在使用 CSS3 时,需遵循最佳实践以确保代码的可维护性和跨浏览器兼容性。避免过度使用复杂选择器,确保添加 `-webkit-`、`-moz-` 等前缀;优先采用相对单位 `%`、`em`、`rem`;关注视觉辅助工具的可及性;利用 CSS 类减少重复样式;合理使用媒体查询实现响应式设计;优化动画效果,避免性能下降;谨慎混合使用布局模型,如 Flexbox 和 Grid;持续测试不同浏览器以确保兼容性。
|
4月前
|
前端开发
css特效——“凹”和“凸”效果
css特效——“凹”和“凸”效果
247 1
|
6月前
|
前端开发
CSS3的几个变形案例……
CSS3的几个变形案例……
|
前端开发
CSS 高阶小技巧 - 角向渐变的妙用!
CSS 高阶小技巧 - 角向渐变的妙用!
105 0
|
6月前
|
前端开发 C++
CSS阶详细解析一
CSS进阶 目标:掌握复合选择器作用和写法;使用background属性添加背景效果 01-复合选择器 定义:由两个或多个基础选择器,通过不同的方式组合而成。 作用:更准确、更高效的选择目标元素(标签)
49 0
|
前端开发 开发者 容器
你会用CSS画一个炫酷的3D旋转柱状体吗?
你会用CSS画一个炫酷的3D旋转柱状体吗?
1084 0
|
前端开发
|
Web App开发 前端开发 JavaScript
基于 CSS 的变换与动画技术
本文介绍基于 CSS 的变换与动画技术
99 0