【译】CSS Variables 快速上手

关于如何快速开始的教程

CSS 自定义属性(也称之为变量)对于前端开发人员来说是一个制胜点。为 CSS 中引入了变量,算是为 CSS 赋予了新的力量,如此一来可以让代码变得更加简洁、灵活。

另外,CSS 原生的变量属于 DOM 的一部分,和预处理器中的不同,相比之下带来了更多的好处。所以更像是 SASS 和 LESS 中的类里的变量。在这篇文章中,我将带你快速学习这项新技术。

为什么要用 CSS 变量

有很多理由让你在 CSS 中使用变量,其中最引人注目的一点就是减少你样式文件中的重复代码。(单纯的描述性语言中,难免会有很多重复)

1
2
3
4
5
6
7
8
/* The old way */
#title {
color: #ff6f69;
}

.quote {
color: #ff6f69;
}

在上面的例子中,我们可以通过创建一个变量,比单纯的去重复书写要好一些,就像我们的如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
/* The new way */
:root {
--red: #ff6f69;
}

#title {
color: var(--red);
}

.quote {
color: var(--red);
}

这样做不仅仅使你的代码更容易阅读,而且在你要更改色值的时候也提供了更多的灵活性。

如今 SASS 和 LESS 变量确实可以继续使用下去,但是,CSS 变量有一些很大的好处。

  1. 它们不需要任务转移工作,因为是浏览器原生属性。所以你不需要做任何设置就可以开始使用,就像你使用 SASS 和 LESS 一样。
  2. 它们属于 DOM,从而带来了很多好处,我们加来本文和后续的课程中介绍这些。

现在让我们开始学习 CSS 变量!

声明你的第一个 CSS 变量

要声明一个变量,首先需要确定变量存在的范围。如果作为全局变量使用它,就要在 :root 这个伪类上声明。这样就能匹配到文档树中的根元素(通常是 <html> 标签)

随着变量被继承,整个 web 项目中的变量都可以用,因为所有的 DOM 元素都是 <html> 标签的后代元素。

1
2
3
:root {
--red: #ff6f69;
}

正如你所看到的,你可以像设置任何一个 CSS 属性那样去声明一个变量,变量名称必须以两个连接符开始。

要访问到变量,需要使用 var() 函数,并将变量名作为参数传入,如下:

1
2
3
#title {
color: var(--main-color);
}

声明一个局部变量

当然了,也可以声明一个局部变量,这些局部变量只能对其声明的元素及其它的子元素访问。如果知道某个变量仅用于应用程序的特定部分(或多个部分),则这是有意义的。

例如,你可能有一个 alert box 它用到了某个特殊的色值,这个色值不会在项目中其余的地方使用。在这种情况下,避免将其定义为全局变量是有道理的:

1
2
3
.alert {
--alert-color: #ff6f69;
}

这种变量只能用在它的子元素上:

1
2
3
4
.alert p {
color: var(--alert-color);
border: 1px solid var(--alert-color);
}

如果你尝试将这个局部变量在程序的其他地方使用,比如用在 navbar 上没有任何作用。浏览器会活忽略这行 CSS 代码。

通过变量更容易相应

CSS 变量的一大优点是它可以访问 DOM。而对于 LESS、SASS,情况并非如此,它们的变量被便宜为常规的 CSS 代码。

在实践中,你可以像下面的例子这样,修改基于屏幕宽度的变量:

1
2
3
4
5
6
7
8
9
:root {
--main-font-size: 16px;
}

media all and (max-width: 600px) {
:root {
--main-font-size: 12px;
}
}

通过这几行简单的代码,你可以为整个应用在小屏幕上做好字体的适配。这样的处理方式是不是很优雅?

通过 JavaScript 访问 CSS 变量

使用 DOM 内部的属性的另一个优点就是你可以通过 JavaScript 访问、更新变量便于交互。如果你想让你的用户能够更改你的网站(比如调整字体大小),这非常合适。

让我们继续本文开始的例子。在 JavaScript 中拿到 CSS 变量只需要如下 3 行代码。

1
2
3
4
5
6
var root = document.querySelector(':root');
var rootStyles = getComputedStyle(root);
var mainColor = rootStyles.getPropertyValue('--main-color');

console.log(mainColor);
--> '#ffeead'

更新 CSS 变量,只需在你声明的变量上调用 setProperty 方法,将变量名作为第一个参数,并将新值作为第二个参数传入。

1
root.style.setProperty('--main-color', '#88d8b0')

这种主色可以改变你的应用程序的外观,所以它是允许用户设置你的网站的主题的最佳途径。

浏览器支持情况

目前全球 77% 的网站支持 CSS 变量,美国接近 90%。我们已经在 Scrimba.com 上使用 CSS 变量有一段时间了,因为我们的观众大都精通技术,而且大多数都是用现代浏览器。

Can I use CSS Variables (Custom Properties)

好了,就是这样。我希望你学到了一些东西。

如果你想好好的学习一番,请务必在 Scrimba 上查看我的免费 CSS Variables 课程。

致谢

原作地址 - Learn CSS Variables in 5 minutes

PDF.js 本地部署

项目需求

  1. 我们产品的桌面版有输出 pdf 功能,并且能借助浏览器预览和打印,好的是 chrome 和 firefox 中都集成了 PDF.js 这样就能很好的实现这些功能。

  2. 然后新的需求又来了,PDF.js 在浏览器中保持默认的设置,功能比较齐全,然后还想设置一些限制在其中,就感觉特别乏力了,最终也不了了之。

  3. 接着又是一波新需求,在手机中要能查看 pdf。好了,这下不得不考虑将 PDF.js 集成到项目中了。

实现细节

基础

项目地址:https://github.com/mozilla/pdf.js

PDF.js、pdf.js是一款使用HTML5 Canvas安全地渲染PDF文件以及遵从网页标准的网页浏览器渲染PDF文件的JavaScript库。

这里有详细的说明文档,如果不想自己编译的话直接去 releases 里下载合适的版本即可,下载解压后的目录如下:

1
2
3
4
./pdfjs-1.10.88-dist
├── build/
├── web/ 这里根据项目需求,最终会更换成`mobile-viewer`目录里的资源
└── LICENSE

打开各目录看了之后,其中web目录中就是 PDF.js 将要用到的预览容器和部分资源,而 viewer.html 就是 PDF.js 浏览要用到的页面,是成品哦!build里面是核心的库文件。这时你直接打开 viewer.html 后直接看到的就是最终效果。

如果不能正常浏览,就需要在 web 服务器中,才能正常浏览。如果没有可以借助文档中,编译的那部分,借助 node.js 和 gulp 很快也能搭起来,顺便也可以自己编译。而且也能看到后续我们需要的 mobile 版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git clone git://github.com/mozilla/pdf.js.git
$ cd pdf.js
$ npm install -g gulp-cli
$ npm install
$ gulp server

...

$ gulp server
[11:33:40] Using gulpfile D:\Documents\GitHub\pdf.js\gulpfile.js
[11:33:40] Starting 'server'...

### Starting local server
Server running at http://localhost:8888/

这时在浏览器中就能看到预览效果,PDF.js Online demo。不得不服,这么强大的东西,各设备适配还是很棒的!

  1. 桌面版
    pdfjs_desktop

  2. 桌面版在手机端的效果
    pdfjs_desktop_portrait
    pdfjs_desktop_landscape

再回顾下刚才用 clone 的文件,其中 example 文件夹里列出来了各种应用场景下的例子,你会惊喜的看到 mobile-viewer 文件夹,直接去浏览即可看到移动端的适配效果。

  1. 手机版
    pdfjs_desktop_portrait
    pdfjs_desktop_landscape

mobile-viewer 文件夹里对应的各文件,就是手机版要用到的,也就是最终要合进项目中的。

进阶

以上这些预览效果都是目录里自带的文件compressed.tracemonkey-pldi-09.pdf,根据实际的渲染效果和等待时间,各设备略有差异。

通过搜索viwer.js中可以发现这个文件的出处:

1
var DEFAULT_URL = '../../web/compressed.tracemonkey-pldi-09.pdf';

这里是固定文件的访问。如果想在自己的网站内用 PDF.js 打开任意 pdf 文件,可以这样调用:
Setup PDF.js in a website

1
<a href="/web/viewer.html?file=%2Fyourpdf.pdf">Open yourpdf.pdf with PDF.js</a>

我们的应用场景是,这个页面内既能浏览 pdf,还有别的内容要展示。这样一来好端端的页面就被破坏了 ╮(╯▽╰)╭。目前就尝试着在这个页面内潜入 iframe,在 iframe 中潜入了 viewer.html 然后在file参数上传入 pdf 的路径……再根据实际情况,对皮肤也做了定制,最终效果图就不展示了 /(ㄒoㄒ)/~~

问题

常见的有跨域问题,这些搜索之后都能解决。但是我们的服务器端也配置过了,直接浏览传入file参数之后,还是不能访问。后来换用 iframe 调用之后没出现过跨域问题,但是又有了新的问题file origin does not match viewer's,这是一项安全措施,大致意思是说你打开的协议和 url 的协议不匹配,难道是因为那个 file 什么的?将这部分注释掉即可。

好了,内测貌似没什么问题了,接着又加了部分需求。准备上线测试,结果发现 PDF.js 在安卓版的微信中打开时,当前页面会直接关闭,然而高贵的 iOS 却好好的,搜了一下相关问题,真的是太可怕了,到目前为止,这个问题还在继续研究中!因为同一部手机,有的数据就会意外关闭,有的却能正常浏览。

grunt-spritesmith 使用笔记

好久以前介绍了 利用 Compass 生成 Sprite 图片,后来在使用中也表现良好,后来对于 retina 屏幕做适配,发现其并不能满足需求,尝试了一番工具之后,找到这个工具。在一番适配之后,发现能满足目前的需求,状况还不错。
其实在长期使用 Grunt 之后,发现这些类似的构建工具的确能够帮我处理很多繁琐的任务,但是前期的学习成本对我来说还是有的。而且对于这些工具,一旦有适合你需求的插件,那当然没问题;若是并不能满足你的需求,要么新找插件、要么自己写一个吧 <( ̄ˇ ̄)/,其实更多的精力还是放在代码本身好了,下面接入正题:

安装

1
npm install grunt-spritesmith --save-dev

设置

可根据 官方文档 和自身项目情况,配置 Gruntfile.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Spritesheet making utility
sprite: {
front: {
// 源文件地址
src: '<%= config.src %>/images/sprites/*.png',
// 对应的 2x 图片文件
retinaSrcFilter: '<%= config.src %>/images/sprites/*@2x.png',
// 生成的 sprites 文件
dest: '<%= config.src %>/images/sprites.png',
// 生成的 sprites-2x 文件
retinaDest: '<%= config.src %>/images/sprites-2x.png',
// 输出的 scss 文件,这里也可以输出 css 文件
destCss: '<%= config.src %>/sass/_sprites.scss',
// 针对需自定义的部分,做适应的配置,这里加了特殊的前缀
cssVarMap: function (sprite) {
sprite.name = 'icons-ipad-' + sprite.name;
}
}
}

文章里用到的相关代码,已经放在 Gist 上了。

1
2
3
4
Running "sprite:front" (sprite) task
Files "source/sass/_sprites.scss", "source/images/sprites.png", "source/images/sprites-2x.png" created.

Done, without errors.

grunt-spritesmith-handbook-1.png
grunt-spritesmith-handbook-2.png
这时候去看生成的 _sprites.scss 文件,这里为了方便管理 css 文件,所以输出了 scss 文件。然后使用的时候直接调用即可,项目中的图标是全部调用的,所以对应的代码如下,生成的 scss 文件中有官方的注释,很好理解:

1
2
@import sprite.scss;
@include retina-sprites($retina-groups);

问题

这时候一切看起来都很美好,貌似问题已经得到了解决。但是问题又来了,在生成的 _sprites.scss 中你会看到,每一个图标对应的语句中,都有 $icons-ipad-rightnav-xxx-image: '../images/sprites.png';,在编译后的 css 文件中每个图标 class 都会有 background-image: url(../images/sprites.png);,这个就是 node_modules/grunt-spritesmith/node_modules/spritesheet-templates/lib/templates 里的模板文件在起作用。模板文件提供了很多种类型的,我这里使用的是 scss 相关的,所以 Gist 里暂且只提供了对应的修改内容。

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
.icons-ipad-rightnav-fav {
background-image: url(../images/sprites.png);
background-position: -24px 0px;
width: 24px;
height: 24px;
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.icons-ipad-rightnav-fav {
background-image: url(../images/sprites-2x.png);
background-size: 48px 48px;
}
}

.icons-ipad-rightnav-line {
background-image: url(../images/sprites.png);
background-position: 0px -24px;
width: 24px;
height: 24px;
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.icons-ipad-rightnav-line {
background-image: url(../images/sprites-2x.png);
background-size: 48px 48px;
}
}

现在的目的就是想和 利用 Compass 生成 Sprite 图片 这里编译后生成的那种,公共属性只出现一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
.icons-sprite, .icons-bell, .icons-briefcase, .icons-globe, .icons-table_tennis, .icons-tree {
background: url('../images/icons-s274ef0b305.png') no-repeat;
}
.icons-bell {
background-position: 0 0;
height: 24px;
width: 24px;
}
.icons-briefcase {
background-position: -44px 0;
height: 21px;
width: 24px;
}

在查阅了一番资料后,还是没能很好的解决这个问题,而且对于自定义模板 cssTemplate 这部分也没有很好的理解,所以感觉解决方案没有那么完美。

解决方案

对应上面的问题,大概看了下 node_modules/grunt-spritesmith/node_modules/spritesheet-templates/lib/templates 里对应的 scss 相关模板,再查阅了部分资料,暂时有了如下的解决方案:

  1. node_modules/grunt-spritesmith/node_modules/spritesheet-templates/lib/templates/scss.template.handlebars 在这里面加入相应的代码,使生成的 scss 文件中,各个图标 clsss 只在这里输出,其中用到了属性选择符。

    1
    2
    3
    4
    5
    6
    7
    8
    {{#block "sprites-imageurl"}}
    i[class*="-ipad-"] {
    background-image: url({{{spritesheet.image}}});
    @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
    background-image: url({{{retina_spritesheet.image}}});
    }
    }
    {{/block}}
  2. scss.template.handlebarsscss_retina.template.handlebars 里删掉 name_image 相关的配置项;在 scss.template.handlebars 里删掉 sprite-image($sprite) 相关的 mixin 语句。

  3. 结合前面的 Grunt task,其中的 cssVarMap 就是和第 1 步中定义的 block 里的 class 名字是相互对应的。
  4. 再次去生成 scss 文件,再编译输出,来看看最终的效果
    grunt-spritesmith-handbook-3.png

相关资料

  1. stackoverflow 上的相关提问 Output a single base css class to specify background-image
  2. grunt-spritesmith issus Css rule optimization

暂且就这么多了,还是需要多多钻研。或许会有更好的解决办法,比如作者在上面的 issus 里提到的用 cssTemplate,这样不用动源模板,即使多人开发,也不会有影响。

推荐阅读:关于前端开发的行业现状和发展分析

我年后重新换了工作,年前的公司因为资金原因导致项目终止,纯属无奈啊。只好再次踏上求职之路,好的是这次找工作已经有了相应的经验,比两年前轻松了许多,但是从业经验增多伴随的也是用人单位对你个人的要求也变高。除了该有的专业技能之外就是个人的互联网从业经验和对产品的参与感,对团队的融入感等。面过几次之后就选择了目前就职的这家公司,项目领导是前领导,一方面是对于项目的认同,一方面是对公司的信任,希望在我们这个项目上,真的能像公司起初这样承诺的,从而实现最终的目标。

话说回来,个人价值的体现就是在这个产品里,最终的成败也是如此。所以一直以来,都是全身心的投入到产品其中,不单单只是个人工作的那一部分,因为是接触的新的行业,除过产品中接触到的之外,需要了解和知道的还有很多,所以在闲暇之余,我还会关注一些同行业的产品,看看他们的产品内容、定位、运营等,通过这样的比较才能更快的对行业有个了解,也对我们产品有更进一步的认知。

放在首位的还是个人专业技能的提升。对于 Front End Developer 人员来说,生活在互联网这个大环境之下,加上 Front End 技术发展如此之快,3 天一个小变换,3 个月一个大变化,这样如此之快的节奏,你得时刻做好充分的准备去适应。说实话,我有时候也很迷茫今后的发展 o-o

本来想来几句鸡汤金句,发现并不能够顺溜的打出来,还是省省吧。遇上了这个行业飞速发展的好时代,还有什么可犹豫的呢,加油!

知乎上的这个问题里的两则回答很棒,给自己看,没事了多思考!因为我觉得这些回答说的很清晰,分析的也很透彻。

推荐阅读

  1. 为什么市场上优秀的前端这么少,国外也是这样吗?

Compass Sprite

相信大家在前端开发这条路不归路上,都为此头疼过,干这件事情不仅费时费力还费眼睛,各种辅助线打上、网格划上,处处精确到像素级别,长期下来想必大家也都有像素对齐强迫症了,唉……说多了都是泪。那就是今天的主角 - 雪碧图(image spriting),至于他的介绍,他的好,他的工作原理也就不必多说了。

不过今天要介绍的新东西,或许能然你不在重操这些枯燥无味的动作,简单配置之后,一切就那么随意的,先放上官方的教程 Compass Sprite,下面重点介绍我的个人实践和一些使用经验。

文件准备

这里我找了 5 枚 24x24 的 Glyphicons 图标,icons 文件夹里放要拼合的图片资源
单个图片资源

1
2
3
4
5
6
7
images/
├── icons/
│ ├── bell.png
│ ├── tree.png
│ ├── table_tennis.png
│ ├── globe.png
└── └── briefcase.png

简单实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ------ style.scss ------
@import "icons/*.png"; // icons 是文件夹的名称
@include all-icons-sprites; // 这是 compass 定义好的 mixin,一次全部生成 icons 这个文件夹里的所有图片的雪碧图

// ------ style.css ------
.icons-sprite, .icons-bell, .icons-briefcase, .icons-globe, .icons-table_tennis, .icons-tree {
background: url('../images/icons-sbf1475184a.png') no-repeat;
}
.icons-bell {
background-position: 0 -48px;
}
.icons-briefcase {
background-position: 0 -72px;
}
// 省略部分

代码部分就是这些了,看一下之前的目录结构,发现在指定的图片文件夹 images 里已经生成了拼合好的雪碧图 icons-sbf1475184a.png,文件命名是由“指定部分-hash string”,每次在源图片改变时,更新缓存编译之后会生成新的雪碧图。
拼合好的雪碧图

这里再补充一下,官方文档里还导入了 sprite 组件,个人在使用中发现会报错,就去除了。编译时通过 compass command 是没有任何问题的。Compass Command Line Documentation

大概是和当前项目配置文件有关,所以建议在没有配置文件的前提下,先用 creat 命令创建项目,在对配置文件进行相应的更改。我是在 grunt-compass 中处理的,没有单独进行这些操作。

关于编译时路径报错,可以在 config.rb 里根据自己的需求,添加或更改配置项。Application Integration

手动指定类名

有时候会遇到这种情况,带有雪碧图属性的 class 自身还有其余属性,就需要单独去定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ------ style.scss ------
@import "icons/*.png";
.icons-bell {
@include icons-sprite(bell); // 注意 mixin 的名称要一致
}

// ------ style.css ------
.icons-sprite, .icons-bell {
background: url('../images/icons-sbf1475184a.png') no-repeat;
}
.icons-bell {
margin: 0 auto 30px;
background-position: 0 -48px;
}
// 省略部分

但是从生成的雪碧图,我看到的仍然将整个文件夹里的全部生成了,这里作为标记,等我去查查资料。

个性配置

  1. 输出布局 Sprite Layouts
    从图中我们可以看到,默认输出的雪碧图是按各个图片,垂直排列的。Compass 提供了:Vertical, Horizontal, Diagonal, Smart,默认以 Vertical 输出。

    1
    $icons-layout: smart; // {文件夹名称}-layout
  1. 控制间距
    为每个图片加入空白间隙,此处指定为 10px

    1
    $icons-spacing: 10px;
  2. 自动获取当前图片的尺寸

    1
    $icons-sprite-dimensions: true;

配置语句,都写在最前。

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
// ------ style.scss ------
$icons-sprite-dimensions: true;
$icons-spacing: 10px;
$icons-layout: horizontal;
@import "icons/*.png";
@include all-icons-sprites;

// Command
>>> Change detected at 23:04:38 to: sass-color.scss
remove images/icons-sfcd98984f1.png
create images/icons-s274ef0b305.png
overwrite css/sass-color.css

// ------ style.css ------
.icons-sprite, .icons-bell, .icons-briefcase, .icons-globe, .icons-table_tennis, .icons-tree {
background: url('../images/icons-s274ef0b305.png') no-repeat;
}
.icons-bell {
background-position: 0 0;
height: 24px;
width: 24px;
}
.icons-briefcase {
background-position: -44px 0;
height: 21px;
width: 24px;
}
// 省略部分

带参数后输出的图片

拓展阅读

  1. 如何禁止 Compass 对生成的 Sprite 图片名字添加随机字符?

Grunt - LiveReload页面自动刷新

在整理这篇文章的时候,默默的看了下键盘上的F5,是不是闪闪发亮,油头粉面的。和他一样的当然也少不了 ctrl alt s。因为传统的流程就是编辑器里 codding,ctrl+s,浏览器里 F5 刷新浏览,即使是你熟练于心、一气呵成。但是当你使用了自动刷新之后再配上强大的编辑器,剩下的只是专心 codding 了,若是配备双屏显示器,那效率绝对成倍成直线飙升。

先介绍下要用到的插件,具体安装就不单独说明了,每个插件也都有对应的文档说明:

  • grunt-contrib-connect
    静态文件服务器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // Gruntfile.js

    // 配合 livereload 使用,建立本地静态调试环境
    // https://www.npmjs.org/package/grunt-contrib-connect
    connect: {
    options: {
    port: 11011,
    hostname: "localhost",
    base: ".",
    livereload: 35729
    },
    all: {
    options: {
    open: true,
    base: [
    "."
    ]
    }
    }
    },
  • grunt-contrib-watch
    监视文件的变化

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

    // 资源监控,主要用在开发时自动刷新浏览器
    // https://www.npmjs.org/package/grunt-contrib-watch
    watch: {
    options: {
    spawn: false
    },
    style: {
    files: ["sass/**/*.scss"],
    tasks: ["compass"]
    },
    livereload: {
    files: [
    "./theme*/*.html",
    "./theme*/*.css"
    ],
    options: {
    livereload: '<%= connect.options.livereload %>' // this port must be same with the connect livereload port
    }
    }
    },
  • 配置任务

    1
    2
    3
    // Gruntfile.js

    grunt.registerTask("livereload", ["connect:all", "watch"]);

通过这次记录,回过头又翻了一下官方文档,发现在目前这个 basic 配置只是勉强能用,还有很多需要改进提升的地方。因为插件也在不断更新,加入了更多的功能。在 connectwatch 的主页看到,功能还是很丰富的。特别是 content 里已经有了 open 功能,那样的话就会少用到一个插件。

通过最近在 Grunt 中的尝试,用在项目中只是略微在开发过程中提升了效率,也只是基础使用,有空了看看说明文档,发现更多高级的功能,还是很不错的。比如有时候插件在更新之后会加入新特性,而自己的配置文件只是起初对于项目适用之后,就不会再做更新和改进,思想还是停留在起初到头来并没有多少提升,还是着重要培养思想。

更新:后来看了文档,grunt-contrib-watch 已经内置 Livereload,以上的内容我做了修改。

Grunt - imagemin图像压缩

项目前端构建部分引入了 Grunt,便于各项处理任务的自动化管理和运行,重复性的任务都交给工具去完成,代码编译、规范校验、单元测试等,一次配置终生受用!他会按照你预先设定好的顺序自动执行各个任务。

关于 Grunt 的前世今生我就不做介绍了,这里只做使用介绍,也是项目开发中的部分经验积累和记录。

项目上线前,都要对网站进行各项测试,前端这部分我们首要考虑的是对图片的压缩,因为我用 PageSpeed 检测的时候,这部分直接飙红了,提示压缩后最大限度降低有效负荷,这都是在开发时,切图没有输出最优图片而导致。以前处理方式,或许会在工具里重新输出图片,还有 Yahoo - Smush.it 上传处理等……这些工具在很大程度上也帮我们不少忙,但难免在你开发的时候要多工具的来回切换,输出保存等。今天我们借助 Grunt 就能很轻松的完成这些任务,而且是在你不经意之间就会默默的完成,是不是很人性、很省心!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 安装插件
npm install grunt-contrib-imagemin --save-dev
// 加载插件(Gruntfile.js)
grunt.loadNpmTasks('grunt-contrib-imagemin');
// 配置插件(图片压缩)
imagemin: {
dynamic: {
options: {
optimizationLevel: 3 // png图片优化水平,3是默认值,取值区间0-7
},
files: [
{
expand: true, // 开启动态扩展
cwd: "images/", // 当前工作路径
src: ["**/*.{png,jpg,gif}"], // 要出处理的文件格式(images下的所有png,jpg,gif)
dest: "images/" // 输出目录(直接覆盖原图)
}
]
}
},

配置好之后,运行 grunt imagemin

1
2
3
4
5
6
Running "imagemin:dynamic" (imagemin) task
✔ images/about_1.jpg (already optimized)
...
✔ images/sprites-s4e83c27da3.png (saved 10.92 kB - 8%)
...
Minified 69 images (saved 255.81 kB)

在安装插件的时候,会出现 pre-build...failed 相关字眼的错误,就试试删除后,再重新安装。或者先安装 npm install jpegtran-bin --save-dev,应该是这个和 imagemin 有依赖关系。

其中 option 里还有一些高级配置,关于 optimizationLevel 也有详细的解释,有兴趣的可以详细阅读。grunt-contrib-imagemin

  1. Grunt 中文社区 - http://gruntjs.org/
  2. Grunt 官网 - http://gruntjs.com/

Sass 学习笔记 - 颜色函数

这篇主要是记录下日常工作中常用的颜色函数,虽然不是射击狮但是要有一颗爱设计的心!

自从开始在项目中使用 Sass 之后,对其各个基本功能都得接触到。熟练使用工具,事半功倍提高搬砖效率,老板要是看到我这话,会给加薪吗?

在接手设计稿之后,不一定各处都给你标注的非常具体详细。难免有时候得自己动手,借助别的工具去获取颜色值,然后再相应的去拓展。有时候也会用到比如相近色、互补色,调节饱和度、亮度……是不是听着都专业!毕竟不是以设计为主,其实我常用的就是 lighten()darken()saturate()desaturate() 这几个而已 >_<,这个主要是用在 backgroundborder:hover,要是在以前,我肯定要在取色工具里,然后在基础色上调节,自从有了颜色函数之后,就省事儿多了,继续停留在编辑器里搞定。

Sass 颜色函数文档Compass 颜色函数文档,下面就介绍几个我常用的:

1
2
3
4
// 以下使用的是定义的这些颜色变量,在颜色函数中既可以使用变量,也可以使用颜色值
$primary: #eb594a;
$dark: #2e3740;
$price: #f13536;
darken($color, $amount) & lighten($color, $amount)
1
2
3
4
5
6
color: darken($primary, 15%); // 亮度降低15%
background-color: lighten($dark, 10%); // 亮度提高10%
// === Compile ===
.func1 {
color: #d12817;
background-color: #43505e; }
desaturate($color, $amount) & saturate($color, $amount)
1
2
3
4
5
6
color: desaturate($primary, 15%); // 饱和度降低15%
background-color: saturate($dark, 10%); // 饱和度提高10%
// === Compile ===
.func2 {
color: #dc6559;
background-color: #293746; }
complement($color) 互补 & adjust-hue($color, $degrees) 色调调节
1
2
3
4
5
6
7
color: complement($price); // 基于 HSL 颜色标准将原色值旋转 180 度后获得相对应的颜色值
background: adjust-hue($price, 180deg); // 改变色调的度数改变颜色值,这里的 180deg 正好和上面的 complement 具有相同的功能
// === Compile ===
.func3 {
color: #35f1f0; }
.func4 {
color: #35f1f0; }
invert($color) 反色

反色函数制作红、绿、蓝的颜色值的反向,不可以改变透明度。

1
2
3
4
color: invert($price);
// === Compile ===
.func5 {
color: #0ecac9; }

transparentize($color, $amount) 透明化 & fade-out($color, $amount) 渐隐

这里在看文档的时候,有点不太明白,等明天在调色板里看看效果对比吧。

和下面介绍的函数效果正好相反,效果是让颜色变得更加不透明,但是为何还要加个参数呢,大概是在原有色值上“加重”这么多?

1
2
3
4
5
6
color: transparentize($primary, .3);
background-color: fade-out($primary, .3);
// === Compile ===
.fun6 {
color: rgba(235, 89, 74, 0.7);
background-color: rgba(235, 89, 74, 0.7); }

rgba($red, $green, $blue, $alpha)

在看了编译之后的结果,是不是感觉和 rgba() 函数的效果一致呢?好,那就顺带说一下这个函数吧:

1
2
3
color: rgba($primary, .3);
// === Compile ===
color: rgba(235, 89, 74, 0.3);

opacify($color, $amount) 不透明化 & fade-in($color, $amount) 渐现

实现的效果正好和上面的函数相反

1
2
3
4
5
6
color: fade-in($primary, .3);
background-color: opacify($primary, .3);
// === Compile ===
.fun7 {
color: #eb594a;
background-color: #eb594a; }

以上这 5 个函数都需要传递 alpha 值,必须小与 1(0 为完全透明)。不知大家发现了没,其中那两对效果相反的函数,其中透明化函数,传值和编译后的透明度值互补;而不透明化函数,传值后编译的结果却和原色值相同,不传值就会编译报语法错误。这个是个疑问,在书中翻了一下,只有很随意的 demo,没有做详细解释。看来还是要多实践,不然书就是白看了。去翻了下官方文档,才发现了其中的缘由:其中 color 这个参数,若不是以 rgba 格式写出 alpha 的值,那就默认是 1;与后面给出的 amount 参数相加或者相减,即使超过 1,结果还是 16 进制的色值。

1
2
3
4
5
color: fade-in(rgba(235, 89, 74, .3), .1);
background-color: opacify(rgba(235, 89, 74, .9), .3);
// === Compile ===
color: rgba(235, 89, 74, 0.4);
background-color: #eb594a;
grayscale($color)

将参数里的颜色值转换为相应的灰度颜色

1
2
3
color: grayscale(rgba(235, 89, 74, .3));
// === Compile ===
color: rgba(155, 155, 155, 0.3);

简单的函数简介就先记录这么几个,还有几个参数稍多的函数,目测下面这几个感觉更负责,伴随着功能那就更强大了!有兴趣的可以去官方文档围观学习。

mix($color1, $color2, [$weight])
scale-color($color, [$red], [$green], [$blue], [$saturation], [$lightness], [$alpha])
adjust-color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])

Sass 学习笔记 - 项目文件结构

项目开始之前接触过 Sass,这次开始开发之前已经下定决心,将 Sass 引入开发环境中。

起初大致是这么安排的:

1
2
3
4
5
6
7
8
9
10
11
12
sass /
├── _variables.scss
├── _mixins.scss
├── _reset.scss
├── _grid.scss
├── _buttons.scss
├── _iconfont.scss
......
├── _home.scss
├── _index.scss
......
├── style.scss

其中 _grid.scss 这种命名带有 _,都是当做 @import 导入使用的,这也是告诉 Sass 在编译的时候,不会编译输出这类文件。因为在使用 Grunt 时会监控目录整个目录的变化,从而再编译输出。

但是在使用中,后期发现随着代码量的增多,最终输出的是根目录下的 style.scss -> style.css 文件体积越来越大,虽然最终输出的时候加了 compressed 但是 200k 的体积还是不太令人满意。

而且你或许会觉得,sass 文件夹里文件杂乱,因为我们单个文件都是分门别类的命名,难免文件数目会逐渐增多。按照相应的使用场景,按需调用 @import,再输出。比如专题页里,_home.scss, _news.scss 这类的,页面所需的只是部分样式模块,这时这种拆分式的代码安排,然后再按需组织,是不是觉得非常得心应手呢。

比如 reset 这部分,我后来改成 normalize.css 并且还是 staticfile 的 CDN 服务;iconfont 这部分改为 Font-Awesome,也使用了 CDN 服务。

后来在逐渐使用中,发现了项目中的文件结构,不管怎么安排还是应该尽可能的让大家在组织起来都觉得顺手。所以在起初构建的时候,按照需求大致分配一下相应的功能模块,然后再具体实施,毕竟各个项目都有特性,但是基础部分应该都适用。

其实还有个更好的途径,就是去 Github 上观摩大神的项目,就能从中慢慢领悟。列举几个前端框架,大家可以参考学习。特别是 twbs,大概是从 3.1 之后加入了 Official Sass port,之前一直只有 Less。当然了也有别的朋友的 Repo,是由 Less -> Sass,现在已经有了官方版。

  1. bootstrap-sass
  2. foundation

Sass 学习笔记 - @extend, placeholder

最近在读 《Sass and Compass for Designers》中文版,之前关注过,等了一阵子之后看到中文版面世了,就迫不及待的买了过来,主要是英文水平太渣,只好看中文。

现在就 Sass 中的两项常用功能,也是作为初学者容易混淆的两个概念,作为读书笔记,记录一下。

使用 @extend 命令拓展现有代码

使用 @extend 命令可扩展另一种样式,可让任何样式继承其他样式定义好的属性和值。

我自己理解就是,多个元素的共有属性,就将这些共有属性定义为基础样式组建,然后其余个性化的组件,都是通过这个基础组件来扩展,之前在项目中用到的不多,通常分不清 @extendplaceholder 这两者的使用场景。

下面我们通过书中的代码,来进一步理解:见P76,书中列举的这个例子,经常都能用到。

对话框:基础通用提示、信息提示、错误提示、成功提示等,这个组件的共性很容易能找到,其余个性化的都是在这个基础上去拓展即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// variables
$text-color: #666;
$default-bg: #ddd;
$state-info-border: #369;
$state-warning-border: #fcf8e3;
// default
.alert {
color: $text-color;
padding: 1em;
background: $default-bg;
}
// info
.alert-info {
@extend .alert;
border: 1px solid $state-info-border;
}
// warning
.alert-warning {
@extend .alert;
border: 1px solid $state-warning-border;
}

先看一下编译之后的代码吧 :)

1
2
3
4
5
6
7
8
9
10
11
12
13
.alert, .alert-info, .alert-warning {
color: #666666;
padding: 1em;
background: #dddddd;
}

.alert-info {
border: 1px solid #336699;
}

.alert-warning {
border: 1px solid #fcf8e3;
}

其实,我本想试试 twbs 里的 alert 组件改写,无奈没有尝试成功。书中提示了,上述代码中,最终 定义的类 .alert 并没有使用,其实只充当了声明,让后续的用来拓展,这里就引出了 placeholder 来解决这个问题。

使用占位符选择器来扩展需要的样式

使用占位符选择器,来定义注视用来做扩展的那些代码。请注意开头位置的占位符选择器 %

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// variables
$text-color: #666;
$default-bg: #ddd;
$state-info-border: #369;
$state-warning-border: #fcf8e3;
// default
%alert {
color: $text-color;
padding: 1em;
background: $default-bg;
}
// info
.alert-info {
@extend %alert;
border: 1px solid $state-info-border;
}
// warning
.alert-warning {
@extend %alert;
border: 1px solid $state-warning-border;
}

再来看一下编译之后的代码吧 :)

1
2
3
4
5
6
7
8
9
10
11
12
13
.alert-info, .alert-warning {
color: #666666;
padding: 1em;
background: #dddddd;
}

.alert-info {
border: 1px solid #336699;
}

.alert-warning {
border: 1px solid #fcf8e3;
}

使用占位符选择器,出了扩展的代码职位,不会生存任何冗余代码。

不同的定义方式,对应不同的调用语法来扩展,还是代码看着最直观。