幂函数

Avatar of Kitty Giraudel
Kitty Giraudel

虽然 Sass 在算术运算方面非常有用,但在数学辅助函数方面略显不足。在官方仓库中,已经有一个关于请求更多与数学相关的函数的开放性问题 存在了将近 3 年。

一些第三方供应商,如CompassSassyMath,提供了对数学功能的高级支持,但它们是外部依赖项,应该(最好)避免。

关于此问题,最受欢迎的请求之一是幂函数,甚至是一个指数运算符。不幸的是,Sass 仍然不支持此功能,尽管它仍在积极讨论中,但短期内不太可能实现。

与此同时,能够将一个数字提升到某个幂在 CSS 中非常有用。以下是一些它可以派上用场的例子:

Sass 实现

幸运的是,我们只需使用 Sass 就可以创建幂函数(这非常简单)。我们只需要一个循环和一些基本的数学运算符(如 */)。

正整数指数

我们函数(命名为 pow)的最简形式如下所示:

@function pow($number, $exponent) {
  $value: 1;

  @if $exponent > 0 {
    @for $i from 1 through $exponent {
      $value: $value * $number;
    }
  }

  @return $value;
}

这是一个例子:

.foo {
  width: pow(20, 2) * 1px; // 400px
}

正整数或负整数指数

但是它只适用于正数的 $power 参数。允许负指数并不难,我们只需要一个小的额外条件:

@function pow($number, $exponent) {
  $value: 1;

  @if $exponent > 0 {
    @for $i from 1 through $exponent {
      $value: $value * $number;
    }
  } @else if $exponent < 0 {
    @for $i from 1 through -$exponent {
      $value: $value / $number;
    }
  }

  @return $value;
}

这是一个例子:

.foo {
  width: pow(10, -2) * 1px; // 0.0001px
}

正数或负数指数

现在,如果我们想要非整数指数呢?例如 4.2?事实是,这真的不容易。它仍然可行,但需要不仅仅是循环和几个运算。

这已经在Bourbon 仓库中完成,以便完善框架中的 modular-scale(..) 函数(尽管已被拒绝)。代码如下:

@function pow($number, $exponent) {
  @if (round($exponent) != $exponent) {
    @return exp($exponent * ln($number));
  }
    
  $value: 1;
  
  @if $exponent > 0 {
    @for $i from 1 through $exponent {
     $value: $value * $number;
    }
  } @else if $exponent < 0 {
    @for $i from 1 through -$exponent {
      $value: $value / $number;
    }
  }

  @return $value;
}

@function factorial($value) {
  $result: 1;

  @if $value == 0 {
    @return $result;
  }
  
  @for $index from 1 through $value {
    $result: $result * $index;
  }
  
  @return $result;
}

@function summation($iteratee, $input, $initial: 0, $limit: 100) {
  $sum: 0;
  
  @for $index from $initial to $limit {
    $sum: $sum + call($iteratee, $input, $index);
  }
  
  @return $sum;
}

@function exp-maclaurin($x, $n) {
  @return (pow($x, $n) / factorial($n));
}

@function exp($value) {
  @return summation('exp-maclaurin', $value, 0, 100);
}

@function ln-maclaurin($x, $n) {
  @return (pow(-1, $n + 1) / $n) * (pow($x - 1, $n));
}

@function ln($value) {
  $ten-exp: 1;
  $ln-ten: 2.30258509;
  
  @while ($value > pow(10, $ten-exp)) {
    $ten-exp: $ten-exp + 1;
  }
  
  @return summation(ln-maclaurin, $value / pow(10, $ten-exp), 1, 100) + $ten-exp * $ln-ten;
}

进一步的考虑

好吧,这很复杂。如果你碰巧需要对非整数指数(如 4.2)的支持,我建议你使用提供此功能的外部依赖项(如sass-math-pow),而不是将所有这些代码包含在你的项目中。这本身并不是一件坏事,但你的项目并不真正负责托管如此大量的非作者编写的 polyfill 代码(这就是我们使用包管理器的理由)。

另请注意,所有这些操作对于像 Sass 这样薄的一层来说都非常密集。此时,如果你的设计依赖于高级数学函数,那么最好通过插件系统(Eyeglass、Ruby gem 等)将这些辅助函数的实现从上层(Node.js、Ruby 等)传递到 Sass。

但是对于基本的 pow(..) 用法,我强烈推荐简单的版本!