• CSS – Sass & SCSS


    前言

    CSS 代码多了就不好管理了, 这是它语法先天的不足.

    Sass 就是加强它语法的, Sass 为 CSS 引入了一些 JS 语言的特性, 比如 variable, function, parameter, extend, import.

    Sass 的 variable 和 import (用于管理), 和 CSS 的 variable, import 不是一回事儿. 分开看待会更好理解.

    它的原理很简单, 就是解析 Sass 语法, 然后编辑去 CSS. 和 TS > JS 一样的道理.

    参考:

    Learn Sass In 20 Minutes | Sass Crash Course

    官方网站

    How to Compile?

    搭配 Webpack 的话, 看这篇 : Webpack 学习笔记

    这里使用的方式是 VS Code Extension.

    安装插件

    注: 它有 2 个同名字的哦, 旧的没有维护了 github issue, 旧的 version 使用 @use 会报错哦.

    点击 Watch Sass 开启 watching mode.

    这时, scss 文件就会被 compile 生成 css 文件了 (.map 是 debug 用的)

    Output window 一直弹出来的问题, 它默认是 information 所以一直会跳出来.

    如果不用 webpack 又不用 extension 也可以用 nodejs 单独版本, 看这篇: Youtube – Stop using an extension to compile Sass

    Variable

    CSS 的 variable 和 Sass 的 variable 不是一个概念. 不要把它们放一起看待.

    Sass 的 variable 是用来存变量的, 编辑完之后就没有. 

    CSS variable 是定义在 element 了, JS 是可以获取到和修改的. 总是完全不是一个世界的东西. 用途动机都不一样.

    $primary-color: red;
    $secondary-color: red;
    
    h1 {
      color: $primary-color;
    }
    p {
      color: $secondary-color;
    }

    定义变量是用 $ 符号, 使用就直接放去想用的地方就可以了.

    编译完成后 css file

    h1 {
      color: red;
    }
    p {
      color: red;
    }

    有了变量表达加分, 要批量换也容易, 修改加分. 

    Private Variable

    $_private-variable: blue;

    变量名字开头是下划线 _ 表示它是一个私有变量, 在 @use 的时候无法引用.

    Default Variable Value

    $primary-color: blue !default;

    !defualt, 这样 @use 的时候可以 override.

    当用于给 CSS variable 赋值

    参考: Breaking Change: CSS Variable Syntax

    直接把 Sass variable 赋值给 CSS variable 是错误的, 必须加上 #{}

    另外当值有 quote 的时候 #{} 依然是错的, 需要用 meta.inspect()

    Nested 嵌套

    CSS 是平的

    #hero-section h1 {
      color: red;
    }
    #hero-section h1:hover {
      color: black;
    }
    #hero-section h1:my-class {
      color: yellow;
    }
    #hero-section p {
      color: blue;
    }

    #hero-section 被重复了 3 次, #hero-section h1 被重复了 2 次

    而且不容易看出它们的关系.

    Sass 是可嵌套的, 它这样写.

    #hero-section {
      h1 {
        color: red;
        &:hover {
          color: black;
        }
        &.my-class {
          color: yellow;
        }
      }
      > p {
        color: blue;
      }
    }

    & 是同时的意思.

    这样就去除了重复的部分, 而且包在一起也增加了关系的表达.

    它编辑出来就和上面 CSS 版本一样.

    @keyframes

    Sass 允许把 @keyframes 写到某个 style code 里面.

    .container {
      animation: dada 2s ease forwards;
    
      @keyframes dada {
        100% {
          background-color: blue;
        }
      }
    }

    compile 以后会被放到 root.

    提醒: 因为最后是放到 root 的, 所以 keyframes 的名字不可以撞哦, 它可没有 scope 的概念, 只是简单的搬出去而已.

    @media

    media query 也不例外, 可嵌套

    .container {
      max-width: 100%;
      @media (min-width: $breakpoint-sm) {
        max- 540px;
      }
    }

    效果

    .container {
      max-width: 100%;
    }
    @media (min- 576px) {
      .container {
        max-width: 540px;
      }
    }

    parent selector

    参考: 

    Stack Overflow – Can I check parent element has specific class in sass?

    Docs – Parent Selector

    把 & 写在后面. 它会生成如下的 selector

    .blue 被放到了最前面, 变成了所谓的 "parent" 但其实是 ancestor. 这招 parent selector 也不是那么厉害, 一些小地方能用到罢了. 很多时候是不够用的.

    @use (前生是 @import)

    参考: 

    Stop using @import with Sass | @use and @forward explained

    2020年的css周边之sass用法指南 @use和@forward 的部分.

    早年是叫 @import 的, 后来换成 @use 了, 功能也改了一些.

    @use 可以理解为 module / namespace, 它让我们可以把 CSS 代码分散到多个 file 做管理. 通过 @use 引入来使用.

    基本用法

    最常见的就是 _variable.scss 了

    它的 file name 开头是下划线 _ 这表示这个 file 是需要被 @use 的, 所以 compiler 不会生成 _variable.css 文件.

    它的使用方式是:

    注意 @use 一定要放在顶部 (before style code, 如果 @use 前面是定义 variable 的话还 ok) , 它的 file name 不需要有下划线(_), 也不需要有 extension (.scss)

    调用 variable 的时候需要配上 namespace (file name)

    Change Namespace

    和 JS 的 import 类似, 可以通过 as 关键字换名字

    通过 as * 还能去掉 namespace 哦

    Override Default Variable

    $primary-color: blue !default;
    @use "./variable" as * with (
      $primary-color: yellow
    );

    使用关键字 with (variable : value) 可以覆盖 default variable.

    @forward

    参考: Sass Docs – @forward

    它有点像 Angular 中的 re-export. import from a,b,c then export as d 这种方式.

    比如有 variable1,2

    做一个 forward import variable 1,2

    @forward "./variable1" as first-*;
    @forward "./variable2" as second-*;

    as first-* 是添加 prefix, 防止撞名字, 如果没有撞名字的可能性,那可以不加

    使用

    @use "./forward";
    h1 {
      color: forward.$first-primary-color;
    }
    p {
      color: forward.$second-primary-color;
    }

    此外它还有 show, hide 可以指定要 export 的 members, 它也可以通过 with override default variable

    @use 和 @forward 的复杂例子

    有 3 个 files: _core.scss, _base.scss, style.scss

    core 是核心模块, base 是项目通用模块, style 是某个 page 的样式

    _core.scss

    @use "sass:list";
    @use "sass:math";
    
    @function map-get-next($map, $key) {
      $keys: map-keys($map);
      $values: map-values($map);
      $index: list.index($keys, $key);
      $count: length($keys);
      $next-index: $index + 1;
      @if ($next-index > $count) {
        @return null;
      }
      @return list.nth($values, $next-index);
    }
    $breakpoint: null !default;
    @function breakpoint($size) {
      @return map-get($breakpoint, $size);
    }
    @function breakpoint-next($size) {
      @return map-get-next($breakpoint, $size);
    }
    @mixin media-breakpoint-up($breakpoint) {
      @media (min-width: breakpoint($breakpoint)) {
        @content;
      }
    }
    @mixin media-breakpoint-down($breakpoint) {
      @media (max-width: breakpoint($breakpoint) - 0.02px) {
        @content;
      }
    }
    @mixin media-breakpoint-only($breakpoint) {
      $current: breakpoint($breakpoint);
      $next: breakpoint-next($breakpoint);
      @if ($next == null) {
        @media (min-width: $current) {
          @content;
        }
      } @else {
        @media (min-width: $current) and (max- $next - 0.02px) {
          @content;
        }
      }
    }
    @mixin media-breakpoint-between($from-breakpoint, $to-breakpoint) {
      @media (min-width: breakpoint($from-breakpoint)) and (max- breakpoint($to-breakpoint) - 0.02px) {
        @content;
      }
    }
    View Code

    4 个 breakpoint 的 mixin 和一个 map-get-next 小方法, breakpoint 需要外部输入 variables.

    _base.scss

    $breakpoint: (
      xs: 0,
      sm: 640px,
      md: 768px,
      lg: 1024px,
      xl: 1280px,
      "2xl": 1536px,
    );
    
    // 顺序是 as > show > with
    // show 的名字是有加 prefix 的
    @forward "./core" as core-* show core-map-get-next,
      core-media-breakpoint-up with (
        $breakpoint: $breakpoint
      );
    
    // 需要用 @use 当前文档才可以调用到 core
    // 不需要也不可以再设置 variables 了
    @use "./core";
    @mixin base-mixin {
      @include core.media-breakpoint-up("lg") {
        h1 {
          width: 30px;
        }
      }
    }

    有几个点需要注意

    1. @forward 使用 as, show/hide, with 的顺序是讲究的. as > show > with

    2. show 在 as 之后, 所以 show 的名字需要加上 prefix

    3. 如果当前 _base.css 也需要调用 core 方法的话, 还需要再 @use './core', @forward 不能让当前文档可调用 core 哦

    还有一点是 @use './core' 不能使用 with 修改变量了, 因为 @forward 已经指定了.

    style.scss

    @use "./base" as *;
    
    $obj: (
      my-color: red,
      my- 400px,
    );
    
    @include core-media-breakpoint-up("lg") {
      h1 {
        width: core-map-get-next($obj, "my-color");
      }
    }
    
    @include base-mixin();

    Operator

    Sass 允许我们直接写 operator

    h1 {
      font-size: 10px + 20px;
    }

    虽然说 CSS 可以用 calc 但是上面这种写法在 compile 之后就变成 30px 了. 性能可定比较好.

    @mixin and @include

    mixin 类似 JS 的 function, 它可以用来封装一段 style code, 想要 copy paste 的时候就调用.

    而且它可以用 parameter 做微调整哦.

    @mixin some-repeat-style {
      h1 {
        color: red;
      }
    }
    
    @mixin some-repeat-property-style {
      color: red;
    }
    
    @include some-repeat-style();
    @include name-repeat-style; /* 如果没有参数也可以不需要括弧 */
    
    p {
      @include some-repeat-property-style();
    }

    它就是把 mixin 内的 style code, copy paste 到 @include 的位置就对了.

    效果

    h1 {
      color: red;
    }
    
    p {
      color: red;
    }

    搭配 parameter 和 default paramter value

    @mixin my-mixin($extra- 100px) {
      h1 {
        font-size: 20px + $extra-width;
      }
    } 

    @content 的用法

    h1 {
      @include font("display") {
        color: red;
      }
    }

    include 调用紧跟着一个对象.

    @mixin font($type) {
      @if $type == "display" {
        font-size: 10rem;
        @content;
      } @else {
        font-size: 1rem;
        @content;
      }
    }

    对象会被传入 @mixin 然后通过 @content paste 出来使用. 很适合用来做 extend 和 conditional (比如 media query)

    甚至可以传参数沟通哦.

    @function

    mixin include 只是简单的 copy paste style code, function 则更像 JS, 它一般用来做算法逻辑等等.

    @function my-function() {
      @return 50px;
    }

    和 mixin 不同, 不管有没有 parameter, function 都要有括弧. 也必须有 return.

    使用的时候不需要加 @include 之类的

    h1 {
      font-size: my-function() + 20px;
    }

    function 一般上会比较复杂, 会搭配 @if, @for, @each 这类 JS 语法来使用. 我目前没有用到这么复杂, 以后才讲.

    @extend

    继承, 比如某个 selector 想拥有另一个 selector 的 style code 的时候, 就可以用到.

    .container {
      width: 500px;
      border: 2px solid red;
    }
    
    .child-container {
      @extend .container;
    }

    child-container 想拥有 .container 的 style. 可以用 @extend 表达

    出来的效果是这样:

    .container, .child-container {
      width: 500px;
      border: 2px solid red;
    }

    看不出什么厉害之处, 挺多只是表达好一些而已. 但是如果要扩展的话..

    .child-container {
      @extend .container;
      background-color: red;
    }

    结果

    .container, .child-container {
      width: 500px;
      border: 2px solid red;
    }
    
    .child-container {
      background-color: red;
    }

    这样结构上就加分了. 所以说 Sass 的目的就是让你的 CSS code 更加 manageable.

    %placeholder

    参考: Youtube – Sass Tutorials #19 - Placeholder Selectors

    上面介绍的是 extend existing class, 但如果只是想抽象的话呢? 可以用 placeholder

    %box {
      outline: 1px solid black;
    }
    .box1 {
      @extend %box;
    }
    .box2 {
      @extend %box;
    }

    %box 是抽象的, 如果没用被引用, 它完全不会出现在 CSS 里面

    效果

    .container .box2, .container .box1 {
      outline: 1px solid black;
    }

    You may not @extend selectors across media queries

    %dada {
      color: blue;
    }
    body {
      @media (min-width: 1px) {
        @extend %dada;
      }
    }

    上面这样是不 ok 的, extend 必须在 media 里面

    下面这样是 ok 的

    body {
      @media (min-width: 1px) {
        %dada {
          color: blue;
        }
        @extend %dada;
      }
    }

    可以用 mixin 来替代, 参考: stackoverflow – Extending selectors from within media queries with Sass

    Color Function

    Sass 有自带一些 function.

    color.complement 补色 (色轮 180°)

    color.scale

    控制色调和饱和度, 还有透明度

    background-color: color.scale(red, $lightness: 0%); /* 起点 */
    background-color: color.scale(red, $lightness: -100%); /* 黑色 */
    background-color: color.scale(red, $lightness: 100%); /* 白色 */
    
    background-color: color.scale(red, $alpha: -40%); /* 相等于 rgba(red, 0.6) */
    
    background-color: color.scale(red, $saturation: 10%, $lightness: -10%); /* 调饱和度, 同时也可以调色调或透明 */

    依据背景色调整 text color

    @function text-color($color) {
      @return if(color.lightness($color) > 40%, black, white);
    }
    
    @mixin bg-and-text-color($color) {
      background-color: $color;
      color: text-color($color);
    }
    
    .container {
      @include bg-and-text-color(black);
    }

    通过 color.lightness 判断是亮色还是暗色

    Get hue, saturation, lightness

    $color: hsl(206, 94%, 28%);
    $hue: color.hue($color); // 206
    $saturation: color.saturation($color); // 94%
    $lightness: color.lightness($color); // 28%

    获取到色相, 饱和度, 明度

    JS 语法

    JS 有 object, array, for loop, if else, 等. Sass 也有, 只是比较少会用到, 除非你的项目真的很复杂, 或者你没有使用 Tailwind, Bootstrap 这种自带架构的框架或库.

    这里介绍一些比较常会用到的

    object & array

    @use "sass:list";
    @use "sass:map";
    
    $obj: (
      color: red,
      size: 100px,
    );
    $obj-keys: map-keys($obj); // color, size
    $obj-values: map-values($obj); //  red, 100px
    
    $arr: red 100px;
    
    h1 {
      color: map-get($obj, "color"); /* get property color */
      color: map-get($obj, color); /* 不一定要 quote, 但 best practice 是有放的 */
      background-color: list.nth($arr, 1); /* get first, not start with zero oh */
    }

    由于它的调用很丑, map-get($obj, color); 倒不如用 variable, $obj-color 更好一些.

    所以通常会用 function 包装使用. 参考: Using SCSS maps as object - is it good?

    $color: (
      "red": red,
      "yellow": yellow,
    );
    
    @function color($key) {
      @if not map-has-key($color, $key) {
        @warn "Color `#{$key}` not found.";
      }
      @return map-get($color, $key);
    }

    调用

    body {
      background-color: color("red");
    }

    这样就比较好一些了.

    注意: key 最好 quote 起来

    $color-collection: (
      white: hsl(0, 0%, 100%),
    );
    @function color($key) {
      @return map-get($color-collection, $key);
    }
    .container {
      color: color("white");
    }

    key = white 的时候, 上面这样 color 是出不来的.

    一定要

    $color-collection: (
      'white': hsl(0, 0%, 100%),
    );

    if else & ternay operator

    @function my-function($value) {
      @if $value < 100 {
        @return 50px;
      } @else {
        @return 10px;
      }
    }
    
    h1 {
      width: my-function(30);
      height: if(30 < 100, 50px, 10px); // ternary operator
    }

    for loop

    from...to

    @for $i from 0 to 3

    result: [0, 1, 2], 从 0 开始到 2 结束. 3 不包含在内

    from...through

    @for $i from 0 through 3

    result: [0, 1, 2, 3], 从 0 开始到 3 结束. 3 也包含在内

    常见用法

    $length: 3;
    @for $i from 0 to $length {
      h1:nth-child(#{$i + 1}) {
        font-size: $i;
      }
    }

    注:nth-child 括弧内需要 #{} 来引用 $i, font-size 值则不需要.

    @each

    $breakpoint: (
      sm: 640px,
      md: 768px,
      lg: 1024px,
      xl: 1280px,
      2xl: 1536px,
    );
    @each $bp in $breakpoint {
      .container-#{list.nth($bp, 1)} {
        max-width: list.nth($bp, 2);
      }
    }

    和 for loop 差不多, 也可以用来 each 出 object 的 key 哦, 它返回的是 array [key, value] 形式

    array length

    $breakpoints: "sm" "md" "lg";
    $breakpoint-count: length($breakpoints);

    array index of

    @use "sass:list";
    
    $breakpoints: "sm" "md" "lg";
    $breakpoint-count: list.index($breakpoints, "sm");
    .target {
      width: $breakpoint-count; /* 1 */
    }

    注: 如果找不到返回值不是 -1 哦, 而是直接没有 width.

  • 相关阅读:
    【转】《基于MFC的OpenGL编程》Part 5 Transformations Rotations, Translations and Scaling
    【转】 《基于MFC的OpenGL编程》Part 10 Texture Mapping
    【转】 《基于MFC的OpenGL编程》Part 11 Blending, Antialiasing and Fog
    win form 托盘功能的实现(引用CSDN)
    C# win form退出窗体时对话框实用
    智能DNS 笔记
    iis无法启动, 找出占用80端口的罪魁祸首
    gvim for windows的剪贴板操作
    内容交换
    Content Networking 读书笔记
  • 原文地址:https://www.cnblogs.com/keatkeat/p/15864625.html
Copyright © 2020-2023  润新知