这是由一个瞎折腾的事件引发的,起因是这样的, 我想在web里画一个等边三角形,当然做法有好几种,canvas、svg、css…… 都可以,我在比较哪种方法更对我的路,当然是纯css代码搞定就很好,查了好多资料,其中最通常的就是用块对象的大粗边,这种做法已经不稀奇了,例如下面的例子:
.triangle{
width: 0; height: 0;
border-width: 50px;
border-style: solid;
border-color: blue red yellow black;
}
做出来就下面那样,要哪个角度,就将其他的颜色设成transparent就行了。
当然,在CSS3中还有种新的方法叫clip-path
的,可以绘制多边形,用法非常灵活,但是去了 caniuse网站查了下,支持度貌似还很不够,不过是个不错的方法,例如下面的代码:
.polygon {
width: 100px;
height: 100px;
background: #f00;
-webkit-clip-path:polygon(50px 0, 100px 37px, 82px 100px, 19px 100px, 0 37px);
}
(在查阅文档的过程中,一个CSS3的新属性shape-outside
也蛮有意思的,这个以后再说……)
看到下面的五边形了吗?没有?那你的浏览器蛮渣的,呵呵~~
当然,这个等边五边形五个顶点的坐标为啥是这些数字,拜托,就是用三角函数算出来的,这不,咱快切入正题了…………
下面来道中学数学题:等边三角形高为y,求边长是多少?
用js的语法来表达,边长的长度x = Math.tan(30 * Math.PI / 180) * y * 2
( JS里三角函数用的是弧度,要把角度转换一下才能计算 ), 问题解决了,我用该方法根据给定的高度,就能算出在一个矩形里用polygon画等边三角形路径时3个点得坐标了:polygon(0.5x 0, x y, 0 y)
, y给定后,x的值通过以上方法就可以得出啦!
其实这么简单的事情,是不值得太高兴的,问题来了,如果我想在 Sass 中要将这个东西封装一下,写一个mixin,传入高度,立刻给我生成一个等边三角形的CSS代码,那就省力了吧,嘿嘿,实现不了!
原因很简单,在Sass中,能够进行简单的加减乘除四则运算,但是没有内建的类似JS的Math.sin()
或Math.cos()
等三角函数的计算方法,傻眼了吧,没有三角函数,就没法做上面的事儿啊!
数值方法 (NUMERICAL METHODS)
问题总是可以解决的,但是我的大部分数学知识早就灰飞烟灭了,如果你掌握了那啥 数值分析, 那啥 CORDIC算法,那啥 Chebyshev多项式,或者那啥 极值逼近算法,这就不是问题啦!天哪,我在上面写了点啥?所以啊,我复制、粘贴了 Taylor展开式来实现咱们的sin()
或者cos()
计算:
哦,天那,上面又是啥玩意儿?习惯一下,随便看两眼…… 不管如何,我们要依靠上面的算法来解决问题啊亲!所以我们需要把以上的算法翻译成Sass能认的语法。
乘方 (POWER)
上面的 Taylor展开式中,我们需要对x2n+1和x2n进行展开操作,但是在现有的Sass中没有这种运算方法,所以我们先先写个 power function:
将“火星语”翻译成Sass能理解的语法:
@function pow($number, $exp) {
$value: 1;
@if $exp > 0 {
@for $i from 1 through $exp {
$value: $value * $number;
}
}
@else if $exp < 0 {
@for $i from 1 through -$exp {
$value: $value / $number;
}
}
@return $value;
}
阶乘 (FACTORIAL)
表达式(2n+1)!和(2n)!需要一个阶乘算法的函数,不过这要比上面简单的多:
同样,将算法转换成Sass的语法:
@function fact($number) {
$value: 1;
@if $number > 0 {
@for $i from 1 through $number {
$value: $value * $i;
}
}
@return $value;
}
正弦、余弦和正切 (SINES, COSINES AND TANGENTS)
现在,必要的算法工具已准备妥当,可以创建我们的三角函数算法了,让我们跟随着 Taylor展开式的脚步,奔跑吧,兄弟!
@function pi() {
@return 3.14159265359;
}
@function rad($angle) {
$unit: unit($angle);
$unitless: $angle / ($angle * 0 + 1);
// If the angle has 'deg' as unit, convert to radians.
@if $unit == deg {
$unitless: $unitless / 180 * pi();
}
@return $unitless;
}
@function sin($angle) {
$sin: 0;
$angle: rad($angle);
// Iterate a bunch of times.
@for $i from 0 through 10 {
$sin: $sin + pow(-1, $i) * pow($angle, (2 * $i + 1)) / fact(2 * $i + 1);
}
@return $sin;
}
@function cos($angle) {
$cos: 0;
$angle: rad($angle);
// Iterate a bunch of times.
@for $i from 0 through 10 {
$cos: $cos + pow(-1, $i) * pow($angle, 2 * $i) / fact(2 * $i);
}
@return $cos;
}
@function tan($angle) {
@return sin($angle) / cos($angle);
}
看看效果如何,杠杠大!
@debug sin(pi()/4); // => 0.70711
@debug cos(45deg); // => 0.70711
Yeah! Give me five!
有了以上利器,我刚才说的在Sass中写等边三角形,简直就易如反掌了啊!
注:这里我在scss里把私有前缀省略了,建议编译的时候使用 autoprefixer 等 POSTCSS 工具帮助你自动添加前缀,在scss中,我们只写标准css语法
@mixin equ-triangle($height){
$h: $height;
$w: tan(30deg) * 2 * $h;
width: $w;
height: $h;
clip-path: polygon($w/2 0, $w $h, 0 $h);
}
.my-triangle {
@include equ-triangle(100px);
background: #f00;
}
编译出来的CSS代码如下:
.my-triangle{
width:115.47005px;
height:100px;
background:#f00;
-webkit-clip-path:polygon(57.73503px 0, 115.47005px 100px, 0 100px);
-moz-clip-path:polygon(57.73503px 0, 115.47005px 100px, 0 100px);
-ms-clip-path:polygon(57.73503px 0, 115.47005px 100px, 0 100px);
clip-path:polygon(57.73503px 0, 115.47005px 100px, 0 100px);
(啥,你又看不到?放着高大上的Chrome不用,那怪谁?)
补充
利用以上计算出来的结果,我们用内联svg的语法来画一个等边三角形试试:
<svg width="115.47" height="100">
<polygon points="57.73 0, 115.47 100, 0 100" fill="red"></polygon>
</svg>
当然啦,我们上面辛苦的来的_Sass function_不能白瞎,得充分利用!干脆把svg得到的图形作为背景图,用CSS定义出来如何?
.svg-triangle {
$h: 100;
$w: tan(30deg) * 2 * $h;
width: $w + px;
height: $h + px;
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='#{$w}' height='#{$h}'><polygon points='#{$w/2},0 #{$w},#{$h} 0,#{$h}' fill='red'/></svg>");
}
编译后得到:
.svg-triangle{
width:115.47005px;
height:100px;
background:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='115.47005' height='100'><polygon points='57.73503,0 115.47005,100 0,100' fill='red'/></svg>")
}
look! 这样做得好处就是内联svg的兼容性比上面的clip-path
强好多。
以上内容基本参考自: https://unindented.org/articles/trigonometry-in-sass/