2019-08-13 我写CSS的常用套路(附demo的效果实现与源码)

前言

本文是笔者写CSS时常用的套路。不论效果再怎么华丽,万变不离其宗。

1、交错动画

image

有时候,我们需要给多个元素添加同一个动画,播放后,不难发现它们会一起运动,一起结束,这样就会显得很平淡无奇。那么如何将动画变得稍微有趣一点呢?很简单,既然它们都是同一时刻开始运动的,那么让它们不在同一时刻运动不就可以了吗。如何让它们不在同一时刻运动呢?注意到CSS动画有延迟(delay)这一属性。举个栗子,比如有十个元素播放十个动画,将第二个元素的动画播放时间设定为比第一个元素晚0.5秒(也就是将延迟设为0.5秒),其他元素以此类推,这样它们就会错开来,形成一种独特的视觉效果。

image

这就是所谓的交错动画:通过设置不同的延迟时间,达到动画交错播放的效果。本demo地址:https://codepen.io/alphardex/pen/XWWWBmQ

<div class="loading">
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
  <div class="dot"></div>
</div>
body {
  display: flex;
  height: 100vh;
  justify-content: center;
  align-items: center;
  background: #222;
}

.loading {
  $colors: #7ef9ff, #89cff0, #4682b4, #0f52ba, `#000080;
  display: flex;
  animation-delay: 1s;

  .dot {
    position: relative;
    width: 2em;
    height: 2em;
    margin: 0.8em;
    border-radius: 50%;

    &::before {
      position: absolute;
      content: "";
      width: 100%;
      height: 100%;
      background: inherit;
      border-radius: inherit;
      animation: wave 2s ease-out infinite;
    }

    @for $i from 1 through 5 {
      &:nth-child(#{$i}) {
        background: nth($colors, $i);

        &::before {
          animation-delay: $i * 0.2s;
        }
      }
    }
  }
}

@keyframes wave {
  50%,
  75% {
    transform: scale(2.5);
  }

  80%,
  100% {
    opacity: 0;
  }
}

2、用JS分割文本

还有一种经常用到的玩法:用JS将句子或单词分割成字母,并给每个字母加上不同延时的动画,同样也很华丽。
image
<p class="landIn">Ano hi watashitachi mada shiranai no Fushigi no monogatari desu.</p>
@import url("https://fonts.googleapis.com/css?family=Lora:400,400i,700");

body {
  display: flex;
  flex-direction: column;
  height: 100vh;
  justify-content: center;
  align-items: center;
  background-image: linear-gradient(rgba(16, 16, 16, 0.8),
      rgba(16, 16, 16, 0.8)),
    url(https://i.loli.net/2019/10/18/buDT4YS6zUMfHst.jpg);
  background-size: cover;
}

p {
  margin: 0 9em;
  font-size: 2em;
  font-weight: 600;
}

.landIn {
  display: flex;
  flex-wrap: wrap;
  line-height: 1.8;
  color: white;
  font-family: Lora, serif;
  white-space: pre;

  span {
    animation: landIn 0.8s ease-out both;
  }
}

@keyframes landIn {
  from {
    opacity: 0;
    transform: translateY(-20%);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}
let landInTexts = document.querySelectorAll(".landIn");
landInTexts.forEach(landInText => {
  let letters = landInText.textContent.split("");
  landInText.textContent = "";
  letters.forEach((letter, i) => {
    let span = document.createElement("span");
    span.textContent = letter;
    span.style.animationDelay = `${i * 0.05}s`;
    landInText.append(span);
  });
});

本demo地址:https://codepen.io/alphardex/full/KKwvKGY一般我们都是从第一个元素开始交错的。但如果要从中间元素开始交错的话,就要给当前元素的延时各加上一个值,这个值就是中间元素的下标到当前元素的下标的距离(也就是下标之差的绝对值)与步长的乘积,即:delay + Math.abs(i - middle) * step,其中中间元素的下标middle = letters.filter(e => e !== "").length / 2

image

本demo地址:https://codepen.io/alphardex/full/eYYMYXJ所有有交错特性的动画都在这儿

<div class="reveal">sword art online</div>
@import url("https://fonts.googleapis.com/css?family=Raleway:400,400i,700");

body {
  display: flex;
  height: 100vh;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: #222;
}

.reveal {
  position: relative;
  display: flex;
  color: #6ee1f5;
  font-size: 2em;
  font-family: Raleway, sans-serif;
  letter-spacing: 3px;
  text-transform: uppercase;
  white-space: pre;

  span {
    opacity: 0;
    transform: scale(0);
    animation: fadeIn 2.4s forwards;
  }

  &::before,
  &::after {
    position: absolute;
    content: "";
    top: 0;
    bottom: 0;
    width: 2px;
    height: 100%;
    background: white;
    opacity: 0;
    transform: scale(0);
  }

  &::before {
    left: 50%;
    animation: slideLeft 1.5s cubic-bezier(0.7, -0.6, 0.3, 1.5) forwards;
  }

  &::after {
    right: 50%;
    animation: slideRight 1.5s cubic-bezier(0.7, -0.6, 0.3, 1.5) forwards;
  }
}

@keyframes fadeIn {
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes slideLeft {
  to {
    left: -6%;
    opacity: 1;
    transform: scale(0.9);
  }
}

@keyframes slideRight {
  to {
    right: -6%;
    opacity: 1;
    transform: scale(0.9);
  }
}
let duration = 0.8;
let delay = 0.3;
let revealText = document.querySelector(".reveal");
let letters = revealText.textContent.split("");
revealText.textContent = "";
let middle = letters.filter(e => e !== " ").length / 2;
letters.forEach((letter, i) => {
  let span = document.createElement("span");
  span.textContent = letter;
  span.style.animationDelay = `${delay + Math.abs(i - middle) * 0.1}s`;
  revealText.append(span);
});

3、随机粒子动画

说到随机性,我们可以实现一种更疯狂的效果:给几百个粒子添加交错动画,并且交错时间随机,位置大小也都是随机。如此一来我们就能用纯CSS模拟出下雪的效果。又到了白色相簿的季节呢~为什么你写CSS这么熟练啊?
image

本demo地址:https://codepen.io/alphardex/full/dyPorwJ

<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
<div class="snow"></div>
body {
  height: 100vh;
  background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%);
  overflow: hidden;
  filter: drop-shadow(0 0 10px white);
}

@function random_range($min, $max) {
  $rand: random();
  $random_range: $min + floor($rand * (($max - $min) + 1));
  @return $random_range;
}

.snow {
  $total: 200;
  position: absolute;
  width: 10px;
  height: 10px;
  background: white;
  border-radius: 50%;

  @for $i from 1 through $total {
    $random-x: random(1000000) * 0.0001vw;
    $random-offset: random_range(-100000, 100000) * 0.0001vw;
    $random-x-end: $random-x + $random-offset;
    $random-x-end-yoyo: $random-x + ($random-offset / 2);
    $random-yoyo-time: random_range(30000, 80000) / 100000;
    $random-yoyo-y: $random-yoyo-time * 100vh;
    $random-scale: random(10000) * 0.0001;
    $fall-duration: random_range(10, 30) * 1s;
    $fall-delay: random(30) * -1s;

    &:nth-child(#{$i}) {
      opacity: random(10000) * 0.0001;
      transform: translate($random-x, -10px) scale($random-scale);
      animation: fall-#{$i} $fall-duration $fall-delay linear infinite;
    }

    @keyframes fall-#{$i} {
      #{percentage($random-yoyo-time)} {
        transform: translate($random-x-end, $random-yoyo-y) scale($random-scale);
      }
      
      to {
        transform: translate($random-x-end-yoyo, 100vh) scale($random-scale);
      }
    }
  }
}

伪类和伪元素

4、伪类

image

HTML元素的状态是可以动态变化的。举个栗子,当你的鼠标悬浮到一个按钮上时,按钮就会变成“悬浮”状态,这时我们就可以利用伪类:hover来选中这一状态的按钮,并对其样式进行改变。:hover是笔者最最常用的一个伪类。还有一个很常用的伪类是:nth-child,用于选中元素的某一个子元素。其他的类似:focus:focus-within等也有一定的使用。本demo地址:https://codepen.io/alphardex/pen/pooYKVa

<button
  data-text="Start"
  class="btn btn-primary btn-ghost btn-border-stroke  btn-text-float-up"
>
  <div class="btn-borders">
    <div class="border-top"></div>
    <div class="border-right"></div>
    <div class="border-bottom"></div>
    <div class="border-left"></div>
  </div>
  <span class="btn-text">Start</span>
</button>
@import url(https://fonts.googleapis.com/css?family=Lato);

body {
  display: flex;
  height: 100vh;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: #222;
}

.btn {
  --hue: 190;
  --ease-in-duration: 0.25s;
  --ease-out-duration: 0.65s;
  --ease-out-delay: var(--ease-in-duration);
  position: relative;
  padding: 1rem 3rem;
  font-size: 1rem;
  line-height: 1.5;
  color: white;
  text-decoration: none;
  background-color: hsl(var(--hue), 100%, 41%);
  border: 1px solid hsl(var(--hue), 100%, 41%);
  outline: transparent;
  overflow: hidden;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: 0.25s;

  &:hover {
    background: hsl(var(--hue), 100%, 31%);
  }

  &-primary {
    --hue: 171;
  }

  &-ghost {
    color: hsl(var(--hue), 100%, 41%);
    background-color: transparent;
    border-color: hsl(var(--hue), 100%, 41%);
    
    &:hover {
      color: white;
    }
  }

  &-border-stroke {
    border-color: hsla(var(--hue), 100%, 41%, 0.35);

    .btn-borders {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;

      .border-top {
        position: absolute;
        top: 0;
        width: 100%;
        height: 1px;
        background: hsl(var(--hue), 100%, 41%);
        transform: scaleX(0);
        transform-origin: left;
      }

      .border-right {
        position: absolute;
        right: 0;
        width: 1px;
        height: 100%;
        background: hsl(var(--hue), 100%, 41%);
        transform: scaleY(0);
        transform-origin: bottom;
      }

      .border-bottom {
        position: absolute;
        bottom: 0;
        width: 100%;
        height: 1px;
        background: hsl(var(--hue), 100%, 41%);
        transform: scaleX(0);
        transform-origin: left;
      }

      .border-left {
        position: absolute;
        left: 0;
        width: 1px;
        height: 100%;
        background: hsl(var(--hue), 100%, 41%);
        transform: scaleY(0);
        transform-origin: bottom;
      }

      // when unhover, ease-in top, right; ease-out bottom, left

      .border-left {
        transition: var(--ease-out-duration) var(--ease-out-delay)
          cubic-bezier(0.2, 1, 0.2, 1);
      }
      .border-bottom {
        transition: var(--ease-out-duration) var(--ease-out-delay)
          cubic-bezier(0.2, 1, 0.2, 1);
      }

      .border-right {
        transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
      }
      .border-top {
        transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
      }
    }

    &:hover {
      color: hsl(var(--hue), 100%, 41%);
      background: transparent;

      .border-top,
      .border-bottom {
        transform: scaleX(1);
      }
      .border-left,
      .border-right {
        transform: scaleY(1);
      }

      // when hover, ease-in bottom, left; ease-out top, right

      .border-left {
        transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
      }
      .border-bottom {
        transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
      }

      .border-right {
        transition: var(--ease-out-duration) var(--ease-out-delay)
          cubic-bezier(0.2, 1, 0.2, 1);
      }
      .border-top {
        transition: var(--ease-out-duration) var(--ease-out-delay)
          cubic-bezier(0.2, 1, 0.2, 1);
      }
    }
  }

  &-text-float-up {
    &::after {
      position: absolute;
      content: attr(data-text);
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      opacity: 0;
      transform: translateY(35%);
      transition: 0.25s ease-out;
    }

    // when hover, ease-in top-text; ease-out bottom-text

    .btn-text {
      display: block;
      transition: 0.75s 0.1s cubic-bezier(0.2, 1, 0.2, 1);
    }

    &:hover {
      // when hover, ease-in bottom-text; ease-out top-text

      .btn-text {
        opacity: 0;
        transform: translateY(-25%);
        transition: 0.25s ease-out;
      }

      &::after {
        opacity: 1;
        transform: translateY(0);
        transition: 0.75s 0.1s cubic-bezier(0.2, 1, 0.2, 1);
      }
    }
  }
}

5、绝对定位实现多重边框

谁规定按钮只能有一套边框的?利用绝对定位和padding,我们可以给按钮做出3套大小不一的边框来,这样效果更炫了。

image

本demo地址:https://codepen.io/alphardex/full/ZEYXomW

<button class="btn btn-primary btn-ghost btn-multiple-border-stroke">
  <div class="btn-borders-group">
    <div class="border-top"></div>
    <div class="border-right"></div>
    <div class="border-bottom"></div>
    <div class="border-left"></div>
  </div>
  <div class="btn-borders-group">
    <div class="border-top"></div>
    <div class="border-right"></div>
    <div class="border-bottom"></div>
    <div class="border-left"></div>
  </div>
  <div class="btn-borders-group">
    <div class="border-top"></div>
    <div class="border-right"></div>
    <div class="border-bottom"></div>
    <div class="border-left"></div>
  </div>
  <span class="btn-text">Start</span>
</button>
@import url(https://fonts.googleapis.com/css?family=Lato);

body {
  display: flex;
  height: 100vh;
  justify-content: center;
  align-items: center;
  text-align: center;
  background: #222;
}

.btn {
  --hue: 190;
  --ease-in-duration: 0.25s;
  --ease-out-duration: 0.65s;
  --ease-out-delay: var(--ease-in-duration);
  position: relative;
  padding: 1rem 3rem;
  font-size: 1rem;
  line-height: 1.5;
  color: white;
  text-decoration: none;
  background-color: hsl(var(--hue), 100%, 41%);
  border: 1px solid hsl(var(--hue), 100%, 41%);
  outline: transparent;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: 0.25s;

  &:hover {
    background: hsl(var(--hue), 100%, 31%);
  }

  &-primary {
    --hue: 171;
  }

  &-ghost {
    color: hsl(var(--hue), 100%, 41%);
    background-color: transparent;
    border-color: hsl(var(--hue), 100%, 41%);

    &:hover {
      color: white;
    }
  }

  &-multiple-border-stroke {
    border-color: transparent;

    .btn-borders-group {
      position: absolute;
      top: 0;
      left: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      width: 100%;
      height: 100%;
      border: 1px solid hsla(var(--hue), 100%, 41%, 0.35);

      &:nth-child(1) {
        left: -8px;
        padding: 0 8px;
      }

      &:nth-child(2) {
        top: -8px;
        padding: 8px 0;
      }

      &:nth-child(3) {
        top: -4px;
        left: -4px;
        padding: 4px;
      }

      .border-top {
        position: absolute;
        top: 0;
        width: 100%;
        height: 1px;
        background: hsl(var(--hue), 100%, 41%);
        transform: scaleX(0);
        transform-origin: left;
      }

      .border-right {
        position: absolute;
        right: 0;
        width: 1px;
        height: 100%;
        background: hsl(var(--hue), 100%, 41%);
        transform: scaleY(0);
        transform-origin: bottom;
      }

      .border-bottom {
        position: absolute;
        bottom: 0;
        width: 100%;
        height: 1px;
        background: hsl(var(--hue), 100%, 41%);
        transform: scaleX(0);
        transform-origin: left;
      }

      .border-left {
        position: absolute;
        left: 0;
        width: 1px;
        height: 100%;
        background: hsl(var(--hue), 100%, 41%);
        transform: scaleY(0);
        transform-origin: bottom;
      }

      // when unhover, ease-in top, right; ease-out bottom, left

      .border-left {
        transition: var(--ease-out-duration) var(--ease-out-delay) cubic-bezier(0.2, 1, 0.2, 1);
      }

      .border-bottom {
        transition: var(--ease-out-duration) var(--ease-out-delay) cubic-bezier(0.2, 1, 0.2, 1);
      }

      .border-right {
        transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
      }

      .border-top {
        transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
      }
    }

    &:hover {
      color: hsl(var(--hue), 100%, 41%);
      background: transparent;

      .border-top,
      .border-bottom {
        transform: scaleX(1);
      }

      .border-left,
      .border-right {
        transform: scaleY(1);
      }

      // when hover, ease-in bottom, left; ease-out top, right

      .border-left {
        transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
      }

      .border-bottom {
        transition: var(--ease-in-duration) cubic-bezier(1, 0, 0.8, 0);
      }

      .border-right {
        transition: var(--ease-out-duration) var(--ease-out-delay) cubic-bezier(0.2, 1, 0.2, 1);
      }

      .border-top {
        transition: var(--ease-out-duration) var(--ease-out-delay) cubic-bezier(0.2, 1, 0.2, 1);
      }
    }
  }
}

6、伪元素

image

简而言之,伪元素就是在原先的元素基础上插入额外的元素,而且这个元素不充当HTML的标签,这样就能保持HTML结构的整洁。我们知道每个元素都有::before::after这两个伪元素,也就是说每个元素都提供了3个矩形(元素本身1个,伪元素2个)来供我们进行形状的绘制。现在又有了clip-path这个属性,几乎任意的形状都可以被绘制出来,全凭你的想象力上面的动图是条子划过文本的动画,条子就是每个文本所对应的伪元素,对每个文本和其伪元素应用动画,就能达到上图的效果了本demo地址:https://codepen.io/alphardex/pen/jOEOEzZ

<header>
  <h1 class="title">I'm alphardex.</h1>
  <p class="subtitle">A CSS Wizard</p>
</header>
// palette: https://www.materialpalette.com/light-blue/pink

@import url("https://fonts.googleapis.com/css?family=Lato");
@import url("https://fonts.googleapis.com/css?family=Lora:400,400i,700");

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: #222;
}

header {
  .title,
  .subtitle {
    position: relative;
    width: 250px;
    height: 30px;
    color: transparent;
    animation: fadeIn 2s 1.6s forwards;

    &::before {
      position: absolute;
      content: "";
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      transform: scaleX(0);
      transform-origin: left;
      animation: slideRight 2s cubic-bezier(0.75, 0, 0, 1) forwards;
    }
  }

  .title {
    margin: 0;
    font-family: Lora, serif;
    font-size: 32px;
    line-height: 30px;

    &::before {
      background: #FF4081;
    }
  }

  .subtitle {
    margin: 10px 0 0 0;
    font-family: Lato, sans-serif;
    font-size: 12px;
    line-height: 30px;
    letter-spacing: 5px;
    text-transform: uppercase;
    animation-delay: 3.2s;

    &::before {
      background: #03A9F4;
      animation-delay: 2s;
    }
  }
}

@keyframes fadeIn {
  to {
    color: white;
  }
}

@keyframes slideRight {
  50% {
    transform: scaleX(1);
    transform-origin: left;
  }

  50.1% {
    transform-origin: right;
  }

  to {
    transform-origin: right;
  }
}

7、attr()生成文本内容

元素可以有自定义的属性值,它的命名格式通常为data-*``attr()用于获取元素的这种自定义属性值,并赋值给其伪元素的content作为其生成的内容利用这个函数,我们可以用伪元素在原先文本的基础上“复制”出另一个文本,如下图所示。

image
看上去有点乱糟糟的对吧?没事,给它加上overflow: hidden,把多余的文本遮住。通过JS分割文本并应用交错动画,就得到了如下的效果,这也是接下来本文要讲的overflow障眼法。
image

本demo地址:https://codepen.io/alphardex/full/wvBeXjd

<ul class="float-text-menu">
  <li><a href="#" data-text="Home">Home</a></li>
  <li><a href="#" data-text="Archives">Archives</a></li>
  <li><a href="#" data-text="Tags">Tags</a></li>
  <li><a href="#" data-text="Categories">Categories</a></li>
  <li><a href="#" data-text="About">About</a></li>
</ul>
@import url(https://fonts.googleapis.com/css?family=Lato);

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: black;
}

.float-text-menu {
  display: flex;
  flex-direction: column;
  list-style-type: none;

  li {

    a {
      display: flex;
      padding: 6px;
      color: white;
      font-family: Lato, sans-serif;
      text-decoration: none;
      overflow: hidden;

      span {
        position: relative;
        transition: 0.3s;

        &::before {
          position: absolute;
          content: attr(data-text);
          transform: translateY(130%);
        }
      }

      &:hover {
        span {
          transform: translateY(-130%);
        }
      }
    }
  }
}
let floatTextMenuLinks = document.querySelectorAll(".float-text-menu li a");
floatTextMenuLinks.forEach(link => {
  let letters = link.textContent.split("");
  link.textContent = "";
  letters.forEach((letter, i) => {
    let span = document.createElement("span");
    span.textContent = letter;
    span.style.transitionDelay = `${i / 20}s`;
    span.dataset.text = letter;
    link.append(span);
  });
});

8、overflow障眼法

之前有做过闪光按钮的效果:鼠标悬浮按钮上时一道光从左到右划过去。笔者就用渐变来模拟那道光,通过transform: translateX()将其平移至右边。

image

但这样明显不对啊,这光为啥能被看见呢?不应该把它给“挡”起来吗?于是乎,给按钮加上overflow: hidden,光在按钮外的位置时就被隐藏起来了。

image

这就是障眼法的力量:)本demo地址:https://codepen.io/alphardex/pen/eYYzXBZ更多障眼法可以看看这个作品,一次性看个够XD

<button class="btn btn-primary btn-ghost btn-shine">
  hover me
</button>
@import url(https://fonts.googleapis.com/css?family=Lato);

body {
  display: flex;
  height: 100vh;
  justify-content: center;
  align-items: center;
  background: #222;
}

.btn {
  --hue: 190;
  position: relative;
  padding: 1rem 3rem;
  font-size: 1rem;
  line-height: 1.5;
  color: white;
  text-decoration: none;
  text-transform: uppercase;
  background-color: hsl(var(--hue), 100%, 41%);
  border: 1px solid hsl(var(--hue), 100%, 41%);
  outline: transparent;
  overflow: hidden;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: 0.25s;

  &:hover {
    background: hsl(var(--hue), 100%, 31%);
  }

  &-primary {
    --hue: 187;
  }

  &-ghost {
    color: hsl(var(--hue), 100%, 41%);
    background-color: transparent;
    border-color: hsl(var(--hue), 100%, 41%);

    &:hover {
      color: white;
    }
  }

  &-shine {
    color: white;

    &::before {
      position: absolute;
      content: "";
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: linear-gradient(
        120deg,
        transparent,
        hsla(var(--hue), 100%, 41%, 0.5),
        transparent
      );
      transform: translateX(-100%);
      transition: 0.6s;
    }

    &:hover {
      background: transparent;
      box-shadow: 0 0 20px 10px hsla(var(--hue), 100%, 41%, 0.5);
    }

    &:hover::before {
      transform: translateX(100%);
    }
  }
}

9、兄弟选择符定制表单元素

提示:这里最好将input作为label的子元素,这样用户点击label时就能传到input上默认的input太丑怎么办?那就把它先抹掉,用appearance: noneopacity: 0都可以然后,利用兄弟选择符~来定制和input相邻的所有元素(+号也行,只不过只能选中最近的元素),例如可以用伪元素生成一个新的方框代替原先的input,利用伪类:checked和动画来表示它被勾选后的状态,本质上还是障眼法哦~

image
本demo地址:https://codepen.io/alphardex/full/rNNPQwa

CSS特性

善用某些CSS特性,也可以为你的作品增色不少哦

<form>
  <fieldset class="todo-list">
    <legend class="todo-list__title">My Special Todo List</legend>
    <label class="todo-list__label">
      <input type="checkbox" name="" id="" />
      <i class="check"></i>
      <span>Make awesome CSS animation</span>
    </label>
    <label class="todo-list__label">
      <input type="checkbox" name="" id="" />
      <i class="check"></i>
      <span>Watch awesome bangumi</span>
    </label>
    <label class="todo-list__label">
      <input type="checkbox" name="" id="" />
      <i class="check"></i>
      <span>Encounter awesome people</span>
    </label>
    <label class="todo-list__label">
      <input type="checkbox" name="" id="" />
      <i class="check"></i>
      <span>Be an awesome man</span>
    </label>
  </fieldset>
</form>
// color scheme: https://coolors.co/e63946-585b57-7b9fa1-264456-0b1420
@import url("https://fonts.googleapis.com/css?family=Lato:400,400i,700");

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: #0b1420;
}

.todo-list {
  display: flex;
  flex-direction: column;
  padding: 0 75px 10px 30px;
  background: #162740;
  border: transparent;

  .todo-list__title {
    padding: 3px 6px;
    color: #f1faee;
    background-color: #264456;
  }

  .todo-list__label {
    display: flex;
    align-items: center;
    margin: 40px 0;
    font-size: 24px;
    font-family: Lato, sans-serif;
    color: #f1faee;
    cursor: pointer;

    input[type="checkbox"] {
      opacity: 0;
      appearance: none;

      & + .check {
        position: absolute;
        width: 25px;
        height: 25px;
        border: 2px solid #f1faee;
        transition: 0.2s;
      }

      &:checked + .check {
        width: 25px;
        height: 15px;
        border-top: transparent;
        border-right: transparent;
        transform: rotate(-45deg);
      }

      & ~ span {
        position: relative;
        left: 40px;
        white-space: nowrap;
        transition: 0.5s;

        &::before {
          position: absolute;
          content: "";
          top: 50%;
          left: 0;
          width: 100%;
          height: 1px;
          background: #f1faee;
          transform: scaleX(0);
          transform-origin: right;
          transition: transform 0.5s;
        }
      }

      &:checked ~ span {
        color: #585b57;

        &::before {
          transform: scaleX(1);
          transform-origin: left;
        }
      }
    }
  }
}

10、animation

此处包括transitiontransformCSS动画可以说是利用CSS设计炫酷特效的最强法器,它几乎贯穿了我的所有作品有人问我为什么我能想出这么多的动画?笔者阅番百部,对常用的动画技巧了如指掌,同样那些酷炫的网站只要细心观察,也会给笔者带来很多设计上的灵感。一言以蔽之:只有多欣赏动画,才能写出好的动画。

11、border-radius

为盒子添加圆角,经常用来美化按钮等组件如果设定为50%则是圆形,也很常用

不规则的曲边形状

调整多个顶点的border-radius可以做出不规则的曲边形状

image.gif

本demo地址:https://codepen.io/alphardex/full/abbWOPR

<nav class="navtab">
  <ul>
    <li class="navtab-item active">
      <i class="fas fa-home"></i>
      <span>Home</span>
    </li>
    <li class="navtab-item">
      <i class="fas fa-compass"></i>
      <span>Explore</span>
    </li>
    <li class="navtab-item">
      <i class="fas fa-bell"></i>
      <span>Notification</span>
    </li>
    <li class="navtab-item">
      <i class="fas fa-user"></i>
      <span>Profile</span>
    </li>
  </ul>
</nav>
@import url(https://fonts.googleapis.com/css?family=Lato);

@mixin center {
  display: flex;
  justify-content: center;
  align-items: center;
}

body {
  @include center;
  height: 100vh;
  font-family: Lato, sans-serif;
  background: #03a9f4;
}

.navtab {
  --navtab-width: 600px;
  --navtab-item-width: calc(var(--navtab-width) / 4 - 20px);
  --navtab-overlay-width: calc(var(--navtab-item-width) + 80px);
  --active-index: 0;

  position: relative;
  width: var(--navtab-width);
  height: 150px;
  background: white;
  border: 1em solid white;
  // https://9elements.github.io/fancy-border-radius/full-control.html#15.5.15.15-50.95.50.85-150.600
  border-radius: 5% 5% 15% 15% / 15% 15% 50% 50%;
  overflow: hidden;

  ul {
    @include center;
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
    list-style-type: none;

    li.navtab-item {
      @include center;
      z-index: 2;
      flex-direction: column;
      width: var(--navtab-item-width);
      height: 100%;
      color: #0288d1;
      cursor: pointer;
      transition: 0.5s ease;

      i {
        font-size: 2em;
        transition: 0.5s ease;
      }

      span {
        font-size: 20px;
        user-select: none;
        opacity: 0;
        transition: 0.5s ease;
      }

      &.active {
        width: var(--navtab-overlay-width);

        i {
          transform: translateY(-10px);
        }

        span {
          opacity: 1;
        }
      }
    }
  }

  &::after {
    position: absolute;
    content: "";
    top: 0;
    left: 0;
    height: 100%;
    width: var(--navtab-overlay-width);
    background: #b3e5fc;
    border-radius: 20px;
    transform: translateX(calc(var(--navtab-item-width) * var(--active-index)));
    transition: 0.5s ease;
  }
}
let navtab = document.querySelector("nav.navtab");
let navtabItems = document.querySelectorAll("li.navtab-item");
navtabItems.forEach((navtabItem, activeIndex) =>
  navtabItem.addEventListener("click", () => {
    navtabItems.forEach(navtabItem => navtabItem.classList.remove("active"));
    navtabItem.classList.add("active");
    (navtab as HTMLElement).style.setProperty(
      "--active-index",
      `${activeIndex}`
    );
  })
);

12、box-shadow

为盒子添加阴影,增加盒子的立体感,可以多层叠加,并且会使阴影更加丝滑

image
本demo地址:https://codepen.io/alphardex/full/QWwwwpp

<ul class="pagination">
  <li class="page-prev">
    <a class="prev" href="#"><i class="fas fa-arrow-left"></i></a>
  </li>
  <li class="page-number active"><a href="#">1</a></li>
  <li class="page-number"><a href="#">2</a></li>
  <li class="page-number"><a href="#">3</a></li>
  <li class="page-number"><a href="#">4</a></li>
  <li class="page-number"><a href="#">5</a></li>
  <li class="page-number"><a href="#">6</a></li>
  <li class="page-next">
    <a class="next" href="#"><i class="fas fa-arrow-right"></i></a>
  </li>
</ul>
body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: lightblue;
}

.pagination {
  --active-index: 0;
  display: flex;
  padding: 10px 20px;
  background: white;
  border-radius: 50px;
  box-shadow:
  0 0.3px 0.6px rgba(0, 0, 0, 0.056),
  0 0.7px 1.3px rgba(0, 0, 0, 0.081),
  0 1.3px 2.5px rgba(0, 0, 0, 0.1),
  0 2.2px 4.5px rgba(0, 0, 0, 0.119),
  0 4.2px 8.4px rgba(0, 0, 0, 0.144),
  0 10px 20px rgba(0, 0, 0, 0.2)
;
  list-style-type: none;

  li {
    margin: 0 5px;

    &.page-number {
      width: 50px;
      height: 50px;
      line-height: 50px;
      text-align: center;

      &:hover a {
        color: white;
        background: #777;
      }

      &.active a {
        color: white;
        background: #333;
      }
    }

    &.page-prev,
    &.page-next {
      font-weight: 700;
    }

    &.page-prev {
      margin-right: 20px;
    }

    &.page-next {
      margin-left: 20px;
    }

    a {
      display: block;
      line-height: 50px;
      font-size: 20px;
      font-weight: 600;
      text-decoration: none;
      color: #777;
      border-radius: 50%;
      transition: 0.3s;

      &.prev:hover,
      &.next:hover {
        color: darken(#777, 50%);
      }
    }
  }
}
let prevLink = document.querySelector(".prev");
let nextLink = document.querySelector(".next");
let pagination = document.querySelector(".pagination");
let pageNumberLinks = document.querySelectorAll(".page-number a");
let maxPageIndex = pageNumberLinks.length - 1;
pageNumberLinks.forEach((pageNumberLink, activeIndex) => {
  pageNumberLink.addEventListener("click", () => {
    pageNumberLinks.forEach(pageNumberLink =>
      pageNumberLink.parentElement.classList.remove("active")
    );
    pageNumberLink.parentElement.classList.add("active");
    (pagination as HTMLElement).style.setProperty(
      "--active-index",
      `${activeIndex}`
    );
  });
});
prevLink.addEventListener("click", () => {
  pageNumberLinks.forEach(pageNumberLink =>
    pageNumberLink.parentElement.classList.remove("active")
  );
  let activeIndex = Number(
    (pagination as HTMLElement).style.getPropertyValue("--active-index")
  );
  activeIndex = activeIndex > 0 ? activeIndex - 1 : 0;
  pageNumberLinks[activeIndex].parentElement.classList.add("active");
  (pagination as HTMLElement).style.setProperty(
    "--active-index",
    `${activeIndex}`
  );
});
nextLink.addEventListener("click", () => {
  pageNumberLinks.forEach(pageNumberLink =>
    pageNumberLink.parentElement.classList.remove("active")
  );
  let activeIndex = Number(
    (pagination as HTMLElement).style.getPropertyValue("--active-index")
  );
  activeIndex = activeIndex < maxPageIndex ? activeIndex + 1 : maxPageIndex;
  pageNumberLinks[activeIndex].parentElement.classList.add("active");
  (pagination as HTMLElement).style.setProperty(
    "--active-index",
    `${activeIndex}`
  );
});

13、遮罩

如果给box-shadow的扩张半径设定足够大的值,可以用它来遮住背景,而无需额外的div元素

image
本demo地址:https://codepen.io/alphardex/full/BaaKvVZ

14、内发光

注意到box-shadow还有个inset,用于盒子内部发光利用这个特性我们可以在盒子内部的某个范围内设定颜色,做出一个新月形

image

再加点动画和滤镜效果,“猩红之月”闪亮登场!
image.gif

本demo地址:https://codepen.io/alphardex/full/eYmGEGp

text-shadow

文本阴影,本质上和box-shadow相同,只不过是相对于文本而言,常用于文本发光,也可通过多层叠加来制作霓虹文本和伪3D文本等效果

15、发光文本

image.gif

本demo地址:https://codepen.io/alphardex/full/Exxodoq

16、霓虹文本

image.gif

本demo地址:https://codepen.io/alphardex/full/rNNwmZz

17、伪3D文本

image.gif

本demo地址https://codepen.io/alphardex/full/QWWavvx

18、background-clip:text

能将背景裁剪成文字的前景色,常用来和color: transparent配合生成渐变文本

image.gif

本demo地址:https://codepen.io/alphardex/full/QWwveZG

gradient

渐变可以作为背景图片的一种,具有很强的色彩效果,甚至可以用来模拟光

19、linear-gradient

线性渐变是笔者最常用的渐变
image

这个作品用到了HTML的dialog标签,渐变背景,动画以及overflow障眼法,细心的你看出来了吗:)本demo地址:https://codepen.io/alphardex/full/eYYxzBm

20、radial-gradient

径向渐变常用于生成圆形背景,上面例子中Snow的背景就是一个椭圆形的径向渐变此外,由于背景可以叠加,我们可以叠加多个不同位置大小的径向渐变来生成圆点群,再加上动画就产生了一种微粒效果,无需多余的div元素。

image

本demo地址:https://codepen.io/alphardex/full/OJPvMGx

21、conic-gradient

圆锥渐变可以用于制作饼图
image

用一个伪元素叠在饼图上面,并将content设为某个值(这个值通过CSS变量计算出来),就能制作出度量计的效果,障眼法又一次完成了它的使命。

image

本demo地址:https://codepen.io/alphardex/full/BaydVvQ

filter

PS里的滤镜,玩过的都懂,blur最常用

22、backdrop-filter

对背景应用滤镜,产生毛玻璃的效果
image.gif

本demo地址:https://codepen.io/alphardex/full/pooQMVp

23、mix-blend-mode

PS里的混合模式,常用于文本在背景下的特殊效果以下利用滤色模式(screen)实现文本视频蒙版效果

image

本demo地址:https://codepen.io/alphardex/full/wvvLYpV

24、clip-path

PS里的裁切,可以制作各种不规则形状。如果和动画结合也会相当有意思。
image

本demo地址:https://codepen.io/alphardex/full/ZEEBRrq

25、-webkit-box-reflect

投影效果,不怎么常用,适合立体感强的作品。
image

本demo地址:https://codepen.io/alphardex/full/ExaZgxp

26、web animations

虽然这并不是一个CSS特性,但是它经常用于完成那些CSS所做不到的事情那么何时用它呢?当CSS动画中有属性无法从CSS中获取时,自然就会使用到它了

跟踪鼠标的位置

目前CSS还尚未有获取鼠标位置的API,因此考虑用JS来进行通过查阅相关的DOM API,发现在监听鼠标事件的API中,可通过e.clientXe.clientY来获得鼠标当前的位置既然能够获取鼠标的位置,那么跟踪鼠标的位置也就不是什么难事了:通过监听mouseentermouseleave事件,来获取鼠标出入一个元素时的位置,并用此坐标来当作鼠标的位移距离,监听mousemove事件,来获取鼠标在元素上移动时的位置,同样地用此坐标来当作鼠标的位移距离,这样一个跟踪鼠标的效果就实现了。

image

本demo地址:https://codepen.io/alphardex/full/OJPmQGz

CSS Houdini

CSS Houdini是CSS的底层API,它使我们能够通过这套接口来扩展CSS的功能

让渐变动起来

目前来说,我们无法直接给渐变添加动画,因为浏览器不理解要改变的值是什么类型这时,我们就可以利用CSS.registerProperty()来注册我们的自定义变量,并声明其语法类型(syntax)为颜色类型<color>,这样浏览器就能理解并对颜色应用插值方法来进行动画还记得上文提到的圆锥渐变conic-gradient()吗?既然它可以用来制作饼图,那么我们能不能让饼图动起来呢?答案是肯定的,定义三个变量:--color1--color2--pos,其中--pos的语法类型为长度百分比<length-percentage>,将其从0变为100%,饼图就会顺时针旋转出现。

image

利用绝对定位和层叠上下文,我们可以叠加多个从小到大的饼图,再给它们设置不同的颜色,应用交错动画,就有了下面这个炫丽的效果。
image

本demo地址:https://codepen.io/alphardex/full/RwNxpXQ

27、彩蛋
image

将交错动画和伪类伪元素结合起来写出来的慎重勇者风格的菜单本demo地址:https://codepen.io/alphardex/full/ExavZdV

最后:

恭喜你将本文读完了。不论是过了一场视觉盛宴也好,还是学到了不少东西也好,还是直接从书签那导航到这里也好(笑),CSS的力量始终超乎你的想象。只要敢于创作,你就是这个世界的神。
`

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • 原文:Chris Coyier // September 2, 2014https://centering-css...
    红绿灯的黄阅读 706评论 0 2
  • 前端开发面试题 面试题目: 根据你的等级和职位的变化,入门级到专家级,广度和深度都会有所增加。 题目类型: 理论知...
    怡宝丶阅读 2,576评论 0 7
  • 1-小测验背后的原理 今天朋友圈被这则心理年龄测试刷屏了。而且这四只的职业分别是前端、服务端、产品、测试,平时也很...
    产品张琪阅读 517评论 3 5
  • 1、青春,是一包象征着阳光的向日葵种子,在现在洒下,就会在未来得到收获,那一株株饱含青春的花朵, 2、那一缕缕充满...
    阿琳系南方姑娘阅读 288评论 0 1
  • 当所有的声音都消失了,当所有的事情都停止了,当所有的人都进入梦乡,我听到了你的歌声,如潺潺的溪流,淙淙流淌。 那是...
    芬芳岁月阅读 462评论 0 0