媒介查询

媒介查询

声明式的设置响应式布局

声明式的概念在前端很是流行,譬如著名的 React 就是典型的声明式组件库。声明式编程应用到 CSS 中即是 CSS 应当定义 What it wants,而不是 How it should。我们上面讨论过的一个关于分割点的容易混淆之处就是分割点同时代表了某个范围。譬如$large:600px这种定义在large这个词本身代表一个范围值的时候就会混淆。因此在具体的某个组件或者标签中,我们应该对其隐藏具体的尺寸设置,譬如:

@mixin for-phone-only {
  @media (max-width: 599px) {
    @content;
  }
}
@mixin for-tablet-portrait-up {
  @media (min-width: 600px) {
    @content;
  }
}
@mixin for-tablet-portait-only {
  @media (min-width: 600px) and (max-width: 899px) {
    @content;
  }
}
@mixin for-tablet-landscape-up {
  @media (min-width: 900px) {
    @content;
  }
}
@mixin for-tablet-landscape-only {
  @media (min-width: 900px) and (max-width: 1199px) {
    @content;
  }
}
@mixin for-desktop-up {
  @media (min-width: 1200px) {
    @content;
  }
}
@mixin for-desktop-only {
  @media (min-width: 1200px) and (max-width: 1799px) {
    @content;
  }
}
@mixin for-big-desktop-up {
  @media (min-width: 1800px) {
    @content;
  }
}

// usage
.my-box {
  padding: 10px;

  @include for-desktop-up {
    padding: 20px;
  }
}

你会发现在上面的定义中我很推荐使用-up-only后缀,在具体使用样式的地方:

.phone-only {
  @include for-phone-only {
    background: purple;
  }
}

.tablet-portait-only {
  @include for-tablet-portait-only {
    background: purple;
  }
}

.tablet-portrait-up {
  @include for-tablet-portrait-up {
    background: purple;
  }
}

.tablet-landscape-only {
  @include for-tablet-landscape-only {
    background: purple;
  }
}

.tablet-landscape-up {
  @include for-tablet-landscape-up {
    background: purple;
  }
}

.desktop-only {
  @include for-desktop-only {
    background: purple;
  }
}

.desktop-up {
  @include for-desktop-up {
    background: purple;
  }
}

.big-desktop-up {
  @include for-big-desktop-up {
    background: purple;
  }
}

不过这种方式在需要大量自定义媒介查询搭配的时候就显得不是那么灵活,我们可以提供更加细粒度的控制方式:

@mixin for-size($size) {
  @if $size == phone-only {
    @media (max-width: 599px) {
      @content;
    }
  } @else if $size == tablet-portrait-up {
    @media (min-width: 600px) {
      @content;
    }
  } @else if $size == tablet-portait-only {
    @media (min-width: 600px) and (max-width: 899px) {
      @content;
    }
  } @else if $size == tablet-landscape-up {
    @media (min-width: 900px) {
      @content;
    }
  } @else if $size == tablet-landscape-only {
    @media (min-width: 900px) and (max-width: 1199px) {
      @content;
    }
  } @else if $size == desktop-up {
    @media (min-width: 1200px) {
      @content;
    }
  } @else if $size == desktop-only {
    @media (min-width: 1200px) and (max-width: 1799px) {
      @content;
    }
  } @else if $size == big-desktop-up {
    @media (min-width: 1800px) {
      @content;
    }
  }
}

// usage
.my-box {
  padding: 10px;

  @include for-size(desktop-up) {
    padding: 20px;
  }
}

这种方式自然能够达成预期的效果,不过如果某个粗心的开发者传入了某个未预定义的范围名,那么久尴尬了,因此我们还是建议不要传入某个具体的尺寸,而是传入某个范围:

@mixin for-size($range) {
  $phone-upper-boundary: 600px;
  $tablet-portrait-upper-boundary: 900px;
  $tablet-landscape-upper-boundary: 1200px;
  $desktop-upper-boundary: 1800px;

  @if $range == phone-only {
    @media (max-width: #{$phone-upper-boundary - 1}) {
      @content;
    }
  } @else if $range == tablet-portrait-up {
    @media (min-width: $phone-upper-boundary) {
      @content;
    }
  } @else if $range == tablet-portait-only {
    @media (min-width: $phone-upper-boundary) and (max-width: #{$tablet-portrait-upper-boundary - 1}) {
      @content;
    }
  } @else if $range == tablet-landscape-up {
    @media (min-width: $tablet-landscape-upper-boundary) {
      @content;
    }
  } @else if $range == tablet-landscape-only {
    @media (min-width: $tablet-portrait-upper-boundary) and (max-width: $tablet-landscape-upper-boundary - 1px) {
      @content;
    }
  } @else if $range == desktop-up {
    @media (min-width: $tablet-landscape-upper-boundary) {
      @content;
    }
  } @else if $range == desktop-only {
    @media (min-width: $tablet-landscape-upper-boundary) and (max-width: $desktop-upper-boundary - 1px) {
      @content;
    }
  } @else if $range == big-desktop-up {
    @media (min-width: $desktop-upper-boundary) {
      @content;
    }
  }
}

// usage
.my-box {
  padding: 10px;

  @include for-size(desktop-up) {
    padding: 20px;
  }
}

Responsive Menu

老实说,笔者一直觉得响应式开发最成功的应用就是菜单了,这里介绍简单的响应式菜单的开发方式,借鉴了W3School的教程,首先,HTML 的元素布局为:

<ul class="topnav">
  <li><a href="#home">Home</a></li>
  <li><a href="#news">News</a></li>
  <li><a href="#contact">Contact</a></li>
  <li><a href="#about">About</a></li>
  <li class="icon">
    <a href="javascript:void(0);" onclick="myFunction()">&#9776;</a>
  </li>
</ul>

然后我们可以添加基本的样式:

/* Remove margins and padding from the list, and add a black background color */
ul.topnav  {
     list-style-type: none;
     margin: 0;
     padding: 0;
     overflow: hidden;
     background-color: #333;
}

/* Float the list items side by side */
ul.topnav li  {
  float: left;
}

/* Style the links inside the list items */
ul.topnav li a  {
     display: inline-block;
     color: #f2f2f2;
     text-align: center;
     padding: 14px 16px;
     text-decoration: none;
     transition: 0.3s;
     font-size: 17px;
}

/* Change background color of links on hover */
ul.topnav li a:hover  {
  background-color: #111;
}

/* Hide the list item that contains the link that should open and close the topnav on small screens */
ul.topnav li.icon  {
  display: none;
}

然后通过 Media Query 进行不同的尺寸下的样式设置:

/* When the screen is less than 680 pixels wide, hide all list items, except for the first one ("Home"). Show the list item that contains the link to open and close the topnav (li.icon) */
@media screen and (max-width:680px) {
  ul.topnav li:not(:first-child) {display: none;}
  ul.topnav li.icon {
    float: right;
    display: inline-block;
  }
}

/* The "responsive" class is added to the topnav with JavaScript when the user clicks on the icon. This class makes the topnav look good on small screens */
@media screen and (max-width:680px) {
  ul.topnav.responsive {position: relative;}
  ul.topnav.responsive li.icon {
    position: absolute;
    right: 0;
    top: 0;
  }
  ul.topnav.responsive li {
    float: none;
    display: inline;
  }
  ul.topnav.responsive li a {
    display: block;
    text-align: left;
  }
}

对于实际的效果的例子在这里

下一页