您当前的位置:首页 > 计算机 > 编程开发 > Html+Div+Css(前端)

Sass 中的反三角函数

时间:12-14来源:作者:点击数:

有可能你会认为数学在 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写的正弦和余弦函数功能的文章。我认为使用同样的方法,也能实现我需要的功能。

三角 101(Trigonometry 101)

先别深入,我们先回过头来回忆一下高中数学知识。

Sass中的反三角函数

这个图看起来有点熟悉。

根据上面的直角三角形,我们一起来回顾几个公式:

α+β=90°

用弧度来表示是这样的:

Sass中的反三角函数

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

Sass中的反三角函数

还有一些基本的三角函数是这样定义的:

sin(α) = a/c
cos(α) = b/c
tan(α) = a/b
sin(β) = b/c
cos(β) = a/c
tan(β) = b/a

知道这些,我们就可以推导出其他的一些公式:

Sass中的反三角函数

头晕了?保持清醒的头脑继续往下。

反正弦函数

什么是反正弦呢?好吧,如果:

sin(α)=z

那么反正弦是逆着的:

arcsin(z)=α

换句话说,给了一个角度的正弦,通过反正弦可以告诉你这个角度值。反余弦和反正切类似于在一个余弦或正切基础给了一个角度。

我们将用Sass来制作一个反正弦asin()的函数。我们将用级数来展开这一点。如果你不是一个数学奇才,泰勒级数展开是相当复杂的。我会尽我所能向大家介绍。对于反正弦,他看起来像这样:

Sass中的反三角函数

看到这个公式就吓坏了。让我们来分析这个结构:z是我们想要得到的α角的正弦。整个和是α的弧度值。当总和在[−π/2,π/2]之间时,z应该在[-1,1]之间。

每一个标签,包括第一个,你都可以写成(1)⋅z,它是被编造出来的两部分;第一部分是毕达哥拉斯定理内的部分,第二部分是毕达哥拉斯定理以外的部分。

对于每个i标签是第一位的,第一部分是(2i−1)/(2i),第二部分中的分子部分是z2i+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(|α|)。根据相关公式,我们可以得知:

Sass中的反三角函数

编写 asin() 函数

哎,好多数学公式呀!让我们来看看一些代码。

首先给所有项的总和设置一个默认的阈值:

$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() 函数

现在我们有一个函数来计算反正弦,在此基础上,我们可以很容易写出反余弦函数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() 函数

对于 atan() 函数,我们从这样的事实开始 tan(α)=sin(α)/cos(α)。我们也知道:

Sass中的反三角函数

也知道:

Sass中的反三角函数

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

Sass中的反三角函数

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

Sass中的反三角函数

将上面公式简化一下:

Sass中的反三角函数

因此得出我们的反正切 atan() 函数:

@function atan($z, $threshold: $default-threshold) {
  @return asin($z / sqrt(1 + pow($z, 2)), $threshold);
}

将这些函数功能更好的用于 CSS 中

请记住,这些函数返回的值是一个无单位的弧度值。在我们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 放了两个案例,以激发你的学习激情:

  • A regular dodecahedron expanding into an icosidodecahedron and then collapsing into an icosahedron
    .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);
      }
    }
  • A stretchy graphical mesh
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐