乐趣区

关于前端:使用三角函数在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($n, $primesList) {
  @each $prime in $primesList {@if ($n % $prime == 0) {@return false;}
  }
  @return true;
}

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

循环体外部,通过 % (取模) 运算符判断 $n 是否能被以后的素数 $prime 整除。如果 $n 能整除,阐明 $n 不是素数,函数会立刻返回 false 终止执行。否则则示意 $n 不可被任何素数整除,因而 $n 是素数,函数返回 true

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

@mixin primeCounter($selector, $upperBound: 2000) {
  
  $curr: 2;
  $primesList: [];
  
  @while $curr <= $upperBound {@if isPrime($curr, $primesList) {$primesList: append($primesList, $curr);
    }
    $curr: $curr + 1;
  }
  :root {
    @each $prime in $primesList {--p#{$prime}: 1;
    }
  }
  $mult: "1";
  @each $prime in $primesList {$mult: $mult "* var(--p#{$prime})";
    $val: $prime;
    @while $val <= $upperBound {#{$selector}:nth-child(#{$val}n) {--p#{$prime}: #{$val};
      }
      $val: $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)

退出移动版