前端预处理技术(Less、Sass、CoffeeScript、TypeScript)

目录

一、Less

  1. 概要
  2. 变量
  3. 解析Less
    • 插件安装
    • 在线处理
    • 预处理
  4. 混入(Mixins)
  5. 嵌套   
  6. 运算
  7. 函数
  8. 继承    
  9. 作用域
  10. 注释
  11. 循环
  12. 示例代码

二、Sass

  1. 变量
  2. 嵌套
  3. 导入
  4. mixin 混入
  5. 扩展/继承
  6. 运算
  7. 函数
    • RGB函数
    • HSL函数简介(HSL用色轮表示颜色值)
    • Opacity函数简介(控制颜色的透明度)
  8. 流程控制

三、CoffeeScript

  1. 安装
  2. 使用

四、TypeScript

  1. 安装
  2. 使用typescript

五、ECMAScript

六、总结

CSS不像其它高级语言一样支持算术运算、变量、流程控制与面向对象特性,所以CSS样式较多时会引起一些问题,如修改复杂,冗余,某些别的语言很简单的功能实现不了等。而javascript则是一种半面向对象的动态语言,有java的影子,有C的味道,中间有比其它语言多的糟粕,使用预处理办法可以解决这些问题。其中Less[les]与Sass是CSS的预处理技术,而CoffeeScript、TypeScript则是javascript的预处理技术。

一、Less

1. 概要

Less是一种动态样式语言,Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量、Mixin、函数等特性,使 CSS 更易维护和扩展。

Less 将 CSS 赋予了动态语言的特性,如 变量, 继承, 运算, 函数。LESS 既可以在 客户端 上运行 (支持IE 6+, Webkit, Firefox),也可以借助Node.js或者Rhino在服务端运行。 Less是一个JS库,所以他可以在客户端运行,相对Sass则必须在服务端借助Ruby运行

中文网站:http://www.lesscss.net/

英文官网:http://lesscss.org

less源码:https://github.com/cloudhead/less.js

github地址:https://github.com/less/less.js

2. 变量

语法:@变量名:值;

  1. 以@作为变量的起始标识,变量名由字母、数字、_和-组成
  2. 没有先定义后使用的规定;
  3. 以最后定义的值为最终值;
  4. 可用于rule值、rule属性、rule属性部件、选择器、选择器部件、字符串拼接;
  5. 定义时 “@变量名: 变量值;” 的形式;引用时采用 “@变量名” 或 “@{变量名}” 的形式;
  6. 存在作用域,局部作用域优先级高于全局作用域。
1
2
3
4
5
6
7
@color: #4d926f; 
#header { color: @color; }
#header { color: #4d926f; }
@color: #253636;
@color: #ff3636; //覆盖第一次的定义
#header {color: @color;} //多次反复解析
#header {color: #ff3636;}

编译后:

1
2
3
4
5
6
7
8
9
10
11
12
#header {
color: #ff3636;
}
#header {
color: #4d926f;
}
#header {
color: #ff3636;
}
#header {
color: #ff3636;
}
3.解析Less

插件安装

  1. 先安装node.js(https://nodejs.org/en/)
  2. 安装less编译器 npm install less -g
  3. 安装插件

  4. 配置

默认是正常的,如果发现Hbuilder不能自动翻译则需要配置

在线处理

页面中直接引用less的源码,使用javascript动态翻译,这样在开发阶段非常方便,但是在运行阶段会影响效率,建议在开发阶段使用less.js在线处理,项目稳定运行时将less文件预处理。

步骤一:

下载到less.js动态处理.less文件的javascript脚本,下载地址: https://github.com/less/less.js

步骤二:

在页面中引入样式与less.js文件,如下:

1
2
<link rel="stylesheet/less" type="text/css" href="styles.less">
<script src="less.js" type="text/javascript"></script>

测试运行

示例代码:

style1.less

1
2
3
4
5
6
7
8
9
10
11
12
/*1定义变量*/
@color:red;
@bgColor:lightgreen; /*定义变量color,值为red*/
.cls11{
color: @color;
}
@color:lightblue; /*重新定义,覆盖前面的定义,后定义的起作用*/
.cls12
{
background: @bgColor;
border: 2px solid @color;
}

de2.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Less</title>
<link rel="stylesheet/less" type="text/css" href="css/style1.less">
<script src="js/less/less.min.js" type="text/javascript"></script>
</head>
<body>
<div id="div1" class="cls12">
Hello Less
</div>
</body>
</html>

运行效果:

从上图可以看出less.js将style1.less文件翻译后变成了一个标准的CSS内部样式表。

预处理

在线处理的效率低,预处理就是将less文件先翻译成标准的CSS文件,再引入到项目中,处理的办法有许多:

方法一:使用lessc

a)、请先在电脑上安装node.js,下载地址: https://nodejs.org/en/


a)、安装lessc

使用npm(node.js package management)node.js包管理器

在命令行模式下输入安装指令:npm install less -g

使用lessc翻译less文件为css文件:

lessc styles.less 显示

lessc styles.less > styles.css 生成文件

参数 –x 普通压缩

参数 -h 帮助

-x的压缩方法已经被弃用,建议使用清理插件。

方法二:使用工具软件

能够翻译Less的工具软件有不少,这里介绍:Koala

Koala是一个开源的预处理语言图形编译工具,目前已支持Less、Sass、Compass与CoffeeScript。
功能特性:
多语言支持: 支持Less、Sass、Compass与CoffeeScript。
实时监听与编译: 在后台监听文件的变动,检测到文件被修改后将自动进行编译。
编译选项支持: 可以设置与自定义你需要的编译选项。
压缩支持: Less、Sass可直接编译生成压缩后的css代码。
错误提示: 编译中如果遇到错误,Koala将在右下角提示并显示出具体的出错地方,方便开发者快速定位。
跨平台: Windows、Mac、Linux完美支持。
安装Koala
在Koala官网根据你的系统平台下载对应的版本。Linux系统要求已安装好ruby运行环境。

下载地址: http://koala-app.com/


注意:路径中不要使用中文,切记!

方法三:使用IDE插件

如果使用Eclipse,Hbuilder,Visual Studio等开发工具可以安装插件完成自动翻译功能,这里使用HBuilder,在工具->插件下可以选择安装,如下图所示:

使用方法:

新建Less文件,保存后会自己生成对应的CSS文件。

1.4、混入(Mixins)
类似函数或宏

定义函数,@radius是参数,3px是默认值

1
2
3
4
5
.borderRadius(@radius:3px){
-moz-border-radius: @radius;
-webkit-border-radius: @radius;
border-radius: @radius;
}

使用函数,带参数

#header { .borderRadius(10px); }

不带参数使用默认参数

.btn { .borderRadius}

注意:

  • 可以不使用参数 .wrap(){…} .pre{ .wrap },也可以使用多个参数
  • 内置变量@arguments代表所有参数(运行时)的值 .boxShadow(@x:0,@y:0,@blur:1px,@color:#000){ box-shadow: @arguments; }

注意,在参数没有默认值的前提下使用@arguments调用时必须赋值,否则会导致整个页面内的less语法出错而失效。

  • Mixins必须使用ID或者类,即#xx或.xx,否则无效。

Less示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*混入(Mixins)*/
/*定义*/
.circle(@width:100px, @color:lightblue) {
width: @width;
height: @width;
background: @color;
border-radius: @width/2;
float: left;
}
.boxShadow(@x:0, @y:0, @blur:1px, @color:#000) {
box-shadow: @arguments;
}
/*调用*/
.cls21 {
.circle();
/*默认值*/
}
.cls22 {
.circle(200px,lightgreen);
/*带参数*/
.boxShadow(5px,5px);
}
.cls23 {
.circle(300px);
/*带一个参数*/
}

HTML页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Less</title>
<link rel="stylesheet" type="text/css" href="css/style2.css" />
</head>
<body>
<div id="div1" class="cls21">
</div>
<div id="div1" class="cls22">
</div>
<div id="div1" class="cls23">
</div>
</body>
</html>

翻译结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*调用*/
.cls21 {
width: 100px;
height: 100px;
background: lightblue;
border-radius: 50px;
float: left;/*默认值*/
}
.cls22 {
width: 200px;
height: 200px;
background: lightgreen;
border-radius: 100px;
float: left;
/*带参数*/
box-shadow: 5px 5px 1px #000;
}
.cls23 {
width: 300px;
height: 300px;
background: lightblue;
border-radius: 150px;
float: left;/*带一个参数*/
}
/*# sourceMappingURL=style2.css.map */

运行效果:

1.5、嵌套   
允许将多个CSS选择器嵌套在一起,&表示当前选择器的父选择器

1
2
3
4
#header { 
&.fl{ float: left; }
.mln { margin-left: 0; }
}

生成

1
2
#header.fl{float: left;} 
#header .mln {margin-left: 0;}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*嵌套*/
#parent {
color: red;
.sub11 {
background: green;
}
&.sub12 {
width: 100px;
}
.sub13 {
height: 200px;
.sub131 {
font-size: 10px;
}
}
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*嵌套*/
#parent {
color: red;
}
#parent .sub11 {
background: green;
}
#parent.sub12 {
width: 100px;
}
#parent .sub13 {
height: 200px;
}
#parent .sub13 .sub131 {
font-size: 10px;
}

1.6、运算

运算主要是针对任何数字、颜色、变量的操作,支持加、减、乘、除、()或者更复杂的综合运算;

@init: #111111;

@transition: @init*2;

.switchColor { color: @transition; }

算术运算示例:

1
2
3
4
5
6
7
8
9
10
/*运算*/
@base: 5%;
@filler: @base * 2;
@other: @base + @filler;
@base-color:lightblue;
.cls41{
color: #888 / 4;
background-color: @base-color + #111;
height: 100% / 2 + @filler;
}

运行结果:

1
2
3
4
5
.cls41 {
color: #222222;
background-color: #bee9f7;
height: 60%;
}

1.7、函数

Less 提供了许多用于转换颜色,处理字符串和进行算术运算的函数

.lightColor{lighten(@color, 10%); }

更多函数: http://www.lesscss.net/functions/

示例:

1
2
3
4
5
6
7
8
9
10
/*函数*/
.cls51 {
/*将一个资源内嵌到样式文件,如果开启了ieCompat选项,而且资源文件的体积过大,或者是在浏览器中使用,则会使用url()进行回退。如果没有指定MIME,则Node.js会使用MIME包来决定正确的MIME。*/
background: data-uri('../img/yes.gif') no-repeat;
height: 20px;
}
.cls52 {
/*增加一定数值的颜色亮度。*/
background: lighten(blue,20%);
}

翻译结果:

1
2
3
4
5
6
7
8
9
10
/*函数*/
.cls51 {
/*将一个资源内嵌到样式文件,如果开启了ieCompat选项,而且资源文件的体积过大,或者是在浏览器中使用,则会使用url()进行回退。如果没有指定MIME,则Node.js会使用MIME包来决定正确的MIME。*/
background: url("data:null;base64,R0lGODlhDAAMAKIAAMznjyJ6Gu732TKGFq7ZTF+nDI7JBf///yH5BAAAAAAALAAAAAAMAAwAAAM8eCdAZgQItdy7RAlXyhidBhjdEAQD1ZDHGVDQUyivMlws1d6xR6EFyKi06xgkHA8oSJhscI8mhWGJTA4JADs=") no-repeat;
height: 20px;
}
.cls52 {
/*增加一定数值的颜色亮度。*/
background: #6666ff;
}

运行效果:

1.8、继承 
   
示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
/*继承*/
.animal {
background-color: black;
color: white;
}
.bear {
&:extend(.animal);
background-color: brown;
}
.mouse{
&:extend(.animal);
}

翻译结果:

1
2
3
4
5
6
7
8
9
10
/*继承*/
.animal,
.bear,
.mouse {
background-color: black;
color: white;
}
.bear {
background-color: brown;
}

1.9、作用域

同一级的变量后者覆盖前者,内部变量优先级高于外部变量,变量只在同一个文件中生效。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
/*作用域*/
@len:10px;
.cls61{
@len:20px;
height:@len;
}
.cls62{
width:@len;
}
@len:30px;
.cls63{
height: @len;
}

结果:

1
2
3
4
5
6
7
8
9
.cls61 {
height: 20px;
}
.cls62 {
width: 30px;
}
.cls63 {
height: 30px;
}

1.10、注释

示例:

1
2
3
4
5
/*注释*/
.cls71{
width: 100px; //单行注释,CSS中不允许单行注释,Less允许
height:100px; /* 多行注释,CSS与Less都允许 */
}

结果:

1
2
3
4
5
/*注释*/
.cls71 {
width: 100px;
height: 100px;/* 多行注释,CSS与Less都允许 */
}

1.11、循环

在Less中,混合可以调用它自身。这样,当一个混合递归调用自己,再结合Guard表达式和模式匹配这两个特性,就可以写出循环结构。

less源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.loop(@counter) when (@counter > 0) {
.loop((@counter - 1)); // 递归调用自身
width: (10px * @counter); // 每次调用时产生的样式代码
}

div {
.loop(3); // 调用循环
}
```

生成css

``` css
div {
width: 30px;
width: 20px;
width: 10px;
}

less源码(生成栅格系统):

1
2
3
4
5
6
7
.generate-columns(5);
.generate-columns(@n, @i: 1) when (@i =< @n) {
.column-@{i} {
width: (@i * 100% / @n);
}
.generate-columns(@n, (@i + 1));
}

生成css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.column-1 {
width: 20%;
}
.column-2 {
width: 40%;
}
.column-3 {
width: 60%;
}
.column-4 {
width: 80%;
}
.column-5 {
width: 100%;
}

1.12、示例代码

style.less:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*1.3、变量**/

@color: red;

/*1.4、混入(Mixins)**/

.circle(@length: 100px, @color: lightblue) {
width: @length;
height: @length;
background: @color;
border-radius: @length/3;
float: left;
}

.boxshadow(@x: 3px, @y: 3px, @blur: 3px, @color: #999) {
box-shadow: @arguments;
box-shadow: @x @y @blur @color;
}

.c1 {
.circle();
/**默认参数*/
}

.c2 {
.circle(200px, lightgreen);
/**指定所有参数*/
}

.c3 {
.circle(300px);
/**指定部分*/
}

.c4 {
.circle(400px);
/**指定部分*/
.boxshadow(5px, 5px, 5px, #00f);
}

.c5 {
.circle(500px);
/**指定部分*/
.boxshadow();
}


/*1.5、嵌套 **/

#main {
color: @color;
.sub1 {
width: 100px;
.sub11 {
background: #00f;
}
}
&.sub2 {
height: 100px;
}
}

@color: blue;

/*1.6、运算**/

@i: 10rem;
@pcolor: blue;
@color: #0000ff;
.cls16 {
width: @i+10/2;
background: #000111+#111000;
color: @pcolor+#00ff00;
}


/*1.7、函数**/

.lightColor {
width: floor(2.6)px;
}

.bg {
background: data-uri('dot2.gif');
}


/*1.8、继承 **/

.animal {
color: black;
background: white;
}

.duck {
&:extend(.animal);
color: yellow;
}

.tduck {
&:extend(.duck);
min-height: 100px;
}

.pig {
&:extend(.animal);
}


/*1.9、作用域*/

@len: 10px;
.cls61 {
@len: 20px;
height: @len;
}

@len: 30px;
.cls62 {
width: @len;
}

@len: 40px;
.cls63 {
height: @len;
}

@len: 50px;

/*1.10、注释**/

.c {
width: 100px; //宽100px
}


/*1.11、循环*/

.loop(@counter) when (@counter > 0) {
width: (10px * @counter); // 每次调用时产生的样式代码
.loop((@counter - 1)); // 递归调用自身
}

div {
.loop(3); // 调用循环
}

.generate-columns(5);
.generate-columns(@n, @i: 1) when (@i =< @n) {
.column-@{i} {
width: (@i * 100% / @n);
}
.generate-columns(@n, (@i + 1));
}

style.css:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*1.3、变量**/


/*1.4、混入(Mixins)**/

.c1 {
width: 100px;
height: 100px;
background: lightblue;
border-radius: 33.33333333px;
float: left;
/**默认参数*/
}

.c2 {
width: 200px;
height: 200px;
background: lightgreen;
border-radius: 66.66666667px;
float: left;
/**指定所有参数*/
}

.c3 {
width: 300px;
height: 300px;
background: lightblue;
border-radius: 100px;
float: left;
/**指定部分*/
}

.c4 {
width: 400px;
height: 400px;
background: lightblue;
border-radius: 133.33333333px;
float: left;
/**指定部分*/
box-shadow: 5px 5px 5px #00f;
}

.c5 {
width: 500px;
height: 500px;
background: lightblue;
border-radius: 166.66666667px;
float: left;
/**指定部分*/
box-shadow: 3px 3px 3px #999;
}


/*1.5、嵌套 **/

#main {
color: #0000ff;
}

#main .sub1 {
width: 100px;
}

#main .sub1 .sub11 {
background: #00f;
}

#main.sub2 {
height: 100px;
}


/*1.6、运算**/

.cls16 {
width: 15rem;
background: #111111;
color: #00ffff;
}


/*1.7、函数**/

.lightColor {
width: 2 px;
}

.bg {
background: url("data:image/gif;base64,R0lGODlhCQALAJEAAAAAAP///wCO/////yH5BAEAAAMALAAAAAAJAAsAAAITnI8naeLsllq0vlhHnhJFpoFMAQA7");
}


/*1.8、继承 **/

.animal,
.duck,
.pig,
.tduck {
color: black;
background: white;
}

.duck,
.tduck {
color: yellow;
}

.tduck {
min-height: 100px;
}


/*1.9、作用域*/

.cls61 {
height: 20px;
}

.cls62 {
width: 50px;
}

.cls63 {
height: 50px;
}


/*1.10、注释**/

.c {
width: 100px;
}


/*1.11、循环*/

div {
width: 30px;
width: 20px;
width: 10px;
}

.column-1 {
width: 20%;
}

.column-2 {
width: 40%;
}

.column-3 {
width: 60%;
}

.column-4 {
width: 80%;
}

.column-5 {
width: 100%;
}

二、Sass

Sass与Less类似类似也是一种CSS的预编译语言,他出现的更晚,但功能更加强大,Sass 有两种语法。 第一种被称为 SCSS (Sassy CSS),是一个 CSS3 语法的扩充版本;第二种比较老的语法成为缩排语法(或者就称为 “Sass”), 提供了一种更简洁的 CSS 书写方式特点如下:

特点:

  1. 不能直接在页面中解析,需要使用ruby预先翻译成css文件,而Less可以在线动态翻译。
  2. Sass功能更加强大,拥有流控语句等Less不具备的功能
  3. 完全兼容 CSS3,在 CSS 语言基础上添加了扩展功能,比如变量、嵌套 (nesting)、混合 (mixin)

在使用时Sass的后缀名为scss,本文全部使用scss的语法,可以安装Koala直接解析,不需要去搭建ruby环境,Koala已封装好。

下载地址: http://koala-app.com/

2.1、变量

sass中可以定义变量,方便统一修改和维护

Sass代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*变量*/
$width:1004px;
$color:blue;

.cls11
{
width: $width;
height: $width/2;
background: $color;
}
$width:100px;
$color:red;
.cls12
{
$color:green;
width: $width;
height: $width/2;
background: $color;
}

CSS代码:

1
2
3
4
5
6
7
8
9
.cls11 {
width: 1004px;
height: 502px;
background: blue; }

.cls12 {
width: 100px;
height: 50px;
background: green; }
2.2、嵌套

sass可以进行选择器的嵌套,表示层级关系,看起来很优雅整齐。

Sass代码:

1
2
3
4
5
6
7
8
9
10
11
.cls21 
{
width: 100px;
.cls22{
height: 200px;
}
.cls23
{
color:blue;
}
}

CSS代码:

1
2
3
4
5
6
7
8
9
10
11
.cls21 {
width: 100px;
}

.cls21 .cls22 {
height: 200px;
}

.cls21 .cls23 {
color: blue;
}

复杂的嵌套:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.news{

//忽略root嵌套
@at-root
.news_title{
width: 10px;
}

// 合并嵌套
.news_content{
width: 20px;
}

//组合嵌套
&_content1{
width: 20px;
&_content2{
width: 20px;
}
}

}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.news .news_content {
// 在根节点.news下
width: 20px;
}

.news_content1 {
// 组合了根节点.news成为当前标签的一部分
width: 20px;
}

.news_content1_content2 {
// 多层组合&值为上一层的标签名称
width: 20px;
}
2.3、导入

sass中如导入其他sass文件,最后编译为一个css文件,优于纯css的@import

reset.scss

1
2
3
4
5
6
7
8
9
10
$zero:0;
$PI:3.14;
*
{
margin: $zero;
padding: $zero;
}
body,html{
height: 100%;
}

Sass代码:

1
2
3
4
5
@import "reset";
.cls31 {
/*height: zero; */
/*error*/
}

CSS代码:

1
2
3
4
5
6
7
8
9
10
* {
margin: 0;
padding: 0; }

body, html {
height: 100%; }

.cls31 {
/*height: zero; */
/*error*/ }
2.4、mixin 混入

sass中可用mixin定义一些代码片段,且可传参数,方便日后根据需求调用。从此处理css3的前缀兼容轻松便捷。定义时使用关键字@mixin,调用时使用@include

SCSS样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@mixin circle($size:100px,$color:lightblue){
width: $size;
height: $size;
border-radius: $size/2;
background: $color;
}

.cls41{
@include circle();
}

.cls42{
@include circle(150px);
}

.cls43{
@include circle(200px,lightgreen);
}

CSS样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.cls41 {
width: 100px;
height: 100px;
border-radius: 50px;
background: lightblue;
}

.cls42 {
width: 150px;
height: 150px;
border-radius: 75px;
background: lightblue;
}

.cls43 {
width: 200px;
height: 200px;
border-radius: 100px;
background: lightgreen;
}
2.5、扩展/继承

sass可通过@extend来实现代码组合声明,使代码更加优越简洁。

SCSS样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.state
{
background: blue;
border: 1px solid lightblue;
}

.success{
@extend .state;
background: green;
}

.error
{
@extend .state;
border: 2px solid red;
}

CSS样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.state,
.success,
.error {
background: blue;
border: 1px solid lightblue;
}

.success {
background: green;
}

.error {
border: 2px solid red;
}
2.6、运算

SCSS样式:

1
2
3
4
.cls61
{
width: (100px+10px)/2-20px%7px+1px*8;
}

CSS样式:

1
2
3
.cls61 {
width: 57px;
}
2.7、函数

sass中集成了大量的颜色函数,让变换颜色更加简单。

SCSS样式:

1
2
3
4
5
6
7
8
9
$pcolor: #999ccc;
.cls71 a {
color: $pcolor;
&:hover {
background: darken($pcolor,15%);
/*变暗15%*/
color: lighten($pcolor,5%);
/*变亮5%*/
}

CSS样式:

1
2
3
4
5
6
7
8
.cls71 a {
color: #999ccc;
}

.cls71 a:hover {
background: #666bb3;
color: #aaacd5;
}

2.7.1. RGB函数

rgb($red, $green, $blue) //根据RGB中的三个值计算出一个颜色;

rgba($red, $green, $blue, $alpha) //根据RGB中红、绿、蓝和透明度计算出一个颜色;

red($color) //获取RGB中的红色值;

green($color) //获取RGB中的绿色值;

blue($color) //获取RGB中的蓝色值;

mix($color1, $color2, [$weight]) //混合两种颜色;

2.7.2. HSL函数简介(HSL用色轮表示颜色值)

hsl(hue,saturation, $lightness): 根据色相、饱和度和亮度的值返回对应的HEX颜色

hsla(hue,saturation, lightness,alpha): 根据色相、饱和度、亮度和透明度的值返回对应的HEX颜色

hue($color):从HEX颜色值中取得色相值

saturation($color): 从一个HEX颜色值中取得饱和度值

lightness($color):从一个HEX颜色值中取得亮度值

ajust-hue(color,degrees):通过改变一个颜色的色相值,创建一个新的颜色

lighten(color,amount):通过改变颜色的亮度值,让颜色变亮,创建一个一个新的颜色

darken(color,amount):通过改变颜色的亮度值,让颜色变暗,创建一个一个新的颜色

saturate(color,amount):通过改变颜色的饱和度值,让颜色更饱和,从而创建一个新的颜色

desaturate(color,amount):通过改变颜色的饱和度值,让颜色更少的饱和,从而创建出一个新的颜色

grayscale(color):将一个颜色变成灰色,相当于desaturate(color,100%);

complement(color):返回一个补充色,相当于adjust?hue(color,180deg);

invert($color):反回一个反相色,红、绿、蓝色值倒过来,而透明度不变

2.7.3.Opacity函数简介(控制颜色的透明度)

alpha($color)/opacity($color):获得透明度值

rgba($color,alpha):改变颜色的透明度值

opacify($color,$amount)/fade-in($color,$amount):使颜色更不透明

transparentize($color,$amount)/fade-out($color,$amount):使颜色更加透明

2.8、流程控制

sass中和其它程序语言一样也拥有流程控制语句,如if,for,each,while,指令,函数等。

SCSS样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
$blur: lightblue;
@for $i from 1 through 10 {
.font-#{$i} {
/*计算字体大小*/
font-size: 12px+$i*2px;
/*颜色变暗*/
color: darken($blur,$i*2);
/*如果i是3的倍数,则下划线*/
@if $i%3==0 {
text-decoration: underline;
}
}
}

CSS样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/*8*/
.font-1 {
font-size: 14px;
color: #a5d4e4; }

.font-2 {
font-size: 16px;
color: #9dd1e1; }

.font-3 {
font-size: 18px;
color: #96cddf;
text-decoration: underline; }

.font-4 {
font-size: 20px;
color: #8ec9dc; }

.font-5 {
font-size: 22px;
color: #86c5da; }

.font-6 {
font-size: 24px;
color: #7ec2d8;
text-decoration: underline; }

.font-7 {
font-size: 26px;
color: #76bed5; }

.font-8 {
font-size: 28px;
color: #6ebad3; }

.font-9 {
font-size: 30px;
color: #67b7d1;
text-decoration: underline; }

.font-10 {
font-size: 32px;
color: #5fb3ce; }

HTML页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" type="text/css" href="css/style3.css" />
</head>
<body style="padding: 10px;">
<div class="font-1">Hello SASS!</div>
<div class="font-2">Hello SASS!</div>
<div class="font-3">Hello SASS!</div>
<div class="font-4">Hello SASS!</div>
<div class="font-5">Hello SASS!</div>
<div class="font-6">Hello SASS!</div>
<div class="font-7">Hello SASS!</div>
<div class="font-8">Hello SASS!</div>
<div class="font-9">Hello SASS!</div>
<div class="font-10">Hello SASS!</div>
</body>
</html>

运行效果:

更多内容请参考:https://www.sass.hk http://sass.bootcss.com/ http://sass-lang.com/

三、CoffeeScript

javascript变得日益重要,但有很多明显的缺点,借助一种中间语言转译出优雅的javascript是解决这些问题的方法。如CoffeeScript,TypeScript。

Coffee Script是JavaScript的转译语言。了解JavaScript的发展历程:https://news.cnblogs.com/n/558565/。

Coffee的特点:

  • CoffeeScript语法类似 Ruby ,可以被编译成 JavaScript
  • CoffeeScript取JavaScript之精华,而抛弃了诸如全局变量声明、with等容易出错的部分
  • CoffeeScript是JavaScript与程序员之间的桥梁,程序员看到的是优雅的CoffeeScript接口,使得编程更简洁,写法更随意
  • 更少,更紧凑,和更清晰的代码
  • 通过规避和改变对JavaScript中不良部分的使用,只留下精华,让代码减少出错率,更容易维护
  • 在很多常用模式的实现上采用了JavaScript中的最佳实践
  • CoffeeScript生成的JavaScript代码都可以完全通过JSLint的检测

中文网: http://coffee-script.org/

官网: http://coffeescript.org/

源码:https://github.com/coffee-js/coffee-script

3.1、安装

CoffeeScript 编译器本身是 CoffeeScript 写的, 使用了 Jison parser generator. 命令行版本的coffee是一个实用的 Node.js 工具。

安装前你需要最新稳定版 Node.js, 和 npm (Node Package Manager)。借助 npm 可以安装 CoffeeScript:

npm install -g coffee-script

安装之后, 你应该可以运行 coffee 命令以执行脚本, 编译 .coffee 文件到 .js 文件, 和提供一个交互式的 REPL. coffee 命令有下列参数:

-c, –compile 编译一个 .coffee 脚本到一个同名的 .js 文件.

-m, –map 随 JavaScript 文件一起生成 source maps. 并且在 JavaScript 里加上 sourceMappingURL 指令.

-i, –interactive 启动一个交互式的 CoffeeScript 会话用来尝试一些代码片段. 等同于执行 coffee 而不加参数.

-o, –output [DIR] 将所有编译后的 JavaScript 文件写到指定文件夹. 与 –compile 或 –watch 搭配使用.

-j, –join [FILE] 编译之前, 按参数传入顺序连接所有脚本到一起, 编译后写到指定的文件. 对于编译大型项目有用.

-w, –watch 监视文件改变, 任何文件更新时重新执行命令.

-p, –print JavaScript 直接打印到 stdout 而不是写到一个文件.

-s, –stdio 将 CoffeeScript 传递到 STDIN 后从 STDOUT 获取 JavaScript. 对其他语言写的进程有好处. 比如:cat src/cake.coffee | coffee -sc

-l, –literate 将代码作为 Literate CoffeeScript 解析. 只会在从 stdio 直接传入代码或者处理某些没有后缀的文件名需要写明这点.

-e, –eval 直接从命令行编译和打印一小段 CoffeeScript. 比如:coffee -e “console.log num for num in [10..1]”

-b, –bare 编译到 JavaScript 时去掉顶层函数的包裹.

-t, –tokens 不对 CoffeeScript 进行解析, 仅仅进行 lex, 打印出 token stream: [IDENTIFIER square] [ASSIGN =] [PARAM_START (] …

-n, –nodes 不对 CoffeeScript 进行编译, 仅仅 lex 和解析, 打印 parse tree:

–nodejs node 命令有一些实用的参数, 比如

–debug, –debug-brk, –max-stack-size, 和 –expose-gc. 用这个参数直接把参数转发到 Node.js. 重复使用 –nodejs 来传递多个参数.

3.2、使用

1、编辑coffee脚本,后缀为coffee,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 赋值:
number = 42
opposite = true

# 条件:
number = -42 if opposite

# 函数:
square = (x) -> x * x

# 数组:
list = [1, 2, 3, 4, 5]

# 对象:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x

# Splats:
race = (winner, runners...) ->
print winner, runners

# 存在性:
alert "I knew it!" if elvis?

# 数组 推导(comprehensions):
cubes = (math.cube num for num in list)

将coffeescript翻译成javascript的方法如下:

a)、使用IDE插件直接翻译

翻译成javascript后的脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
(function() {
var cubes, list, math, num, number, opposite, race, square,
slice = [].slice;

number = 42;

opposite = true;

if (opposite) {
number = -42;
}

square = function(x) {
return x * x;
};

list = [1, 2, 3, 4, 5];

math = {
root: Math.sqrt,
square: square,
cube: function(x) {
return x * square(x);
}
};

race = function() {
var runners, winner;
winner = arguments[0], runners = 2 <= arguments.length ? slice.call(arguments, 1) : [];
return print(winner, runners);
};

if (typeof elvis !== "undefined" && elvis !== null) {
alert("I knew it!");
}

cubes = (function() {
var i, len, results;
results = [];
for (i = 0, len = list.length; i < len; i++) {
num = list[i];
results.push(math.cube(num));
}
return results;
})();

}).call(this);

b)、命令行翻译

翻译后的结果与上文相同,-c是参数表示编译的意思,-w是监听文件的变化,文件发生变化后将立即编译。

面向对象示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Animal
constructor: (@name) ->

move: (meters) ->
alert @name + " moved #{meters}m."

class Snake extends Animal
move: ->
alert "Slithering..."
super 5

class Horse extends Animal
move: ->
alert "Galloping..."
super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"

sam.move()
tom.move()

翻译后的javascript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
(function() {
var Animal, Horse, Snake, sam, tom,
extend = function(child, parent) {
for(var key in parent) {
if(hasProp.call(parent, key)) child[key] = parent[key];
}

function ctor() {
this.constructor = child;
}
ctor.prototype = parent.prototype;
child.prototype = new ctor();
child.__super__ = parent.prototype;
return child;
},
hasProp = {}.hasOwnProperty;

Animal = (function() {
function Animal(name) {
this.name = name;
}

Animal.prototype.move = function(meters) {
return alert(this.name + (" moved " + meters + "m."));
};

return Animal;

})();

Snake = (function(superClass) {
extend(Snake, superClass);

function Snake() {
return Snake.__super__.constructor.apply(this, arguments);
}

Snake.prototype.move = function() {
alert("Slithering...");
return Snake.__super__.move.call(this, 5);
};

return Snake;

})(Animal);

Horse = (function(superClass) {
extend(Horse, superClass);

function Horse() {
return Horse.__super__.constructor.apply(this, arguments);
}

Horse.prototype.move = function() {
alert("Galloping...");
return Horse.__super__.move.call(this, 45);
};

return Horse;

})(Animal);

sam = new Snake("Sammy the Python");

tom = new Horse("Tommy the Palomino");

sam.move();

tom.move();

}).call(this);

四、TypeScript

TypeScript是一种由微软开发的自由和开源的编程语言,它是JavaScript的一个超集,扩展了JavaScript的语法,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。安德斯·海尔斯伯格,C#的首席架构师,工作于TypeScript的开发。

官网:http://www.typescriptlang.org/

github:https://github.com/Microsoft/TypeScript

4.1、安装
  • 在node.js环境下安装typescript,npm install -g typescript
  • 使用Microsoft指定的编辑器或IDE如,VS与微软Visual Studio Code 免费跨平台代码编辑器,安装相应的插件。

4.2、使用typescript

编写typescript源代码,greeter.ts:

1
2
3
4
5
6
7
8
9
class Greeter {
constructor(public greeting: string) { }
greet() {
return "<h1>" + this.greeting + "</h1>";
}
};

var greeter = new Greeter("Hello, world!");
document.body.innerHTML = greeter.greet();

使用tsc greeter.ts编译生成javascript greeter.js脚本:

1
2
3
4
5
6
7
8
9
10
11
12
var Greeter = (function () {
function Greeter(greeting) {
this.greeting = greeting;
}
Greeter.prototype.greet = function () {
return "<h1>" + this.greeting + "</h1>";
};
return Greeter;
}());
;
var greeter = new Greeter("Hello, world!");
document.body.innerHTML = greeter.greet();

新建一个页面测试:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>TypeScript Hello World!</title>
</head>
<body>
<script src="typescript/greeter.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>

运行结果:

示例2,raytracer.ts代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
class Vector {
constructor(public x: number,
public y: number,
public z: number) {
}
static times(k: number, v: Vector) { return new Vector(k * v.x, k * v.y, k * v.z); }
static minus(v1: Vector, v2: Vector) { return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); }
static plus(v1: Vector, v2: Vector) { return new Vector(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); }
static dot(v1: Vector, v2: Vector) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; }
static mag(v: Vector) { return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); }
static norm(v: Vector) {
var mag = Vector.mag(v);
var div = (mag === 0) ? Infinity : 1.0 / mag;
return Vector.times(div, v);
}
static cross(v1: Vector, v2: Vector) {
return new Vector(v1.y * v2.z - v1.z * v2.y,
v1.z * v2.x - v1.x * v2.z,
v1.x * v2.y - v1.y * v2.x);
}
}

class Color {
constructor(public r: number,
public g: number,
public b: number) {
}
static scale(k: number, v: Color) { return new Color(k * v.r, k * v.g, k * v.b); }
static plus(v1: Color, v2: Color) { return new Color(v1.r + v2.r, v1.g + v2.g, v1.b + v2.b); }
static times(v1: Color, v2: Color) { return new Color(v1.r * v2.r, v1.g * v2.g, v1.b * v2.b); }
static white = new Color(1.0, 1.0, 1.0);
static grey = new Color(0.5, 0.5, 0.5);
static black = new Color(0.0, 0.0, 0.0);
static background = Color.black;
static defaultColor = Color.black;
static toDrawingColor(c: Color) {
var legalize = d => d > 1 ? 1 : d;
return {
r: Math.floor(legalize(c.r) * 255),
g: Math.floor(legalize(c.g) * 255),
b: Math.floor(legalize(c.b) * 255)
}
}
}

class Camera {
public forward: Vector;
public right: Vector;
public up: Vector;

constructor(public pos: Vector, lookAt: Vector) {
var down = new Vector(0.0, -1.0, 0.0);
this.forward = Vector.norm(Vector.minus(lookAt, this.pos));
this.right = Vector.times(1.5, Vector.norm(Vector.cross(this.forward, down)));
this.up = Vector.times(1.5, Vector.norm(Vector.cross(this.forward, this.right)));
}
}

interface Ray {
start: Vector;
dir: Vector;
}

interface Intersection {
thing: Thing;
ray: Ray;
dist: number;
}

interface Surface {
diffuse: (pos: Vector) => Color;
specular: (pos: Vector) => Color;
reflect: (pos: Vector) => number;
roughness: number;
}

interface Thing {
intersect: (ray: Ray) => Intersection;
normal: (pos: Vector) => Vector;
surface: Surface;
}

interface Light {
pos: Vector;
color: Color;
}

interface Scene {
things: Thing[];
lights: Light[];
camera: Camera;
}

class Sphere implements Thing {
public radius2: number;

constructor(public center: Vector, radius: number, public surface: Surface) {
this.radius2 = radius * radius;
}
normal(pos: Vector): Vector { return Vector.norm(Vector.minus(pos, this.center)); }
intersect(ray: Ray) {
var eo = Vector.minus(this.center, ray.start);
var v = Vector.dot(eo, ray.dir);
var dist = 0;
if (v >= 0) {
var disc = this.radius2 - (Vector.dot(eo, eo) - v * v);
if (disc >= 0) {
dist = v - Math.sqrt(disc);
}
}
if (dist === 0) {
return null;
} else {
return { thing: this, ray: ray, dist: dist };
}
}
}

class Plane implements Thing {
public normal: (pos: Vector) =>Vector;
public intersect: (ray: Ray) =>Intersection;
constructor(norm: Vector, offset: number, public surface: Surface) {
this.normal = function(pos: Vector) { return norm; }
this.intersect = function(ray: Ray): Intersection {
var denom = Vector.dot(norm, ray.dir);
if (denom > 0) {
return null;
} else {
var dist = (Vector.dot(norm, ray.start) + offset) / (-denom);
return { thing: this, ray: ray, dist: dist };
}
}
}
}

module Surfaces {
export var shiny: Surface = {
diffuse: function(pos) { return Color.white; },
specular: function(pos) { return Color.grey; },
reflect: function(pos) { return 0.7; },
roughness: 250
}
export var checkerboard: Surface = {
diffuse: function(pos) {
if ((Math.floor(pos.z) + Math.floor(pos.x)) % 2 !== 0) {
return Color.white;
} else {
return Color.black;
}
},
specular: function(pos) { return Color.white; },
reflect: function(pos) {
if ((Math.floor(pos.z) + Math.floor(pos.x)) % 2 !== 0) {
return 0.1;
} else {
return 0.7;
}
},
roughness: 150
}
}


class RayTracer {
private maxDepth = 5;

private intersections(ray: Ray, scene: Scene) {
var closest = +Infinity;
var closestInter: Intersection = undefined;
for (var i in scene.things) {
var inter = scene.things[i].intersect(ray);
if (inter != null && inter.dist < closest) {
closestInter = inter;
closest = inter.dist;
}
}
return closestInter;
}

private testRay(ray: Ray, scene: Scene) {
var isect = this.intersections(ray, scene);
if (isect != null) {
return isect.dist;
} else {
return undefined;
}
}

private traceRay(ray: Ray, scene: Scene, depth: number): Color {
var isect = this.intersections(ray, scene);
if (isect === undefined) {
return Color.background;
} else {
return this.shade(isect, scene, depth);
}
}

private shade(isect: Intersection, scene: Scene, depth: number) {
var d = isect.ray.dir;
var pos = Vector.plus(Vector.times(isect.dist, d), isect.ray.start);
var normal = isect.thing.normal(pos);
var reflectDir = Vector.minus(d, Vector.times(2, Vector.times(Vector.dot(normal, d), normal)));
var naturalColor = Color.plus(Color.background,
this.getNaturalColor(isect.thing, pos, normal, reflectDir, scene));
var reflectedColor = (depth >= this.maxDepth) ? Color.grey : this.getReflectionColor(isect.thing, pos, normal, reflectDir, scene, depth);
return Color.plus(naturalColor, reflectedColor);
}

private getReflectionColor(thing: Thing, pos: Vector, normal: Vector, rd: Vector, scene: Scene, depth: number) {
return Color.scale(thing.surface.reflect(pos), this.traceRay({ start: pos, dir: rd }, scene, depth + 1));
}

private getNaturalColor(thing: Thing, pos: Vector, norm: Vector, rd: Vector, scene: Scene) {
var addLight = (col, light) => {
var ldis = Vector.minus(light.pos, pos);
var livec = Vector.norm(ldis);
var neatIsect = this.testRay({ start: pos, dir: livec }, scene);
var isInShadow = (neatIsect === undefined) ? false : (neatIsect <= Vector.mag(ldis));
if (isInShadow) {
return col;
} else {
var illum = Vector.dot(livec, norm);
var lcolor = (illum > 0) ? Color.scale(illum, light.color)
: Color.defaultColor;
var specular = Vector.dot(livec, Vector.norm(rd));
var scolor = (specular > 0) ? Color.scale(Math.pow(specular, thing.surface.roughness), light.color)
: Color.defaultColor;
return Color.plus(col, Color.plus(Color.times(thing.surface.diffuse(pos), lcolor),
Color.times(thing.surface.specular(pos), scolor)));
}
}
return scene.lights.reduce(addLight, Color.defaultColor);
}

render(scene, ctx, screenWidth, screenHeight) {
var getPoint = (x, y, camera) => {
var recenterX = x =>(x - (screenWidth / 2.0)) / 2.0 / screenWidth;
var recenterY = y => - (y - (screenHeight / 2.0)) / 2.0 / screenHeight;
return Vector.norm(Vector.plus(camera.forward, Vector.plus(Vector.times(recenterX(x), camera.right), Vector.times(recenterY(y), camera.up))));
}
for (var y = 0; y < screenHeight; y++) {
for (var x = 0; x < screenWidth; x++) {
var color = this.traceRay({ start: scene.camera.pos, dir: getPoint(x, y, scene.camera) }, scene, 0);
var c = Color.toDrawingColor(color);
ctx.fillStyle = "rgb(" + String(c.r) + ", " + String(c.g) + ", " + String(c.b) + ")";
ctx.fillRect(x, y, x + 1, y + 1);
}
}
}
}


function defaultScene(): Scene {
return {
things: [new Plane(new Vector(0.0, 1.0, 0.0), 0.0, Surfaces.checkerboard),
new Sphere(new Vector(0.0, 1.0, -0.25), 1.0, Surfaces.shiny),
new Sphere(new Vector(-1.0, 0.5, 1.5), 0.5, Surfaces.shiny)],
lights: [{ pos: new Vector(-2.0, 2.5, 0.0), color: new Color(0.49, 0.07, 0.07) },
{ pos: new Vector(1.5, 2.5, 1.5), color: new Color(0.07, 0.07, 0.49) },
{ pos: new Vector(1.5, 2.5, -1.5), color: new Color(0.07, 0.49, 0.071) },
{ pos: new Vector(0.0, 3.5, 0.0), color: new Color(0.21, 0.21, 0.35) }],
camera: new Camera(new Vector(3.0, 2.0, 4.0), new Vector(-1.0, 0.5, 0.0))
};
}

function exec() {
var canv = document.createElement("canvas");
canv.width = 256;
canv.height = 256;
document.body.appendChild(canv);
var ctx = canv.getContext("2d");
var rayTracer = new RayTracer();
return rayTracer.render(defaultScene(), ctx, canv.width, canv.height);
}

exec();

编译生成后的raytracer.js代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
var Vector = (function() {
function Vector(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Vector.times = function(k, v) {
return new Vector(k * v.x, k * v.y, k * v.z);
};
Vector.minus = function(v1, v2) {
return new Vector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
};
Vector.plus = function(v1, v2) {
return new Vector(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
};
Vector.dot = function(v1, v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
};
Vector.mag = function(v) {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
};
Vector.norm = function(v) {
var mag = Vector.mag(v);
var div = (mag === 0) ? Infinity : 1.0 / mag;
return Vector.times(div, v);
};
Vector.cross = function(v1, v2) {
return new Vector(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);
};
return Vector;
}());
var Color = (function() {
function Color(r, g, b) {
this.r = r;
this.g = g;
this.b = b;
}
Color.scale = function(k, v) {
return new Color(k * v.r, k * v.g, k * v.b);
};
Color.plus = function(v1, v2) {
return new Color(v1.r + v2.r, v1.g + v2.g, v1.b + v2.b);
};
Color.times = function(v1, v2) {
return new Color(v1.r * v2.r, v1.g * v2.g, v1.b * v2.b);
};
Color.toDrawingColor = function(c) {
var legalize = function(d) {
return d > 1 ? 1 : d;
};
return {
r: Math.floor(legalize(c.r) * 255),
g: Math.floor(legalize(c.g) * 255),
b: Math.floor(legalize(c.b) * 255)
};
};
return Color;
}());
Color.white = new Color(1.0, 1.0, 1.0);
Color.grey = new Color(0.5, 0.5, 0.5);
Color.black = new Color(0.0, 0.0, 0.0);
Color.background = Color.black;
Color.defaultColor = Color.black;
var Camera = (function() {
function Camera(pos, lookAt) {
this.pos = pos;
var down = new Vector(0.0, -1.0, 0.0);
this.forward = Vector.norm(Vector.minus(lookAt, this.pos));
this.right = Vector.times(1.5, Vector.norm(Vector.cross(this.forward, down)));
this.up = Vector.times(1.5, Vector.norm(Vector.cross(this.forward, this.right)));
}
return Camera;
}());
var Sphere = (function() {
function Sphere(center, radius, surface) {
this.center = center;
this.surface = surface;
this.radius2 = radius * radius;
}
Sphere.prototype.normal = function(pos) {
return Vector.norm(Vector.minus(pos, this.center));
};
Sphere.prototype.intersect = function(ray) {
var eo = Vector.minus(this.center, ray.start);
var v = Vector.dot(eo, ray.dir);
var dist = 0;
if(v >= 0) {
var disc = this.radius2 - (Vector.dot(eo, eo) - v * v);
if(disc >= 0) {
dist = v - Math.sqrt(disc);
}
}
if(dist === 0) {
return null;
} else {
return {
thing: this,
ray: ray,
dist: dist
};
}
};
return Sphere;
}());
var Plane = (function() {
function Plane(norm, offset, surface) {
this.surface = surface;
this.normal = function(pos) {
return norm;
};
this.intersect = function(ray) {
var denom = Vector.dot(norm, ray.dir);
if(denom > 0) {
return null;
} else {
var dist = (Vector.dot(norm, ray.start) + offset) / (-denom);
return {
thing: this,
ray: ray,
dist: dist
};
}
};
}
return Plane;
}());
var Surfaces;
(function(Surfaces) {
Surfaces.shiny = {
diffuse: function(pos) {
return Color.white;
},
specular: function(pos) {
return Color.grey;
},
reflect: function(pos) {
return 0.7;
},
roughness: 250
};
Surfaces.checkerboard = {
diffuse: function(pos) {
if((Math.floor(pos.z) + Math.floor(pos.x)) % 2 !== 0) {
return Color.white;
} else {
return Color.black;
}
},
specular: function(pos) {
return Color.white;
},
reflect: function(pos) {
if((Math.floor(pos.z) + Math.floor(pos.x)) % 2 !== 0) {
return 0.1;
} else {
return 0.7;
}
},
roughness: 150
};
})(Surfaces || (Surfaces = {}));
var RayTracer = (function() {
function RayTracer() {
this.maxDepth = 5;
}
RayTracer.prototype.intersections = function(ray, scene) {
var closest = +Infinity;
var closestInter = undefined;
for(var i in scene.things) {
var inter = scene.things[i].intersect(ray);
if(inter != null && inter.dist < closest) {
closestInter = inter;
closest = inter.dist;
}
}
return closestInter;
};
RayTracer.prototype.testRay = function(ray, scene) {
var isect = this.intersections(ray, scene);
if(isect != null) {
return isect.dist;
} else {
return undefined;
}
};
RayTracer.prototype.traceRay = function(ray, scene, depth) {
var isect = this.intersections(ray, scene);
if(isect === undefined) {
return Color.background;
} else {
return this.shade(isect, scene, depth);
}
};
RayTracer.prototype.shade = function(isect, scene, depth) {
var d = isect.ray.dir;
var pos = Vector.plus(Vector.times(isect.dist, d), isect.ray.start);
var normal = isect.thing.normal(pos);
var reflectDir = Vector.minus(d, Vector.times(2, Vector.times(Vector.dot(normal, d), normal)));
var naturalColor = Color.plus(Color.background, this.getNaturalColor(isect.thing, pos, normal, reflectDir, scene));
var reflectedColor = (depth >= this.maxDepth) ? Color.grey : this.getReflectionColor(isect.thing, pos, normal, reflectDir, scene, depth);
return Color.plus(naturalColor, reflectedColor);
};
RayTracer.prototype.getReflectionColor = function(thing, pos, normal, rd, scene, depth) {
return Color.scale(thing.surface.reflect(pos), this.traceRay({
start: pos,
dir: rd
}, scene, depth + 1));
};
RayTracer.prototype.getNaturalColor = function(thing, pos, norm, rd, scene) {
var _this = this;
var addLight = function(col, light) {
var ldis = Vector.minus(light.pos, pos);
var livec = Vector.norm(ldis);
var neatIsect = _this.testRay({
start: pos,
dir: livec
}, scene);
var isInShadow = (neatIsect === undefined) ? false : (neatIsect <= Vector.mag(ldis));
if(isInShadow) {
return col;
} else {
var illum = Vector.dot(livec, norm);
var lcolor = (illum > 0) ? Color.scale(illum, light.color) :
Color.defaultColor;
var specular = Vector.dot(livec, Vector.norm(rd));
var scolor = (specular > 0) ? Color.scale(Math.pow(specular, thing.surface.roughness), light.color) :
Color.defaultColor;
return Color.plus(col, Color.plus(Color.times(thing.surface.diffuse(pos), lcolor), Color.times(thing.surface.specular(pos), scolor)));
}
};
return scene.lights.reduce(addLight, Color.defaultColor);
};
RayTracer.prototype.render = function(scene, ctx, screenWidth, screenHeight) {
var getPoint = function(x, y, camera) {
var recenterX = function(x) {
return(x - (screenWidth / 2.0)) / 2.0 / screenWidth;
};
var recenterY = function(y) {
return -(y - (screenHeight / 2.0)) / 2.0 / screenHeight;
};
return Vector.norm(Vector.plus(camera.forward, Vector.plus(Vector.times(recenterX(x), camera.right), Vector.times(recenterY(y), camera.up))));
};
for(var y = 0; y < screenHeight; y++) {
for(var x = 0; x < screenWidth; x++) {
var color = this.traceRay({
start: scene.camera.pos,
dir: getPoint(x, y, scene.camera)
}, scene, 0);
var c = Color.toDrawingColor(color);
ctx.fillStyle = "rgb(" + String(c.r) + ", " + String(c.g) + ", " + String(c.b) + ")";
ctx.fillRect(x, y, x + 1, y + 1);
}
}
};
return RayTracer;
}());

function defaultScene() {
return {
things: [new Plane(new Vector(0.0, 1.0, 0.0), 0.0, Surfaces.checkerboard),
new Sphere(new Vector(0.0, 1.0, -0.25), 1.0, Surfaces.shiny),
new Sphere(new Vector(-1.0, 0.5, 1.5), 0.5, Surfaces.shiny)
],
lights: [{
pos: new Vector(-2.0, 2.5, 0.0),
color: new Color(0.49, 0.07, 0.07)
}, {
pos: new Vector(1.5, 2.5, 1.5),
color: new Color(0.07, 0.07, 0.49)
}, {
pos: new Vector(1.5, 2.5, -1.5),
color: new Color(0.07, 0.49, 0.071)
}, {
pos: new Vector(0.0, 3.5, 0.0),
color: new Color(0.21, 0.21, 0.35)
}],
camera: new Camera(new Vector(3.0, 2.0, 4.0), new Vector(-1.0, 0.5, 0.0))
};
}

function exec() {
var canv = document.createElement("canvas");
canv.width = 600;
canv.height = 600;
document.body.appendChild(canv);
var ctx = canv.getContext("2d");
var rayTracer = new RayTracer();
return rayTracer.render(defaultScene(), ctx, canv.width, canv.height);
}
exec();

运行效果:

五、ECMAScript

它是一种由ECMA国际(前身为欧洲计算机制造商协会)制定和发布的脚本语言规范,javascript在它基础上经行了自己的封装。但通常来说,术语ECMAScript和javascript指的是同一个。业界所说的ECMAScript其实是指一种规范,或者说是一个标准。具体点来说,它其实就是一份文档

JS包含三个部分:ECMAScript(核心)、DOM(文档对象模型)、BOM(浏览器对象模型),ECMAScript是js语言的基础。

  1. ECMAScript3新增了对正则表达式、新控制语句、try-catch异常处理的支持,修改了字符处理、错误定义和数值输出等内容。标志着ECMAScript成为了一门真正的编程语言。
  2. 第四版于2008年7月发布前被废弃。
  3. ECMAScript5力求澄清第3版中的歧义,并添加了新的功能。新功能包括:原生JSON对象、继承的方法、高级属性的定义以及引入严格模式。
  4. ECMAScript6是继ES5之后的一次主要改进,增添了许多必要的特性,例如:模块和类以及一些实用特性,Maps、Sets、Promises、生成器(Generators)等。

浏览器支持:

一般来说,除了针对个别特性的特殊说明,各大主流浏览器都支持es5,包括

Chrome 13+

Firefox 4+

Safari 5.1*

IE 9*

其中IE9不支持es的严格模式,从IE10开始支持。Safari 5.1不支持 Function.prototype.bind

IE8只支持defineProperty、getOwnPropertyDescriptor的部分特性和JSon的新特性,IE9支持除了严格模式以外的新特性,IE10和其他主流浏览器都支持了。
因此在PC端开发的时候,要注意IE9以下的兼容,移动端开发时,可以比较放心了。

版本:

1995年,网景浏览器发布,包含一种脚本语言叫LiveScript

1996年,网景为搭上热炒Java的顺风车,将LiveScript改名为Javascript, 并提供给ECMA International进行标准化

1997年,ECMAScript1发布

1998年,ECMAScript2发布

1999年,ECMAScript3发布

3.0版是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了JavaScript语言的基本语法,以后的版本完全继承。直到今天,初学者一开始学习JavaScript,其实就是在学3.0版的语法。

2007年,ECMAScript4.0版草案发布,本来预计次年8月发布正式版本。但是,各方对于是否通过这个标准,发生了严重分歧。以Yahoo、Microsoft、Google为首的大公司,反对JavaScript的大幅升级,主张小幅改动;以JavaScript创造者Brendan Eich为首的Mozilla公司,则坚持当前的草案。

2008年, 由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激烈,ECMA开会决定,中止ECMAScript 4.0的开发,将其中涉及现有功能改善的一小部分,发布为ECMAScript 3.1,而将其他激进的设想扩大范围,放入以后的版本,由于会议的气氛,该版本的项目代号起名为Harmony(和谐)。会后不久,ECMAScript 3.1就改名为ECMAScript 5。

2009年,ECMAScript5发布,Harmony项目则一分为二,一些较为可行的设想定名为JavaScript.next继续开发,后来演变成ECMAScript 6;一些不是很成熟的设想,则被视为JavaScript.next.next,在更远的将来再考虑推出。TC39委员会的总体考虑是,ES5与ES3基本保持兼容,较大的语法修正和新功能加入,将由JavaScript.next完成。当时,JavaScript.next指的是ES6,第六版发布以后,就指ES7。TC39的判断是,ES5会在2013年的年中成为JavaScript开发的主流标准,并在此后五年中一直保持这个位置。

2011年,ECMAScript5.1发布

2015年,ECMAScript6发布,同年决定以后将多年一次的完整的新版本发布改为一年一次的新特性版本的发布,因此ES6又叫ES2015

2017年,ECMAScript8(EcmaScript 2017)在6月底由TC39正式发布

六、总结

coffeescript已经过去了,除非你对它非常熟悉,否则建选择typescript。

因为ECMAScript6的出现,javascript比以前要完善一些,但浏览器的支持度还是不够,但是有一天当JavaScript变得足够完善时这些中间语言就没有太多市场了。

上面提到的4种预处理工具都可以加快开发速度,某些程度上可以提高代码质量。

至于学习的方法我认为官网有详细的帮助。

总的来说要选择:Coffeescript、TypeScript或ES6都有争议。

参考网站:http://www.peise.net/tools/web/



切换样式的参考代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title></title>
</head>

<body>
<h1>Hello Sass!</h1>

<button type="button" class="color" data-color="css/red.css">红色</button>
<button type="button" class="color" data-color="css/blue.css">蓝色</button>
<link rel="stylesheet" type="text/css" href="#" id="css1"/>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript">
$(".color").click(function(){
var file=$(this).data("color");
$("#css1").attr("href",file);
});
</script>
</body>

</html>

本文链接: https://netlover.cn/2018/09/05/qianduanyuchuli.html