Sandal 项目中的 Scss 文件,可以看成有 2 部分组成,它们的包含关系如下:
core
├─ reset
└─ function
├─ variables
├─ mixin
├─ media-queries
└─ animation
ext
├─ icons
├─ helper
├─ grid
└─ page-slide
对于主体框架(core),我们可以以两种方式使引入,根据需要决定吧:
// 集成了所有的基础功能,并且不输出任何样式
@import 'node_modules/sass-sandal/function';
// 在引入基础功能的前提下,引入重置样式 _reset.scss,其实主体就是 normallize.css 啦!
@import 'node_modules/sass-sandal/core';
sandal 包括两个集合文件(core,function)和两个文件夹(core,ext)。其中 core 文件夹中为核心基础文件,包括 variables,media-queries,mixin,animation,reset;ext文件夹中是一些扩展文件,目前包括 svg icons,grid 网格系统,helper 基础 class,page-slide 为页面切换 class。
两个集合文件(core,function)导入的都是 core 中的文件,区别在于 core 除了提供基本功能之外还会生成一份 reset 样式,而 function 则只提供基本功能。至于 ext 中的文件则属于额外的一些模块扩展,可根据需求导入。
| 模块名 | 作用描述 |
|---|---|
| variables | 负责基础变量的文件,如常用的颜色,字体等变量。这个源码自己看看即可。 |
| media-queries | 负责响应式断点判断的文件,来自 paranoida 的 sass-mediaqueries。 |
| mixin | 负责功能方面的文件。这里我们大概分成三个部分,一个是混合部分即mixin(,一个是 placeholder 选择器部分即 %,最后就是我们的 function 函数部分。常用的 include 及 extend 就是在这里定义的。 |
| animation | 提供六组简单实用动画:fade-in/out, shrink-in/out, up-in/out, down-in/out,left-in/out,right-in/out。默认不产生样式,通过 include 调用。 |
| reset | 在 normalize 的基础上,根据目前我们大家的使用习惯进行了一些归零行动,及基础文字颜色,clearfix,box-sizing 等。 |
| 模块名 | 作用描述 |
|---|---|
| helper | 提供常用的基础 class,如 clearfix, full-width 等。 |
| grid | 移动端的网格系统,分为 row 和 col。 |
| icons | 分为绘制的 icon 和 svg 图标两种,可参考 icons 文件夹中的 icons.html。 |
| page-slide | 设置了两种页面进出的动画方式:左进右出和右进做出。 |
media query 其实是引用自另一个开源项目 Sass MediaQueries。
从源码来看,这个文件里其他所有的 mixin 都是基于以下这个主 mixin 生成的:
@mixin mq($args...) {
$media-type: 'only screen';
$media-type-key: 'media-type';
$args: keywords($args);
$expr: '';
@if map-has-key($args, $media-type-key) {
$media-type: map-get($args, $media-type-key);
$args: map-remove($args, $media-type-key);
}
@each $key, $value in $args {
@if $value {
$expr: "#{$expr} and (#{$key}: #{$value})";
}
}
@media #{$media-type} #{$expr} {
@content;
}
}
利用这个 mixin 我们基本上可以生成任何媒体查询了,下面看两个例子:
@include mq($max-width: 1000px) {
...
}
// 以上代码将会编译成
@media only screen and (max-width: 1000px) {
...
}
利用这个主生成器,我们可以进一步生成一些语法糖,供我们方便调用:
@mixin max-screen($max)
@include mq($max-width: $max) {
@content;
}
}
// 使用这个新定义的 mixin
@include max-screen(1000px) {
...
}
// 以上代码将会编译成
@media only screen and (max-width: 1000px) {
...
}
在这个文件中,作者给我们定义了大概十多种 mixin 语法糖,但有些例如 iphone4、ipad 等特殊机型分辨率的判断,我这边建议慎用!以下提供一些推荐的 mixin,从命名上就能看出其用途了:
screen(min-width, max-width, orientation)
min-screen(width)
max-screen(width)
screen-height(min-height, max-height, orientation)
min-screen-height(height)
max-screen-height(height)
landscape
portrait
其实我们用的最多的应该就是 min-screen、max-screen 这些了,另外如果是针对类似 iPad 或者 iPhone 6 Plus 等屏幕可翻转设备的话,landscape 和 portrait 也是非常实用的。
Mixin 应该是我们选择使用这种 css 预处理工具的一大理由之一,本框架中包含了一系列非常实用的 mixin 来简化我们的 css 编写以及进行模块化管理,最大限度的做到 DRY(Don't Repeat Yourself)。本文件中除了定义了一些 @mixin,还定义了一些 % 和 @function。
某些代码既定义了 @mixin,也定义了 %,开发者可以根据自己的需求或喜好调用。但是我建议谨慎使用 %,以下源码示例了这种巧妙的双定义法,只是如无必要,我们只用 @mixin 的代码就好:
@mixin center-block($extend: true) {
@if $extend {
@extend %center-block;
}
@else {
margin-left: auto;
margin-right: auto;
}
}
%center-block {
@include center-block(false);
}
我们可以用以下方法调用该方法:
.demo1 {
@include center-block;
}
.demo2 {
@extend %center-block;
}
// 将会编译成
.demo1, .demo2 {
margin-left: auto;
margin-right: auto;
}
center-block: 块对象居中
.demo {
@include center-block;
}
// 将会编译成
.demo {
margin-left: auto;
margin-right: auto;
}
clearfix: 用以自动清除内部 float 的浮动
.demo {
@include clearfix;
}
// 将会编译成
.demo::before,
.demo::after {
content: "";
display: table;
}
.demo::after {
clear: both;
}
hidden-clip: 只隐藏于视觉,屏幕浏览器可以阅读
.demo {
@include hidden-clip;
}
// 将会编译成
.demo {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
}
ellipsis: 当容器内字符溢出时,用...标识出来
.demo {
@include ellipsis;
}
// 将会编译成
.demo {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
ellipsis-lines: 多行省略,ellipsis-lines定义了多行省略,只支持老版本的 flex-box,咱忽略它!
word-break: 字内断行,妈妈再也不用担心我对不齐啦!
.demo {
@include ellipsis;
}
// 将会编译成
.demo {
white-space: normal;
word-wrap: break-word;
word-break: break-all;
}
disabled: 禁止样式设定,这里需要 _variables.css 里的预定义变量支持。且看编译完成的代码,为啥强行给这么多 !important?太暴力啦!
$colorDisabled: (text: #999, bg: #e3e3e3, border: #dbdbdb) !default;
.demo {
@include disabled;
}
// 将会编译成
.demo {
background-color: #e3e3e3 !important;
color: #999 !important;
cursor: default !important;
pointer-events: none !important;
}
ir: 图片替换文字,视觉上将这些文字给隐藏了
.demo {
@include ir;
}
// 将会编译成
.demo {
font: 0/0 a;
text-shadow: none;
border: 0 none;
color: transparent;
}
triangle: 利用厚边框的夹角生成小三角形,通常可以利用一个容器的伪元素来实现,减少 dom 节点
/*
接受三个参数
$direction: 三角形方向,有 top|right|bottom|left 四个值,默认 top
$borderWidth: 三角形底边宽度,默认 6px
$borderColor: 三角形颜色, 默认是变量表里的值
*/
.demo::after {
@include triangle($borderColor: gray);
}
// 将会编译成
.demo::after {
content: "";
height: 0;
width: 0;
overflow: hidden;
}
.demo::after {
border-bottom: 6px solid gray;
border-left: 6px dashed transparent;
border-right: 6px dashed transparent;
}
v-arrow: 方向箭头,一般在移动端的列表里还蛮常用的
/*
接受三个参数
$direction: 箭头方向,有 top|right|bottom|left 四个值,默认 right
$borderWidth: 箭头边宽, 默认 2px
$size: 箭头边长, 默认 10px
*/
.demo {
@include v-arrow;
}
// 将会编译成
.demo {
display: inline-block;
vertical-align: middle;
width: 10px;
height: 10px;
border-top: 2px solid currentColor;
border-right: 2px solid currentColor;
transform: rotate(45deg);
}
parent-state: 这个 @mixin 蛮有意思,改变父元素的状态,需注意,父元素不能有组合选择器,例如 .a, .b {}
.parent{
.child{
@include parent-state(":hover"){
color: #f00;
}
}
}
// 将会编译成
.parent:hover .child {
color: #f00;
}
animation-fade: 这个 @mixin 生成一个 keyframes,设置渐隐渐现的动画关键帧
/*
接受三个参数
$name: keyframes 的名称,默认 animationFade
$from: opacity的开始值, 默认为 0
$to: opacity 的结束值, 默认 false,不指定值
*/
@include animation-fade;
// 将会编译成
@keyframes animationFade {
from {
opacity: 0;
}
}
animation-translate: 这个 @mixin 生成一个 translate 的关键帧
/*
接受三个参数
name: keyframes 的名称,默认 animationTranslate
$from: translate 开始值,默认 y -100%
$to: translate 结束值,默认 false,不指定值
*/
@include animation-translate($from: xy -50% -50%, $to: y -100%);
// 将会编译成
@keyframes animationTranslate {
from {
transform: translate(-50%, -50%);
}
to {
transform: translate(0, -100%);
}
}
retina border: retina 屏幕下 0.5px 的实现方式,这个将单独开个页面专讲。
fixed: 给容器设置 fixed 定位
// 1. 不带参数,top 置顶
.fix-top {
@include fixed;
}
// 将会编译成
.fix-top {
position: fixed;
left: 0; right: 0;
top: 0;
}
// 2. 传入数值,为 top 赋值
.fix-px {
@include fixed(40px);
}
// 将会编译成
.fix-px {
position: fixed;
left: 0; right: 0;
top: 40px;
}
// 3. 传入 bottom 关键词,置底
.fix-bottom {
@include fixed(bottom);
}
// 将会编译成
.fix-bottom {
position: fixed;
left: 0; right: 0;
bottom: 0;
}
justify: Flex 模式下的 space-between 对齐方式(感觉莫意义啊~~)
.demo {
@include justify;
}
// 将会编译成
.demo {
display: flex;
justify-content: space-between;
}
equal-table: 以表格布局方式等分一行中的容器
/*
默认为 li 子元素等分
可以传入指定子元素选择符参与等分
eg: equal-table('a')
*/
ul {
@include equal-table;
}
// 将会编译成
ul {
display: table;
table-layout: fixed;
width: 100%;
}
ul li {
display: table-cell;
}
equal-flex: 以 flex 布局来等分一行中的子元素
/*
与 equal-table 类似,
也可传入指定子元素选择符
eg: equal-table('a')
*/
div {
@include equal-flex('a');
}
// 将会编译成
div {
display: flex;
}
div a {
flex: 1;
width: 1%;
}
line-equal-gap: 使用这个 @mixin 产生和 flex 布局中 jusify-content: space-around | space-between 那样的效果,所不同的是,这里是指定 gap 宽度而不管容器宽度的
/*
该 @mixin 可定制 3 个参数:
$gap: 间隔宽度,默认 10px
$child: 与上例类似,可指定子元素选择符,默认 'li'
$lr: 指定是否需要在两头也设置 gap,默认是 true
*/
div {
@include line-equal-gap('span');
}
// 将会编译成
div {
display: flex;
padding-left: 10px;
padding-right: 10px;
}
div span {
flex: 1;
width: 1%;
}
div span:not(:first-of-type) {
margin-left: 10px;
}
line-equal-item: 感觉这个就是 justify-content 的排布啊,默认为 space-around 排布,传入 false 参数则为 space-between 排布
/* 无参数(使用默认参数) */
.demo1 {
@include line-equal-item;
}
// 将会编译成
.demo1 {
display: flex;
justify-content: space-between;
}
demo1::before, demo1::after {
content: "";
}
/* false 参数,不要两头的 gap */
.demo2 {
@include line-equal-item(false);
}
// 将会编译成
.demo2 {
display: flex;
justify-content: space-between;
}
center-flex: 使用 flex 布局方式使容器中子元素居中,可传入 x 或 y 参数各自轴向居中,默认 both,横向和竖向同时居中
.center {
@include: center-flex;
}
// 将会编译成
.center {
display: flex;
justify-content: center;
align-items: center;
}
center-translate: 传统居中方式,先绝对定位,用 left: 50% 或 top: 50% 偏移后再用 translate 来修正,同样,接受 x 或 y 传参,默认 both
.center-y {
@include center-translate(y);
}
// 将会编译成
.center-y {
position: absolute;
top: 50%;
transform: translate3d(0, -50%, 0);
}
object-wrap: 这个 @mixin ... 貌似没看出应用场景啊(呆若木鸡.jpg)
/*
接受两个参数
$percent: wrap 的百分比,默认 100%
$child: 用于子元素选择器,默认 'img'
*/
.wrap {
@include object-wrap($child: span)
}
// 将会编译成
.wrap {
position: relative;
padding-top: 100%;
height: 0;
}
.wrap span {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
自苹果的智能手机 iPhone4 问世后,"retina" 一词就为人所津津乐道了。视网膜屏以 4 个像素描绘普通屏幕的 1 个像素,其显示的精致度大大提高了,因此,1px 宽度的细线,在 retina 屏上是 2 个物理像素的宽,使得 0.5px 的绘制宽度能够得以实现。下面介绍几种绘制 0.5px 的方法,在 sandal 中已经封装成 @mixin,已方便使用
该方法使用媒体查询,检查 webkit 浏览器的私有属性 -webkit-min-device-pixel-ratio,如果其值为 2 或 3(plus 机型) 则肯定支持 0.5px,该方法在 iOS9+ 中得以实现,使用场景不是很广泛,我们直接看源码吧
@mixin retina-one-px() {
@supports (border-width: 0.5px) {
@media only
screen and (-webkit-min-device-pixel-ratio: 2),
screen and (-webkit-min-device-pixel-ratio: 3) {
border-width: 0.5px;
}
}
}
// 调用
.demo {
@include retina-one-px;
border-top-style: solid;
}
其原理也很简单,绘制一个 50% 单位的纯色 linear-gradient 背景色后,用 background-size 属性将其限定在 1px 范围内,源码太长,就不贴了,如何引用见下,可惜的是,安卓4.3的机型不支持 background-size 的百分比取值
/*
支持两个传参
$direction: 可选关键词 top | right | bottom | left | v(左右俩边框) | h(上下俩边框) | all,默认 top
$color: 边框颜色,默认取变量表中的 $colorBorder
*/
.demo {
@include retina-one-px-bg;
}
该方法是目前为止最通用的一个方法,按照 css3 transform scale 的定义,理论上边框可以任意细(1/n px),直到设备的物理像素极限。所以,我们可以用 scale(0.5) 来缩小这一个像素的宽度。同样,sandal 为我们准备好了采用这种方法的 @mixin,需要注意的是,为不影响容器内本身的内容缩放,在这里,我们通常会将这个 border 的绘制运用在其伪元素上,这样就不影响正常内容了。
/*
支持两个参数
$direction: 可选关键词 top | right | bottom | left,默认 top
$color: 边框颜色,默认取变量表中的 $colorBorder
*/
.box {
position: relative;
&::befefore {
content: '';
@include retina-one-px-border;
}
}
这里,Sandal 提供了 2 个 @extend %border-tb 和 %border-all 来实现快速实现,上下边框和四个边框描绘的定义
// border top & bottom
.borer-tb {
@extend %border-tb;
}
// 将会编译成
.border-tb {
position: relative;
}
.border-tb::before {
content: "";
position: absolute;
left: 0;
top: 0;
box-sizing: border-box;
right: 0;
height: 0;
transform: scaleY(0.5);
border-top: 1px solid #dbdbdb;
z-index: 1;
}
.border-tb::after {
content: "";
position: absolute;
left: 0;
top: 0;
box-sizing: border-box;
right: 0;
height: 0;
transform: scaleY(0.5);
border-top: 1px solid #dbdbdb;
top: auto;
bottom: 0;
}
// border all
.border-all {
@extend %border-all;
}
// 将会编译为
.border-all {
position: relative;
}
.border-all::before {
content: "";
position: absolute;
left: 0;
top: 0;
box-sizing: border-box;
width: 200%;
height: 200%;
transform-origin: left top;
transform: scale(0.5);
border: 1px solid #dbdbdb;
z-index: -1;
}
这里是一些 _mixin.scss 文件中没有归类的 @minxin, @extend 和 @function,有些使用价值不大的代码就不贴出来了,反正留着也不会显性编译出来。
%bar-line: 独立 @extend,为列表绘制分割线,会依赖 retina-one-px-border 这个 @mixin 对 retina 屏绘制 0.5px 的超细线。
.bar-line {
@extend %bar-line;
}
%gap-item: 一个每行上下都绘有细线的列表样式,为了显示效果,同样需要 retina-one-px-border
.gap-item {
@extend %gap-item;
}
结合: 利用以上 @extend 则能生成一个非常标准的 list,并且在 retina 屏上会有 0.5px 的超细横线,效果可以看 这里
.bar {
@extend %item-v-right;
@extend %bar-line;
position: relative;
}
/*
以上代码很简单,但编译下来却是很长,
加上 autoprefixer 的处理,大概会生成 50 行代码
scss 的优势体现无疑
*/
%item-v-right: 右侧箭头跳转指向,需要依赖 v-arrow 这个 @mixin,需要注意的是右侧箭头是利用 ::after 伪元素生成的,其会进行 absolute 定位,所以主元素务必需要有一个 position: relative | absolute 的定位。
.item-v-right {
@extend %item-v-right;
position: relative;
}
通用 btn 样式定义: 以一个通用样式 %btn-basic 和 btn-size、btn-color 两个 @mixin 组合,可以定义多种样式的 button
.btn {
@extend %btn-basic;
}
// 将会产生一个通用的 button 样式
.btn {
display: inline-block;
vertical-align: middle;
cursor: pointer;
text-align: center;
border: 1px solid transparent;
box-sizing: border-box;
user-select: none;
padding: 0 1em;
white-space: nowrap;
}
/*
定义一类特殊用途 button
btn-size 默认参数(需参数表内预定义值支持):
($padding: 1em, $height: $barHeight, $radius: 3px)
btn-color 默认参数:
($colorText: #333, $colorBg: #666, $colorBorder: false)
*/
.btn-normal {
@include btn-size;
@include btn-color;
}
tint & shade:这是 2 个处理颜色的函数,其实质是采用 scss 内置的颜色处理函数 mix() 通过白色或黑色来混合给定的颜色,使得到的颜色变深或变浅,可以通过以下代码和示例来体验一下
.left {
background-color: tint(green, 40%);
}
.center {
background-color: green;
}
.right {
background-color: shade(green, 40%);
}
Sandal 为我们提供6组简单实用动画,这些动画定义,默认不产生样式,通过@include调用。其中淡入淡出那个,其实我们之前在 _mixin.scss 中已有定义,这里只是对其进行进一步的封装。
下面以 shrink-in/out 来举个例子,如何使用这些 @mixin:
/*
shrink-in 可传入两个参数
$className: 要生成的样式名主体,默认 shrink
$from:开始缩放时的大小,默认 0.815
*/
@include animation-shrink-in;
// 代码将编译为
.shrink-in {
animation-duration: 0.3s;
animation-fill-mode: both;
}
.shrink-in {
animation-name: shrinkIn;
}
@keyframes shrinkIn {
0% {
opacity: 0;
transform: scale(0.815);
}
100% {
opacity: 1;
transform: scale(1);
}
}
以下为所有 6 类 @mixin 的名称以及默认参数,供参考。
animation-fade-in($className: fade, $from: 0)animation-fade-out($className: fade, $to: 0)animation-shrink-in($className: shrink, $from: 0.815)animation-shrink-out($className: shrink, $to: 1.185)animation-down-in($className: down, $value: 100%)animation-down-out($className: down, $value: 100%)animation-up-in($className: up, $value: -100%)animation-up-out($className: up, $value: -100%)animation-left-in($className: left, $value: -100%)animation-left-out($className: left, $value: -100%)animation-right-in($className: right, $value: 100%)animation-right-out($className: right, $value: 100%)这是一个辅助类的预定义,根据需要可通过人工方式 import 进来。
这个模块内定义的内容很简单,大家看看源码就好了:
@charset "UTF-8";
//-----------------------------------------------------
// helper scss
//-----------------------------------------------------
.fl {
float: left;
}
.fr {
float: right;
}
.fs12 {
font-size: 12px;
}
.grayc {
color: #ccc;
}
.gray9 {
color: #999;
}
.gray6 {
color: #666;
}
.p10 {
padding: 10px;
}
.plr10 {
padding-left: 10px;
padding-right: 10px;
}
.mt10 {
margin-top: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.mlr10 {
margin-left: 10px;
margin-right: 10px;
}
// 左右两端对齐
.justify {
@extend %justify;
}
.ellipsis {
@extend %ellipsis;
}
.full-width {
width: 100%;
}
.pos-s {
position: static !important;
}
.fixed-bottom {
@include fixed(bottom);
}