Stylus 学习使用整理总结(下)

1,141 阅读12分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文承接《Stylus 学习使用整理总结(上)》,整理归纳了 Stylus 的进阶用法,包括剩余参数、条件、内置方法等。

Stylus进阶用法

剩余参数

剩余参数

Stylus 支持 name... 形式的其余参数。这些参数可以消化传递给混写或函数的参数们。这在处理浏览器私有属性,如 -moz-webkit 的时候很管用。下面这个例子中,所有的参数 (1px, 2px, ...) 都被一个 args 参数给简单处理了:

box-shadow(args...)
  -webkit-box-shadow args
  -moz-box-shadow args
  box-shadow args
#login
  box-shadow 1px 2px 5px #eee

生成为:

#login {
  -webkit-box-shadow: 1px 2px 5px #eee;
  -moz-box-shadow: 1px 2px 5px #eee;
  box-shadow: 1px 2px 5px #eee;
}

想指定特定的参数,如 x-offset,可以使用 args[0], 或者,可能希望重新定义混入。

box-shadow(offset-x, args...)
  got-offset-x offset-x
  -webkit-box-shadow offset-x args
  -moz-box-shadow offset-x args
  box-shadow offset-x args
#login
  box-shadow 1px 2px 5px #eee

生成为:

#login {
  got-offset-x: 1px;
  -webkit-box-shadow: 1px 2px 5px #eee;
  -moz-box-shadow: 1px 2px 5px #eee;
  box-shadow: 1px 2px 5px #eee;
}

参数

arguments 变量可以实现表达式的精确传递,包括逗号等等。这可以弥补 args... 参数的一些不足,例子:

box-shadow(args...)
  -webkit-box-shadow args
  -moz-box-shadow args
  box-shadow args
#login
  box-shadow #ddd 1px 1px, #eee 2px 2px

结果并非想要得到的:

#login {
  -webkit-box-shadow: #ddd 1px 1px #eee 2px 2px;
  -moz-box-shadow: #ddd 1px 1px #eee 2px 2px;
    box-shadow: #ddd 1px 1px #eee 2px 2px;
}

逗号被忽略了。现在使用 arguments 重新定义这个混合书写。

box-shadow()
  -webkit-box-shadow arguments
  -moz-box-shadow arguments
  box-shadow arguments
body
  box-shadow #ddd 1px 1px, #eee 2px 2px

得到想要的结果:

body {
  -webkit-box-shadow: #ddd 1px 1px, #eee 2px 2px;
  -moz-box-shadow: #ddd 1px 1px, #eee 2px 2px;
  box-shadow: #ddd 1px 1px, #eee 2px 2px;
}

条件

条件

条件提供了语言的流控制,否则就是纯粹的静态语言。提供的条件有导入、混入、函数以及更多。

if / else if / else

跟一般的语言一致, if 表达式满足 (true) 的时候执行后面语句块,否则,继续后面的 else ifelse.

下面这个例子,根据 overload 的条件,决定是使用 padding 还是 margin.

overload-padding = true

if overload-padding
  padding(y, x)
    margin y x

body
  padding 5px 10px

另外的例子:

box(x, y, margin = false)
  padding y x
  if margin
    margin y x

body
  box(5px, 10px, true)

另外的box()帮手:

box(x, y, margin-only = false)
  if margin-only
    margin y x
  else
    padding y x

除非(unless)

熟悉 Ruby 程序语言的用户应该都知道 unless 条件,其基本上与 if 相反,本质上是 (!(expr)).

下面这个例子中,如果 disable-padding-overrideundefinedfalse, padding 将被干掉,显示 margin 代替之。但是,如果是 true, padding 将会如期继续输出 padding 5px 10px.

disable-padding-override = true

unless disable-padding-override is defined and disable-padding-override
  padding(x, y)
    margin y x

body
  padding 5px 10px

后缀条件

Stylus 支持后缀条件,这就意味着 ifunless 可以当作操作符;当右边表达式为真的时候执行左边的操作对象。例如,我们定义 negative() 来执行一些基本的检查。下面我们使用块式条件:

negative(n)
  unless n is a 'unit'
    error('无效数值')
  if n < 0
    yes
  else
    no

接下来,利用后缀条件让我们的方法简洁。

negative(n)
  error('无效数值') unless n is a 'unit'
  return yes if n < 0
  no

当然,我们可以更进一步。如这个 n < 0 ? yes : no 可以用布尔代替:n < 0。后缀条件适用于大多数的单行语句。如,@import, @charset, 混合书写等。当然,下面所示的属性也是可以的:

pad(types = margin padding, n = 5px)
  padding unit(n, px) if padding in types
  margin unit(n, px) if margin in types
body
  pad()
body
  pad(margin)
body
  apply-mixins = true
  pad(padding, 10) if apply-mixins

结果为:

body {
  padding: 5px;
  margin: 5px;
}
body {
  margin: 5px;
}
body {
  padding: 10px;
}

迭代

迭代

Stylus 允许你通过 for/in 对表达式进行迭代形式如下:

for <val-name> [, <key-name>] in <expression>

例如:

body
  for num in 1 2 3
    foo num

生成:

body {
  foo: 1;
  foo: 2;
  foo: 3;
}

下面这个例子演示了如何使用 <key-name>

body
  fonts = Impact Arial sans-serif
  for font, i in fonts
    foo i font

生成为:

body {
  foo: 0 Impact;
  foo: 1 Arial;
  foo: 2 sans-serif;
}

混合书写(Mixins)

我们可以在混写中使用循环实现更强大的功能,例如,我们可以把表达式对作为使用插值和循环的属性。下面,我们定义 apply(), 利用所有的 arguments,这样逗号分隔以及表达式列表都会支持。

apply(props)
  props = arguments if length(arguments) > 1
  for prop in props
    {prop[0]} prop[1]

body
  apply(one 1, two 2, three 3)

body
  list = (one 1) (two 2) (three 3)
  apply(list)

函数(Functions)

Stylus 函数同样可以包含 for 循环。下面就是简单使用示例:

求和:

sum(nums)
  sum = 0
  for n in nums
    sum += n

sum(1 2 3)                                    // => 6

连接:

join(delim, args)
  buf = ''
  for arg, index in args
    if index
      buf += delim + arg
    else
      buf += arg

join(', ', foo bar baz)                     // => "foo, bar, baz"

后缀(Postfix)

if/unless 可以利用后面语句一样,for 也可以。如下后缀解析的例子:

sum(nums)
  sum = 0
  sum += n for n in nums

join(delim, args)
  buf = ''
  buf += i ? delim + arg : arg for arg, i in args

我们也可以从循环返回,下例子就是 n % 2 == 0true 的时候返回数值。

first-even(nums)
  return n if n % 2 == 0 for n in nums

first-even(1 3 5 5 6 3 2)                           // => 6

@IMPORT

导入

Stylus 支持字面 @import CSS, 也支持其他 Stylus 样式的动态导入。

字面CSS

任何 .css 扩展的文件名将作为字面量。例如:

@import "reset.css"

渲染如下:

@import "reset.css"

Stylus导入

  • 当使用 @import 没有 .css 扩展,会被认为是 Stylus 片段(如:@import "mixins/border-radius")。
  • @import 工作原理为:遍历目录队列,并检查任意目录中是否有该文件(类似 noderequire.paths )。该队列默认为单一路径,从 filename 选项的 dirname 衍生而来。 因此,如果你的文件名是 /tmp/testing/stylus/main.styl,导入将显现为 /tmp/testing/stylus/
  • @import 也支持索引形式。这意味着当你 @import blueprint, 则会理解成 blueprint.stylblueprint/index.styl. 下面语句:
@import 'mixins/vendor'

等同于:

.import('mixins/vendor')

@MEDIA

@media 工作原理和在常规 CSS 中一样,但是要使用 Stylus 的块状符号。

@media print
  #header
  #footer
    display none

生成为:

@media print {
  #header,
  #footer {
    display: none;
  }
}

@FONT-FACE

@font-face 跟其在 CSS 中作用表现一样,在后面简单地添加个块状属性即可,例如:

@font-face
  font-family Geo
  font-style normal
  src url(fonts/geo_sans_light/GensansLight.ttf)
.ingeo
  font-family Geo

生成为:

@font-face {
  font-family: Geo;
  font-style: normal;
  src: url("fonts/geo_sans_light/GensansLight.ttf");
}
.ingeo {
  font-family: Geo;
}

@KEYFRAMES

@keyframes

Stylus 支持 @keyframes 规则,当编译的时候转换成@-webkit-keyframes

@keyframes pulse
0%
  background-color red
  opacity 1.0
  -webkit-transform scale(1.0) rotate(0deg)
100%
  background-color red
  opacity 1.0
  -webkit-transform scale(1.0) rotate(0deg)

生成为:

@-webkit-keyframes pulse {
  0% {
  background-color: red;
  opacity: 1;
    -webkit-transform: scale(1) rotate(0deg);
  }
  100% {
    background-color: red;
    opacity: 1;
    -webkit-transform: scale(1) rotate(0deg);
  }
}

扩展

使用 @keyframes,通过 vendors变量,会自动添加私有前缀(webkit moz official)。这意味着可以在任意时候立即高效地做修改。考虑下面的例子:

@keyframes foo {
  from {
    color: black
  }
  to {
    color: white
  }
}

扩增两个默认前缀,官方解析:

@-moz-keyframes foo {
  0% {
    color: #000;
  }
  100% {
    color: #fff;
  }
}
@-webkit-keyframes foo {
  0% {
    color: #000;
  }
  100% {
    color: #fff;
  }
}
@keyframes foo {
  0% {
    color: #000;
  }
  100% {
    color: #fff;
  }
}

如果我们只想有标准解析,很简单,修改 vendors

vendors = official
@keyframes foo {
  from {
    color: black
  }
  to {
    color: white
  }
}

生成为:

@keyframes foo {
  0% {
    color: #000;
  }
  100% {
    color: #fff;
  }
}

内置方法

red(color)

返回 color 中的红色比重。

red(#c00)                                   // => 204

green(color)

返回 color 中的绿色比重。

green(#0c0)                                 // => 204

blue(color)

返回 color 中的蓝色比重。

blue(#00c)                                  // => 204

alpha(color)

返回 color 中的透明度比重。

alpha(#fff)                                 // => 1
alpha(rgba(0,0,0,0.3))                      // => 0.3

dark(color)

检查 color 是否是暗色。

dark(black)                                 // => true
dark(#005716)                               // => true
dark(white)                                 // => false

light(color)

检查 color 是否是亮色。

light(black)                                // => false
light(white)                                // => true
light(#00FF40)                              // => true

hue(color)

返回给定 color 的色调。

hue(hsla(50deg, 100%, 80%))                 // => 50deg

saturation(color)

返回给定 color 的饱和度。

saturation(hsla(50deg, 100%, 80%))         // => 100%

lightness(color)

返回给定 color 的亮度。

lightness(hsla(50deg, 100%, 80%))          // => 80%

push(expr, args…)

后面推送给定的 argsexpr.

nums = 1 2
push(nums, 3, 4, 5)

nums                                      // => 1 2 3 4 5

别名为 append().

unshift(expr, args…)

起始位置插入给定的 argsexpr.

nums = 4 5
unshift(nums, 3, 2, 1)

nums                                      // => 1 2 3 4 5

别名为 prepend().

keys(pairs)

返回给定 pairs 中的键。

pairs = (one 1) (two 2) (three 3)
keys(pairs)                               // => one two three

values(pairs)

返回给定 pairs 中的值。

pairs = (one 1) (two 2) (three 3)
values(pairs)                            // => 1 2 3

typeof(node)

字符串形式返回 node 类型。

type(12)                                  // => 'unit'
typeof(12)                                // => 'unit'
typeof(#fff)                              // => 'rgba'
type-of(#fff)                             // => 'rgba'

别名有 type-oftype.

unit(unit[, type])

返回 unit 类型的字符串或空字符串,或者赋予 type 值而无需单位转换。

unit(10)                                  // => ''
unit(15in)                                // => 'in'
unit(15%, 'px')                           // => 15px
unit(15%, px)                             // => 15px

match(pattern, string)

检测 string 是否匹配给定的 pattern.

match('^foo(bar)?', foo)                  // => true
match('^foo(bar)?', 'foobar')             // => true
match('^foo(bar)?', 'bar')                // => false

abs(unit)

返回绝对值。

abs(-5px)                                 // => 5px
abs(5px)                                  // => 5px

ceil(unit)

向上取整。

ceil(5.5in)                               // => 6in

floor(unit)

向下取整。

floor(5.6px)                              // => 5px

round(unit)

四舍五入取整。

round(5.5px)                              // => 6px
round(5.4px)                              // => 5px

min(a, b)

取较小值。

min(1, 5)                                 // => 1

max(a, b)

取较大值。

max(1, 5)                                 // => 5

even(unit)

返回是否为偶数。

even(6px)
// => true

odd(unit)

返回是否为奇数。

odd(5mm)                                // => true

sum(nums)

求和。

sum(1 2 3)                              // => 6

avg(nums)

求平均数。

avg(1 2 3)                              // => 2

join(delim, vals…)

给定 vals 使用 delim 连接。

join(' ', 1 2 3)                       // => "1 2 3"
join(',', 1 2 3)                       // => "1,2,3"
join(', ', foo bar baz)                // => "foo, bar, baz"
join(', ', foo, bar, baz)              // => "foo, bar, baz"
join(', ', 1 2, 3 4, 5 6)              // => "1 2, 3 4, 5 6"

hsla(color | h,s,l,a)

转换给定 colorHSLA 节点,或 h,s,l,a 比重值。

hslaa(10deg, 50%, 30%, 0.5)            // => HSLA
hslaa(#ffcc00)                         // => HSLA

hsla(color | h,s,l)

转换给定 colorHSLA 节点,或 h,s,l 比重值。

hsla(10, 50, 30)                      // => HSLA
hsla(#ffcc00)                         // => HSLA

rgba(color | r,g,b,a)

r,g,b,a 通道返回 RGBA, 或提供 color 来调整透明度。

rgba(255,0,0,0.5)                     // => rgba(255,0,0,0.5)
rgba(255,0,0,1)                       // => #ff0000
rgba(#ffcc00, 0.5)                    // rgba(255,204,0,0.5)
// 另外,stylus支持#rgba以及#rrggbbaa符号。
#fc08                                 // => rgba(255,204,0,0.5)
#ffcc00ee                             // => rgba(255,204,0,0.9)

rgb(color | r,g,b)

r,g,b 通道返回 RGBA 或生成一个 RGBA 节点。

rgb(255,204,0)                        // => #ffcc00
rgb(#fff)                             // => #fff

lighten(color, amount)

给定 color 增亮 amount值。该方法单位敏感,例如,支持百分比,如下:

lighten(#2c2c2c, 30)                  // => #787878
lighten(#2c2c2c, 30%)                 // => #393939

darken(color, amount)

给定 color 变暗 amount 值。该方法单位敏感,例如,支持百分比,如下:

darken(#D62828, 30)                   // => #551010
darken(#D62828, 30%)                  // => #961c1c

desaturate(color, amount)

给定 color 饱和度减小 amount.

desaturate(#f00, 40%)                 // => #c33

saturate(color, amount)

给定 color 饱和度增加 amount.

saturate(#c33, 40%)                   // => #f00

invert(color)

颜色反相。红绿蓝颜色反转,透明度不变。

invert(#d62828)                       // => #29d7d7

unquote(str | ident)

给定 str 引号去除,返回 Literal 节点。

unquote("sans-serif")                 // => sans-serif
unquote(sans-serif)                   // => sans-serif
unquote('1px / 2px')                  // => 1px / 2px

s(fmt, …)

s() 方法类似于 unquote(),不过后者返回的是 Literal 节点,而这里起接受一个格式化的字符串,非常像C语言sprintf(). 目前,唯一标识符是 %s.

s('bar()');                           // => bar()
s('bar(%s)', 'baz');                  // => bar("baz")
s('bar(%s)', baz);                    // => bar(baz)
s('bar(%s)', 15px);                   // => bar(15px)
s('rgba(%s, %s, %s, 0.5)', 255, 100, 50);   // => rgba(255, 100, 50, 0.5)
s('bar(%Z)', 15px);                   // => bar(%Z)
s('bar(%s, %s)', 15px);               // => bar(15px, null)

为表现一致检测这个%字符串操作符。

operate(op, left, right)

leftright 操作对象上执行给定的 op.

op = '+'
operate(op, 15, 5)                    // => 20

length([expr])

括号表达式扮演元组,length() 方法返回该表达式的长度。

length((1 2 3 4))                    // => 4
length((1 2))                        // => 2
length((1))                          // => 1
length(())                           // => 0
length(1 2 3)                        // => 3
length(1)                            // => 1
length()                             // => 0

warn(msg)

使用给定的 error 警告,并不退出。

warn("oh noes!")
error(msg)                            // 伴随着给定的错误msg退出。

add(a, b)

  unless a is a 'unit' and b is a 'unit'
    error('add() expects units')
  a + b

last(expr)

返回给定 expr 的最后一个值。

nums = 1 2 3
last(nums)
last(1 2 3)                           // => 3

list = (one 1) (two 2) (three 3)
last(list)                            // => (three 3)

p(expr)

检查给定的 expr.

fonts = Arial, sans-serif
p('test')
p(123)
p((1 2 3))
p(fonts)
p(#fff)
p(rgba(0,0,0,0.2))
add(a, b)
  a + b
// p(add) 标准输出:
inspect: "test"
inspect: 123
inspect: 1 2 3
inspect: Arial, sans-serif
inspect: #fff
inspect: rgba(0,0,0,0.2)
inspect: add(a, b)

opposite-position(positions)

返回给定 positions 相反内容。

opposite-position(right)                      // => left
opposite-position(top left)                   // => bottom right
opposite-position('top' 'left')               // => bottom right

image-size(path)

返回指定 path 图片的 widthheight. 向上查找路径的方法和 @import 一样,paths 设置的时候改变。

width(img)
  return image-size(img)[0]

height(img)
  return image-size(img)[1]

image-size('tux.png')                         // => 405px 250px
image-size('tux.png')[0] == width('tux.png')  // => true

add-property(name, expr)

使用给定的 expr 为最近的块域添加属性 name

例如:

something()
  add-property('bar', 1 2 3)
  s('bar')

body
  foo: something()

结果:

body {
  bar: 1 2 3;
  foo: bar;
}

接下来,神奇current-property 局部变量将大放异彩,这个变量自动提供给函数体,且包含当前属性名和值的表达式。例如,使用 p()检查这个局部变量,可以得到:

p(current-property)                         // => "foo" (foo __CALL__ bar baz)
p(current-property[0])                      // => "foo"
p(current-property[1])                      // => foo __CALL__ bar baz

使用 current-property 可以让例子走得更远点,使用新值复制该属性,且确保功能的条件仅在属性值中使用。

something(n)
  if current-property
    add-property(current-property[0], s('-webkit-something(%s)', n))
    add-property(current-property[0], s('-moz-something(%s)', n))
    s('something(%s)', n)
  else
    error('something() must be used within a property')

body {
  foo: something(15px) bar;
}

生成为:

body {
  foo: -webkit-something(15px);
  foo: -moz-something(15px);
  foo: something(15px) bar;
}

未定义方法

未定义方法一字面量形式输出。例如,我们可以在CSS 中调用 rgba-stop(50%, #fff), 其会按照你所期望的显示,下面这个例子中我们简单定义了方法 stop(), 其返回了字面上 rgba-stop() 调用。

stop(pos, rgba)
  rgba-stop(pos, rgba)

stop(50%, orange)                           // => rgba-stop(50%, #ffa500)