有可能你会认为数学在 CSS 中用不上,但实际上,在写 CSS 时运用一些数学可以帮你做一些令人惊讶的事情。数学(尤其是三角函数)可以帮助你模拟一个真实的世界。如果你想做这样的事情,你需要了解复杂的三维变换。如果你只是想在朋友面前展示一下你的才华,这将是一件非常有兴趣的事情。
这里有一个示例:https://www.cdsy.xyz/tools/runcode?name=for71199_sanshiermianti
/*Haml:*/
.polyhedron.polyhedron--icosidodecahedron
.polyhedron.polyhedron--pentagonal-rotunda
-(1..10).each do |i|
.polyhedron__face.polyhedron__face--triangle
-(1..6).each do |i|
.polyhedron__face.polyhedron__face--pentagon
.penta__inner
.polyhedron.polyhedron--pentagonal-rotunda
-(1..10).each do |i|
.polyhedron__face.polyhedron__face--triangle
-(1..6).each do |i|
.polyhedron__face.polyhedron__face--pentagon
.penta__inner
@import 'compass/css3';
$l: 8em;
$of: .32;
$linecol: white;
@function central-angle($n) {
@return 360deg/$n;
}
@function in-angle($n) {
@return 180deg*($n - 2)/$n;
}
@function skew-angle($in-angle) {
@return abs(90deg - $in-angle)
}
@function inradius($n, $l) {
@return ($l/2)/tan(central-angle($n)/2);
}
@function circumradius($n, $l) {
@return ($l/2)/sin(central-angle($n)/2);
}
$tri-n: 3;
$tri-ca: central-angle($tri-n);
$tri-a: in-angle($tri-n);
$tri-sa: skew-angle($tri-a);
$tri-h: $l*sin($tri-a);
$tri-ri: inradius($tri-n, $l);
$tri-rc: circumradius($tri-n, $l);
$penta-n: 5;
$penta-ca: central-angle($penta-n);
$penta-a: in-angle($penta-n);
$penta-sa: skew-angle($penta-n);
$penta-ri: inradius($penta-n, $l);
$penta-rc: circumradius($penta-n, $l);
$penta-h: $penta-ri + $penta-rc;
$penta-rhl: $l + $l/2/sin(90deg - (180deg - $penta-a));
$penta-rha1: $penta-a;
$penta-rhsa1: abs(90deg - $penta-rha1);
$penta-rha2: 180deg - 2*(180deg - $penta-a);
$penta-rhsa2: abs(90deg - $penta-rha2);
$penta-sup: $l*cos($penta-a/2);
$penta-inf: $penta-rhl*cos($penta-a/2) - $penta-sup; /* ok */
$deca-n: 10;
$deca-ca: central-angle($deca-n);
$deca-ri: inradius($deca-n, $l);
$deca-rc: circumradius($deca-n, $l);
$proj-h: $deca-ri - $penta-rc;
@function asin($value, $unit: 'rad', $precision: 25) {
$coeff: 1;
$sum: 0;
$flag: 0;
$sign: 1;
@if $value > 1 {
@warn 'Invalid input.';
@return false;
}
@if abs($value) > 1/sqrt(2) {
$flag: 1;
$sign: $value/abs($value);
$value: sqrt(1 - pow($value, 2));
}
$sum: $sum + $coeff*$value;
@for $i from 1 through $precision {
$coeff: $coeff*(2*$i - 1)/(2*$i);
$sum: $sum + $coeff*pow($value, 2*$i + 1)/(2*$i + 1);
}
$result: $sign*($flag*pi()/2 + pow(-1, $flag)*$sum);
$result: $result*180deg/pi();
@return $result;
}
$penta-rax: asin($proj-h/$penta-h);
$rotunda-h: sqrt(pow($penta-h, 2) - pow($proj-h, 2));
$rotunda-inf: $penta-inf*cos($penta-rax);
$rotunda-sup: $rotunda-h - $rotunda-inf;
$tri-inf-rax: 90deg - asin($rotunda-inf/$tri-h);
$tri-sup-rax: 90deg - asin($rotunda-sup/$tri-h);
@mixin sp($w, $h: $w) {
margin: if($w == $h, -$w/2, -$h/2 (-$w/2));
width: $w; height: $h;
}
@mixin gradme($n, $rc, $ac: 0deg, $ac2: 0, $rev: false) {
$bg: ();
@for $i from 0 to $n {
$a: $i*360deg/$n + $ac;
$au: if($rev, 360 - $a/1deg, $a/1deg);
$c: hsl($au + $ac2, 100%, 50%);
$bg: $bg, radial-gradient(circle at
$rc*(1 + cos($a)) $rc*(1 + sin($a)),
rgba($c, $of), rgba($c, 0) 1.25*$rc);
}
background: $bg;
background-position: 50% 50%;
background-size: 2*$rc 2*$rc;
}
html, body { height: 100%; }
html {
overflow: hidden;
}
body {
margin: 0;
perspective: 32em;
background: dimgrey;
color: $linecol;
}
.polyhedron, .polyhedron *, .polyhedron *:before {
box-sizing: border-box;
position: absolute;
top: 50%; left: 50%;
transform-style: preserve-3d;
}
.polyhedron--icosidodecahedron {
//transform: rotateX(85deg);
animation: ani 16s linear infinite;
}
/**/
@keyframes ani {
from { transform: rotate(0deg) rotateX(0deg); }
to { transform: rotate(360deg) rotateX(-720deg); }
}
.polyhedron--pentagonal-rotunda:first-child {
transform: translateY(-$rotunda-h/2);
}
.polyhedron--pentagonal-rotunda:last-child {
transform:
rotateY($deca-ca) scaleY(-1) translateY(-$rotunda-h/2);
}
.polyhedron__face {
overflow: hidden;
@include sp($l);
//backface-visibility: hidden;
*, &:before, *:before {
overflow: hidden;
margin: inherit;
width: inherit; height: inherit;
}
}
.polyhedron__face--triangle:before,
.penta__inner:before {
border: solid .0625em;
content: '';
}
.polyhedron__face--triangle {
border-top: solid .125em;
border-left: solid .125em;
&:before {
transform: scaleX(1/cos($tri-sa)) skewY(-$tri-sa)
rotate(-$tri-a/2) translateY(-50%);
}
}
.polyhedron__face--pentagon {
@include sp($penta-rhl);
transform: rotateY(2*$penta-ca)
translate3d(-.0625em, -$rotunda-h/2, $penta-ri)
rotateX(90deg)
rotate(-$penta-rha1/2)
skewY($penta-rhsa1) scaleX(cos($penta-rhsa1));
}
.penta__inner {
transform: scaleX(1/cos($penta-rhsa1)) skewY(-$penta-rhsa1)
rotate($penta-rha1/2)
translateY(-$penta-inf)
rotate($penta-rha2/2)
skewY($penta-rhsa2) scaleX(cos($penta-rhsa2));
&:before {
@include sp(2*$penta-rc, $penta-rc + $penta-ri);
transform: scaleX(1/cos($penta-rhsa2)) skewY(-$penta-rhsa2)
rotate(-$penta-rha2/2)
translateY($penta-inf - ($penta-rc + $penta-ri)/2);
background: rgba(247, 80, 103, $of);
@include gradme($penta-n, $penta-rc, -90deg, -72, true);
}
}
@for $i from 0 to 5 {
.polyhedron__face--triangle:nth-child(#{$i + 1}) {
transform: rotateY($i*$penta-ca)
translate3d(0, $rotunda-h/2 - .0625em, $deca-ri)
rotateX($tri-inf-rax)
rotate($tri-a/2)
skewY($tri-sa) scaleX(cos($tri-sa));
&:before {
background: linear-gradient(90deg,
hsla((2*$i + 1)*360/$deca-n, 100%, 50%, $of),
hsla(2*($i + 1)*360/$deca-n, 100%, 50%, $of));
}
}
.polyhedron__face--triangle:nth-child(#{$i + 6}) {
transform: rotateY($i*$penta-ca)
rotate(180deg)
translate3d(0, $rotunda-h/2, $penta-ri)
rotateX(-$tri-sup-rax)
rotate($tri-a/2)
skewY($tri-sa) scaleX(cos($tri-sa));
&:before {
background: linear-gradient(90deg,
hsla((2*$i + 1)*360/$deca-n, 100%, 50%, $of),
hsla(2*($i + 1)*360/$deca-n, 100%, 50%, $of));
}
}
.polyhedron__face--pentagon:nth-child(#{$i + 11}) {
transform: rotateY(($i + .5)*$penta-ca)
translate3d(0, $rotunda-h/2, $deca-ri)
rotateX($penta-rax)
rotate(-$penta-rha1/2)
skewY($penta-rhsa1) scaleX(cos($penta-rhsa1));
.penta__inner:before {
background: linear-gradient(90deg,
hsla(2*($i + 1)*360/$deca-n, 100%, 50%, $of),
hsla((2*$i + 3)*360/$deca-n, 100%, 50%, $of));
}
}
}

这是一个旋转的三十二面体(由二十个三角形的页和十二个五角形的面组成),在CSS中可以说是一个使用三角学完成的一个高级示例。如果你为此感到头疼,你可以先看看Mason Wendell写的文本阴影的案例。Mason Wendell使用了Compass的sin()和cos()函数来实现CSS的阴影重叠的事情。
我对三角函数并不太了解。但有时候三角函数对我来说并不够用,特别是在CSS中写2D和3D的案例时,我发现需要使用正弦,余弦和正切来计算一些值。我需要asin()、acos()和atan()函数。不幸的是,Compass并不提供这些函数功能,所以我有下面两个选择:
那我肯定会选择第二条了。
幸运的我,偶然发现了一篇使用Sass写的正弦和余弦函数功能的文章。我认为使用同样的方法,也能实现我需要的功能。
先别深入,我们先回过头来回忆一下高中数学知识。

这个图看起来有点熟悉。
根据上面的直角三角形,我们一起来回顾几个公式:
α+β=90°
用弧度来表示是这样的:

大家可能还记得,毕达哥拉斯定理(Pythagorean theorem)告诉我们:

还有一些基本的三角函数是这样定义的:
sin(α) = a/c
cos(α) = b/c
tan(α) = a/b
sin(β) = b/c
cos(β) = a/c
tan(β) = b/a
知道这些,我们就可以推导出其他的一些公式:

头晕了?保持清醒的头脑继续往下。
什么是反正弦呢?好吧,如果:
sin(α)=z
那么反正弦是逆着的:
arcsin(z)=α
换句话说,给了一个角度的正弦,通过反正弦可以告诉你这个角度值。反余弦和反正切类似于在一个余弦或正切基础给了一个角度。
我们将用Sass来制作一个反正弦asin()的函数。我们将用级数来展开这一点。如果你不是一个数学奇才,泰勒级数展开是相当复杂的。我会尽我所能向大家介绍。对于反正弦,他看起来像这样:

看到这个公式就吓坏了。让我们来分析这个结构:z是我们想要得到的α角的正弦。整个和是α的弧度值。当总和在[−π/2,π/2]之间时,z应该在[-1,1]之间。
每一个标签,包括第一个,你都可以写成(1)⋅z,它是被编造出来的两部分;第一部分是毕达哥拉斯定理内的部分,第二部分是毕达哥拉斯定理以外的部分。
对于每个i标签是第一位的,第一部分是(2i−1)/(2i),第二部分中的分子部分是z的2i+1幂次方,而分母是2i+1。
这可能会得到一个无限大的数,但是一旦得到期限值,对于条款中的值可能会变得非常小,小得我们甚至可以忽略他不计,而且还是安全的,不会影响其他任何东西。
但我们应该从哪个地方开始停下来呢?比方说,在一个范围的十分之一。以弧度1度的值来计算,1度 = π/180 ≈ 3.14 / 180 ≈ .0175。所以这里的十分之一就是“.00175” 。所以当值小于“.00175”时,他就会停下来,就像我们起床的闹钟,到这个点就会停下来。
我们一起来看两示例。
z=0时,这个简单,因为所有条款都是0,所以根据级数展开,无单位的弧度值是0和度值是0⋅180°/π=0°。
z=1时,第一项是1,第二项是1/6=.167,第三项是3/40=0.075,第四项是.045,第五项是.030,第六项是.022,第七项是.017,第八项是.014。我们注意到我们有一个问题,每一项明显有下降,但这种放慢真的非常缓慢,离我们停下来的临界点.00175还好远。
但离我们需要的真正值有多远呢?我们把所有项做了一个总结,如下:
1+.167+.075+.045+.030+.022+.017+.014=1.496
这个弧度值转换成度值是85°。离正确的90°并不是太远,但现在变得越来越难接近准确值。这将会导致更多的循环和较慢。它的一个问题是,虽然在特定的情况下影响的程序较小,我们也应该保证每种情况下的绝对值在[0,π/2]上半部分。
我们要怎样解决这个问题呢?首先要检查产生的角度绝对值是否大于π/4,如果是,我们采用这种方法π/2−|α|计算他的补差。由于正弦函数在[0,π/2]之间是一个递增函数,所以我们要检查z的绝对值是否大于sin(π/4)。
但是我们怎么知道正弦函数中的补差值中α的绝对值呢?嗯,他等于余弦的α的绝对值:sin(π/2−|α|)=cos(|α|)。根据相关公式,我们可以得知:

哎,好多数学公式呀!让我们来看看一些代码。
首先给所有项的总和设置一个默认的阈值:
$default-threshold: pi() / 180 / 10;
pi()函数是Compass内置的一个数学函数,其返回的值就是一个π值。
然后我们开始写我们的函数:
@function asin($z) {
$sum: 0;
//abs(),Sass内置函数,返回一个数的绝对值
//sin(),Compass内置函数,返回一个角度的正弦值
//sqrt(),Compass内置函数,返回一个数的平方根
//pow(),Compass内置函数,返回一个数的幂值,如pow($z,2)返回的是$z的二次方
@if abs($z) > sin(pi() / 4) {
$z: sqrt(1 - pow($z, 2));
}
@return $sum;
}
我们设计总和$sum的初始值为0,如果我们角度的绝对值大于pi()/4时,我们要确保计算他的补差。但在那之后,我们怎么知道我们已计算了补差呢?是切换到初始角度吗?
为了更好的跟踪,我引入了一个布尔变量$complement,并且定义其初始值为false,但在@if区块内通过true来切换返回的值。默认之下返回的是$sum值,如果检测到$complement变量值为true时,则返回pi()/2 - $sum。
@function asin($z) {
$sum: 0;
$complement: false;
@if abs($z) > sin(pi() / 4) {
$complement: true;
$z: sqrt(1 - pow($z, 2));
}
//Miscellaneous函数,当$complement为true,返回pi()/2 - $sum,反之返回$sum
@return if($complement, pi() / 2 - $sum, $sum);
}
但这只适合正值,所以我们要引入变量$sign,它的值可以是1或者-1。我们还让$z值等于他的绝对值,并且让$sign值等于$z除以他自身的绝对值。代码变成:
@function asin($z) {
$sum: 0;
$complement: false;
$sign: $z / abs($z);
$z: abs($z);
@if $z > sin(pi() / 4) {
$complement: true;
$z: sqrt(1 - pow($z, 2));
}
@return $sign * (if($complement, pi() / 2 - $sum, $sum));
}
现在,让实际项目加起来得到总和,一旦这个这个总和的值比我们传递给函数的阈值小,我们设置他停止下来。首先设置第一个项$term,其值等于$z,并且放在循环语句@while的前面:
@function asin($z, $threshold: $default-threshold) {
$sum: 0;
$complement: false;
$sign: $z / abs($z);
$z: abs($z);
@if $z > sin(pi() / 4) {
$complement: true;
$z: sqrt(1 - pow($z, 2));
}
$term: $z;
@while $term > $threshold {
$sum: $sum + $term;
}
@return $sign * (if($complement, pi() / 2 - $sum, $sum));
}
在这一点上,除非我们的$term值小于$threshold值,不然@while会一直循环计算,因为我们在里面没有改变$term值。因此,每一次迭代计算机都会计算一次。为了做到这一点,我们在循环前初始化两个变量。一个是$i,即当前项索引;另一个是$k,毕达哥拉斯定理里面的那部分。在此之后,我们在循环之内,不断递增$i的值和重新计算$k和$term的值:
@function asin($z, $threshold: $default-threshold) {
$sum: 0;
$complement: false;
$sign: $z / abs($z);
$z: abs($z);
@if $z > sin(pi() / 4) {
$complement: true;
$z: sqrt(1 - pow($z, 2));
}
$term: $z;
$i: 0;
$k: 1;
@while $term > $threshold {
$sum: $sum + $term;
$i: $i + 1;
$k: $k * (2 * $i - 1) / (2 * $i);
$j: 2 * $i + 1;
$term: $k * pow($z, $j) / $j;
}
@return $sign * (if($complement, pi() / 2 - $sum, $sum));
}
到此就完成了,在Sass中我们有了自己的asin()函数功能。
我们是否有什么方法可以检测出abs($z) <= 1,如果他返回的是一个false,抛出一个错误。因为在这种情况之下,$term不会在$threshold值内返回false,我们的循环就是一个无限循环。
现在我们有一个函数来计算反正弦,在此基础上,我们可以很容易写出反余弦函数acos()。事实上,在此示例中,角度α的值都是在[0,π]之间,不难得出cos(α)=sin(π / 2−α)。如果我们知道cos(α)=z,那么arcsin(z)=π/2−α,如此一来,给我们提供α=π/2−arcsin(z)。
@function acos($z, $threshold: $default-threshold) {
@return pi() / 2 - asin($z, $threshold);
}
对于 atan() 函数,我们从这样的事实开始 tan(α)=sin(α)/cos(α)。我们也知道:

也知道:

根据这样的关系,我们可以得出正弦与正切之间的关系:

我们也知道 tan(α)=z 所以:

将上面公式简化一下:

因此得出我们的反正切 atan() 函数:
@function atan($z, $threshold: $default-threshold) {
@return asin($z / sqrt(1 + pow($z, 2)), $threshold);
}
请记住,这些函数返回的值是一个无单位的弧度值。在我们CSS中是无法直接使用这些值,因为在CSS中至少需要有一个1rad或者带有单位的换算值。但是,如果我们在调用函数时,能指定单位呢?如:
transform: rotate(asin(.5, 'deg'));
为了做到这一点,我们需要一个能转换角度的函数,它能接受一个无单位的弧度值,并且它能将其转换成我们指定的单位,如:
$in-degrees: convert-angle(pi() / 4, 'deg');
$in-turns: convert-angle(pi() / 2, turn); // 让单位带不带引号都能工作
首先我们创建无单位的弧度与 CSS 单位之间的换算系数表。在 Sass 中,我们可以使用 Map 功能:
$factors: (
rad: 1rad,
deg: 180deg / pi(),
turn: .5turn / pi(),
grad: 200grad / pi()
);
这样,我们只需要使用无单位的弧度值乘以系数值,也就是:
@function convert-angle($value, $unit-name) {
$factors: (
rad: 1rad,
deg: 180deg / pi(),
turn: .5turn / pi(),
grad: 200grad / pi()
);
@return $value * map-get($factors, $unit-name);
}
这个功能是失败的,如果 $unit-name 不是 $factors 列表中对应的关键词,或者 $value 已经有单位了,在 CSS 中无效的。因此我们需要完善此函数功能:
@function convert-angle($value, $unit-name) {
$factors: (
rad: 1rad,
deg: 180deg / pi(),
grad: 200grad / pi(),
turn: .5turn / pi()
);
@if not unitless($value) {
@warn '`#{$value}` should be unitless';
@return false;
}
@if not map-has-key($factors, $unit-name) {
@warn 'unit `#{$unit-name}` is not a valid unit - please make sure it is either `deg`, `rad`, `grad` or `turn`';
@return false;
}
@return $value*map-get($factors, $unit-name);
}
现在我们只需要完善我们的反三角函数具有单位换算功能:
@function asin($z, $unit-name: deg, $threshold: $default-threshold) {
$sum: 0;
$complement: false;
$sign: $z / abs($z);
$z: abs($z);
@if $z > sin(pi() / 4) {
$complement: true;
$z: sqrt(1 - pow($z, 2));
}
$term: $z;
$i: 0;
$k: 1;
@while $term > $threshold {
$sum: $sum + $term;
$i: $i + 1;
$k: $k * (2 * $i - 1) / (2 * $i);
$j: 2 * $i + 1;
$term: $k * pow($z, $j) / $j;
}
@return convert-angle($sign*(if($complement, pi()/2 - $sum, $sum)), $unit-name);
}
@function acos($z, $unit-name: deg, $threshold: $default-threshold) {
@return convert-angle(pi()/2, $unit-name) - asin($z, $unit-name, $threshold);
}
@function atan($z, $unit-name: deg, $threshold: $default-threshold) {
@return asin($z/sqrt(1 + pow($z, 2)), $unit-name, $threshold);
}
我将度 deg 设置为默认单位,因为这可以是大多数人比较了解和会使用的,并且将其放置在 $threshold 前面,因为它仍然有可能有人可能会改变单位。
如果你能跟着看到这里,你是一个真正的爱学习的人。最后的功能如下:
$default-threshold: pi() / 180 / 20;
@function convert-angle($value, $unit-name) {
$factors: (
rad: 1rad,
deg: 180deg / pi(),
grad: 200grad / pi(),
turn: .5turn / pi()
);
@if not unitless($value) {
@warn '`#{$value}` should be unitless';
@return false;
}
@if not map-has-key($factors, $unit-name) {
@warn 'unit `#{$unit-name}` is not a valid unit - please make sure it is either `deg`, `rad`, `grad` or `turn`';
@return false;
}
@return $value*map-get($factors, $unit-name);
}
@function asin($z, $unit-name: deg, $threshold: $default-threshold) {
$sum: 0;
$complement: false;
$sign: if($z != 0, $z / abs($z), 1);
$z: abs($z);
@if $z > 1 {
@warn 'illegal `#{$z}` value for function';
@return false;
}
@if $z > sin(pi() / 4) {
$complement: true;
$z: sqrt(1 - pow($z, 2));
}
$term: $z;
$i: 0;
$k: 1;
@while $term > $threshold {
$sum: $sum + $term;
$i: $i + 1;
$k: $k * (2 * $i - 1) / (2*$i);
$j: 2 * $i + 1;
$term: $k * pow($z, $j) / $j;
}
@return convert-angle($sign * (if($complement, pi() / 2 - $sum, $sum)), $unit-name);
}
@function acos($z, $unit-name: deg, $threshold: $default-threshold) {
@return convert-angle(pi() / 2, $unit-name) - asin($z, $unit-name, $threshold);
}
@function atan($z, $unit-name: deg, $threshold: $default-threshold) {
@return asin($z/sqrt(1 + pow($z, 2)), $unit-name, $threshold);
}
在结束这篇文章时,我在 Codepen 放了两个案例,以激发你的学习激情:
.polyhedron.polyhedron--rhombicosidodecahedron
.polyhedron__comp.polyhedron--dodecahedron
-(1..12).each do |i|
.polyhedron__face.polyhedron--dodecahedron__face
.transformer.scaler-shifter
.polygon.polygon--pentagon
.polygon--pentagon__inner
.polyhedron__comp.polyhedron--icosahedron
-(1..20).each do |i|
.polyhedron__face.polyhedron--icosahedron__face
.transformer.scaler-shifter
.polygon.polygon--triangle
.polyhedron__comp.stretchers
-(1..30).each do |i|
.polyhedron__face.stretcher__face
.transformer.stretcher
.polygon.polygon--rect
@import 'compass/css3';
$l: 6.5em;
$shades: #490a3d #e97f02 #8a9b0f;
$of: .32;
$est: 9s;
@function central-angle($n) {
@return 360deg/$n;
}
@function in-angle($n) {
@return 180deg*($n - 2)/$n;
}
@function skew-angle($in-angle) {
@return abs(90deg - $in-angle)
}
@function inradius($n, $l) {
@return ($l/2)/tan(central-angle($n)/2);
}
@function circumradius($n, $l) {
@return ($l/2)/sin(central-angle($n)/2);
}
$tri-n: 3;
$tri-ca: central-angle($tri-n);
$tri-a: in-angle($tri-n);
$tri-sa: skew-angle($tri-a);
$tri-h: $l*sin($tri-a);
$tri-ri: inradius($tri-n, $l);
$tri-rc: circumradius($tri-n, $l);
$penta-n: 5;
$penta-ca: central-angle($penta-n);
$penta-a: in-angle($penta-n);
$penta-sa: skew-angle($penta-n);
$penta-ri: inradius($penta-n, $l);
$penta-rc: circumradius($penta-n, $l);
$penta-h: $penta-ri + $penta-rc;
$penta-rhl: $l + $l/2/sin(90deg - (180deg - $penta-a));
$penta-rha1: $penta-a;
$penta-rhsa1: abs(90deg - $penta-rha1);
$penta-rha2: 180deg - 2*(180deg - $penta-a);
$penta-rhsa2: abs(90deg - $penta-rha2);
$penta-sup: $l*cos($penta-a/2);
$penta-inf: $penta-rhl*cos($penta-a/2) - $penta-sup;
$quad-n: 4;
$quad-ca: central-angle($quad-n);
$quad-a: in-angle($quad-n);
$quad-ri: inradius($quad-n, $l);
$quad-rc: circumradius($quad-n, $l);
$dodeca-n: 12;
$dodeca-da: acos(-1/sqrt(5));
$dodeca-rax: abs(90deg - $dodeca-da);
$dodeca-ri: $l*sqrt(5/2 + 11*sqrt(5)/10)/2;
$dodeca-rc: $l*sqrt(3)*(1 + sqrt(5))/4;
$dodeca-rm: $l*(sqrt(5) + 3)/4;
$dodeca-hlp: $penta-inf*cos($dodeca-rax);
$dodeca-hsp: $penta-sup*cos($dodeca-rax);
$dodeca-erax1: $dodeca-da/2;
$dodeca-erax2: acos($dodeca-hlp/$l);
$dodeca-eraz3: .96*atan($dodeca-hsp/($l*sin($penta-a/2)));
$icosa-n: 20;
$icosa-da: acos(sqrt(5)/3) + 90deg;
$icosa-rax2: asin(($penta-rc - $penta-ri)/$tri-h);
$icosa-rax1: 90deg - acos($penta-ri/$tri-h);
$icosa-ri: sqrt(3)*(sqrt(5) + 3)/12*$l;
$icosa-rc: $l*sqrt(2*sqrt(5) + 10)/4;
$icosa-rm: $l*(sqrt(5) + 1)/4;
$stretch-n: 30;
$rhombicosidodeca-rc: $l*sqrt(11 + 4*sqrt(5))/2;
$rhombicosidodeca-ri3: sqrt(pow($rhombicosidodeca-rc, 2) - pow($tri-rc, 2));
$rhombicosidodeca-ri5: sqrt(pow($rhombicosidodeca-rc, 2) - pow($penta-rc, 2));
$rhombicosidodeca-ri4: sqrt(pow($rhombicosidodeca-rc, 2) - pow($quad-rc, 2));
@mixin sp($w, $h: $w) {
margin: if($h != $w, -$h/2, unquote('')) -$w/2;
width: $w; height: $h;
}
html, body { height: 100%; }
body {
overflow: hidden;
margin: 0;
perspective: 32em;
perspective-origin: calc(50% - #{$l/4}) 50%;
background: linear-gradient(black, dimgrey);
color: lemonchiffon;
}
.polyhedron, .polyhedron *,
.polyhedron :before, .polyhedron :after {
box-sizing: border-box;
position: absolute;
top: 50%; left: 50%;
transform-style: preserve-3d;
}
.polyhedron { animation: rot 28s linear infinite; }
@keyframes rot { to { transform: rotateY(360deg); } }
.polyhedron {
&__face { backface-visibility: hidden; }
&--dodecahedron__face {
@for $i from 0 to $dodeca-n {
&:nth-child(#{$i + 1}) {
$j: floor($i/($dodeca-n/2));
$aa: (pow(-1, $j) - 1)*($penta-ca/4);
transform:
if($j !=0, rotate(180deg), ())
if($i%($dodeca-n/2) == 5,
rotateY(180deg + $aa) rotateX(-90deg),
rotateY($i*$penta-ca + $aa) rotateX(-$dodeca-rax));
.polygon :before {
$k: $i%($dodeca-n/2);
$c1: hsla($k*360/$penta-n, 100%, 60%, $of/4);
$c2: hsla(($k + 1)*360/$penta-n, 100%, 60%, $of/4);
background-image: linear-gradient(90deg, $c1, $c2);
}
}
}
}
&--icosahedron__face {
@for $i from 0 to $icosa-n {
&:nth-child(#{$i + 1}) {
$j: floor($i/($icosa-n/2));
$k: floor($i/$penta-n);
$aa: (pow(-1, $k + 1) - 1)*($penta-ca/4);
transform:
if($k%2 != 0, rotate(180deg), ())
rotateY($i*$penta-ca + $aa)
rotateX(if(($j + $k)%2 == 0,
-$icosa-rax1,
-$icosa-rax2))
if(($j + $k)%2 == 0, rotate(180deg), ());
.polygon:before {
$p: $i%($penta-n);
$c1: hsla($p*360/$penta-n, 100%, 60%, $of/4);
$c2: hsla(($p + 1)*360/$penta-n, 100%, 60%, $of/4);
background-image: linear-gradient(90deg, $c1, $c2);
background: rgba(nth($shades, 2), .15);
}
}
}
}
}
.stretcher__face {
@for $i from 0 to $stretch-n {
&:nth-child(#{$i + 1}) {
$j: floor($i/($stretch-n/2));
$j1: $i%($stretch-n/2);
$k: floor($j1/$penta-n);
$p: $i%$penta-n;
$ax: if($k == 0, $dodeca-erax1, $dodeca-erax2);
$ay: if($k == 1, $penta-ca/2, pow(-1, $j)*$penta-ca/4);
$az: if($k == 1, 90deg, pow(-1, $j + 1)*$dodeca-eraz3);
transform:
if($j != 0, rotate(180deg), ())
rotateY(($p - .5 + $j*.5)*$penta-ca +
if($k == 0, 0deg, $ay))
if($k < 2, rotateX($ax), ())
if($k == 0, (), rotate($az));
&:before {
$c1: hsla($p*360/$penta-n, 100%, 60%, $of/4);
$c2: hsla(($p + 1)*360/$penta-n, 100%, 60%, $of/4);
background-image: linear-gradient(90deg, $c1, $c2);
}
}
}
}
.transformer {
//backface-visivility: hidden;
animation: ani $est linear infinite alternate;
.polyhedron--dodecahedron & {
animation-name: ani-penta;
}
.polyhedron--icosahedron & {
animation-name: ani-tri;
}
.stretchers & {
animation-name: ani-rect;
}
}
@keyframes ani-penta {
0%, 12.5% {
transform: translateZ($dodeca-ri) scale(1);
}
37.5%, 62.5% {
transform: translateZ($rhombicosidodeca-ri5) scale(1);
}
87.5%, 100% {
transform: translateZ($icosa-rc) scale(.001);
}
}
@keyframes ani-tri {
0%, 12.5% {
transform: translateZ($dodeca-rc) scale(.001);
}
37.5%, 62.5% {
transform: translateZ($rhombicosidodeca-ri3) scale(1);
}
87.5%, 100% {
transform: translateZ($icosa-ri) scale(1);
}
}
@keyframes ani-rect {
0%, 12.5% {
transform: translateZ($dodeca-rm) scale(1, .001);
}
37.5%, 62.5% {
transform: translateZ($rhombicosidodeca-ri4) scale(1);
}
87.5%, 100% {
transform: translateZ($icosa-rm) scale(.001, 1);
}
}
.polygon {
@include sp($l);
&, * { overflow: hidden; }
*, &:before, &:after, :before, :after {
margin: inherit;
width: inherit; height: inherit;
content: '';
}
&--pentagon {
@include sp($penta-rhl);
transform: translateY($penta-ri) rotate(-$penta-rha1/2)
skewY($penta-rhsa1) scaleX(cos($penta-rhsa1));
&__inner {
@include sp($penta-rhl);
transform: scaleX(1/cos($penta-rhsa1)) skewY(-$penta-rhsa1)
rotate($penta-rha1/2)
translateY(-$penta-inf)
rotate($penta-rha2/2)
skewY($penta-rhsa2) scaleX(cos($penta-rhsa2));
&:before {
border: solid .125em;
transform: scaleX(1/cos($penta-rhsa2))
skewY(-$penta-rhsa2)
rotate(-$penta-rha2/2)
translateY($penta-inf - $penta-rhl/2);
background: rgba(nth($shades, 1), $of);
}
&:after {
background-image:
linear-gradient(0deg,
lemonchiffon .125em, transparent.125em),
linear-gradient(-90deg,
lemonchiffon .125em, transparent.125em);
background-position: 0 0;
background-repeat: no-repeat;
background-size: $l 100%, 100% $l;
}
}
&:after {
background-image:
linear-gradient(
lemonchiffon .125em, transparent.125em),
linear-gradient(-90deg,
lemonchiffon .125em, transparent.125em);
background-position: 100% 0;
background-repeat: no-repeat;
background-size: $l 100%, 100% $l;
}
}
&--triangle {
border: solid .125em;
border-right-color: transparent;
border-bottom-color: transparent;
transform: translateY($tri-ri) rotate($tri-a/2)
skewY($tri-sa) scaleX(cos($tri-sa));
&:before {
border: solid .125em;
transform: scaleX(1/cos($tri-sa)) skewY(-$tri-sa)
rotate(-$tri-a/2) translateY(-50%);
background: rgba(nth($shades, 2), $of);
}
}
&--rect:before {
border: solid .125em;
background: rgba(nth($shades, 3), $of);
}
}
