快来看,使用 CSS 打造复古老电影效果🎬

309 阅读9分钟

前言

本文将通过CSS来实现老电影的复古风特效,满满的怀旧感。整个案例,主要依靠CSS的filter属性,配合keyframe帧动画来实现。本文案例演示完之后,希望能够让大家收货不少干货知识!

下面先来看一下最终效果(在PC浏览器下查看效果更佳!):

接下来,我们要来仔细看看如何实现的。

1. HTML结构及初始化样式

说起老电影,大家可能会想到电影画面上不断闪烁的黑线以及像小飞虫一样不停闪烁的黑点(实际是因为老电影胶片上的划痕以及拍摄过程中的噪点导致的),我们先看一下效果实现所需要的HTML结构:

<body>
  <input type="checkbox" id="invert"><label for="invert"></label>
  <input type="checkbox" id="sepia"><label for="sepia"></label>
  <div class="content">
    <div class='film'>
      <div class="grain"></div>
    </div>
  </div> 
</body>

整个案例的HTML结构很简单

  • 两个input[checkbox]元素的按钮,用来切换不同的效果,每个input元素关联一个label元素
  • div.content 元素,用来展示图片背景
  • div.film 元素,用来做模糊效果
  • div.grain 元素,用来做颗粒效果

下面是一些初始化的样式,去除body元素的边距,背景色等样式:

:root {
  --trsn: all 0.5s ease 0s;
  --black: #000;
}

*,
*:before,
*:after {
  position: absolute;
}

body {
  margin: 0;
  padding: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--black);
}

2. 添加图片背景样式

.content {
  width: 100vw;
  height: 100vh;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: url(https://img.picgo.net/2024/09/04/2fdda3cc7cd98d1001e9c9ad5069af0e7bec55e73aaa257752a3429821c8.jpeg) no-repeat center center;
  background-size: cover;
  transition: var(--trsn);
}

.content:before {
  content: "雪山图片";
  font-family: Arial, Helvetica, serif;
  bottom: 0vmin;
  font-weight: 500;
  font-size: 1.35vmin;
  color: #fffD;
  border-radius: 1px;
  padding: 20vmin 2vmin 3vmin;
  letter-spacing: 0.5px;
  word-spacing: 3px;
  background: linear-gradient(0deg, black, transparent);
  width: 100%;
  text-align: center;
}

.content:after {
  content: "";
  width: 100%;
  height: 100%;
  box-shadow: 0 0 20vmin 0vmin var(--black) inset, 0 0 3vmin 0.5vmin var(--black) inset, 0 0 1vmin 1vmin var(--black);
}

这段css中,主要用来设置电影的基础样式。

首先我们通过backgroundcontent元素设置了一张背景图片,背景图片设置为居中显示,同时设置background-size: cover;,让背景图片能够铺满整个元素。

其次我们通过伪类设置了图片的标题介绍(:before设置)和模仿电影镜头的边框(:after设置),通过box-shadow属性,我们设置了内阴影。

box-shadow 允许为元素添加阴影效果,这个属性可以设置多个阴影效果,每个阴影效果之间用逗号分隔。box-shadow属性可以设置的值包括阴影的X轴偏移量、Y轴偏移量、模糊半径、扩散半径和颜色。基本语法如下:box-shadow: horizontal-offset vertical-offset blur-radius spread-radius color;

在示例代码中,box-shadow: 0 0 20vmin 0vmin var(--black) inset, 0 0 3vmin 0.5vmin var(--black) inset, 0 0 1vmin 1vmin var(--black);,我们设置了3个阴影值,分别用‘,’隔开。

  1. 第一个阴影值表示,水平偏移量为0,垂直偏移量为0,模糊半径为20vmin,扩展半径为0的黑色内阴影;
  2. 第二个阴影值表示,水平偏移量为0,垂直偏移量为0,模糊半径为3vmin,扩展半径为0.5vmin的黑色内阴影;
  3. 第三个阴影值表示,水平偏移量为0,垂直偏移量为0,模糊半径为1vmin,扩展半径为1vmin的黑色外阴影;

3. 添加特效

在前面我们仔细分析了HTML结构,接下来我们正式开始来实现老电影的特效。

3.1 模拟竖线

首先,我们通过伪类+背景的方式去实现界面上的竖线效果:

.film {
  width: 100%;
  height: 100%;
  filter: blur(0.45px) drop-shadow(0px 0px 0px #fff1);
}

.film:before,
.film:after {
  content: '';
  width: 120%;
  height: 100%;
  top: 0;
  left: 0;
  padding-left: 100px;
  opacity: 0.5;
  background: repeating-linear-gradient(90deg, #0002 0 2px, transparent 4px 37vmin);
}

.film:after {
  left: 30%;
}

在这段css中,我们给.film元素宽高分别设置为100%,让其能够铺满整个父元素的内容区域。我们还设置了filter: blur(0.45px) drop-shadow(0px 0px 0px #fff1);, 这样会给.film元素一个0.45px的模糊效果,同时添加白色阴影。

然后我们设置其:before:after伪类,给它们添加宽高和定位。这个时候我们在界面上没有看到竖线的效果,那怎么来实现呢?

如果是普通元素,我们可能想到通过在其内添加子元素的方式来模拟细线的效果,但是对于伪类,我们无法直接去添加DOM元素。但是,我们可以去设置背景来模拟细线的效果:

background: repeating-linear-gradient(90deg, #0002 0 2px, transparent 4px 37vmin);

这个样式就是给其设置了一个重复线性渐变的背景样式,这个渐变背景的渐变角度为90度,会重复显示0.2透明度的黑色线条。现在,我们可以看到界面上出现了4条黑线。

3.2 模拟噪点

接下来,我们模拟一下老电影的噪点效果:

.grain {
  width: 100%;
  height: 100%;
}

.grain:after {
  content: '';
  width: 110%;
  height: 110%;
  top: -5%;
  left: -5%;
  opacity: .25;
  background-image:
    repeating-conic-gradient(var(--black) 0%, transparent .00003%, transparent .0005%, transparent .00095%),
    repeating-conic-gradient(var(--black) 0%, transparent .00005%, transparent 0.00015%, transparent 0.0009%);
  filter: drop-shadow(0px 0px 1px black);
}

在这段样式中,我们给.grain元素添加了:after伪类,通过设置其背景样式background-image:repeating-conic-gradient(var(--black) 0%, transparent .00003%, transparent .0005%, transparent .00095%), repeating-conic-gradient(var(--black) 0%, transparent .00005%, transparent 0.00015%, transparent 0.0009%);来实现了噪点的效果。repeating-conic-gradient 是一个渐变函数,它的值是渐变的颜色和停止点,如果有多个值的话,用,来分隔。

这个时候我们可以看到界面上的噪点比较大比较黑,所以添加了opacity: .25;使界面上的黑点变得柔和自然一些。

repeating-conic-gradient 是 CSS 中的一种渐变函数,用于创建重复的圆锥渐变效果。它通常用于背景样式,可以产生一种环形的渐变效果,适合用于图形、按钮或装饰性元素。

3.3 添加动画

好了,现在我们可以在界面上看到竖线和黑色噪点,但是目前的效果是静止不动的,和我们印象中的老电影效果还差了点。接下来我们就是要让这些内容动起来。

在CSS中想要实现动画效果,我们第一反应就是通过keyframes帧动画来实现。

.film:before {
  animation: film-scratch 0.45s steps(1) infinite;
}

.film:after {
  animation: effect-scratch 2s infinite;
}

@keyframes film-scratch {
  0%,
  100% {
    transform: translateX(0);
    opacity: 0.5;
  }

  10% {
    transform: translateX(-1%);
  }

  20% {
    transform: translateX(1%);
  }

  30% {
    transform: translateX(-2%);
    opacity: 0.75;
  }

  40% {
    transform: translateX(3%);
  }

  50% {
    transform: translateX(-3%);
    opacity: 0.5;
  }

  60% {
    transform: translateX(8%);
  }

  70% {
    transform: translateX(-3%);
  }

  80% {
    transform: translateX(10%);
    opacity: 0.25;
  }

  90% {
    transform: translateX(-2%);
  }
}

@keyframes effect-scratch {
  0% {
    transform: translateX(0);
    opacity: 0.75;
  }

  10% {
    transform: translateX(-1%);
  }

  20% {
    transform: translateX(1%);
  }

  30% {
    transform: translateX(-2%);
  }

  40% {
    transform: translateX(3%);
  }

  50% {
    transform: translateX(-3%);
    opacity: 0.5;
  }

  60% {
    transform: translateX(8%);
  }

  70% {
    transform: translateX(-3%);
  }

  80% {
    transform: translateX(10%);
    opacity: 0.25;
  }

  90% {
    transform: translateX(20%);
  }

  100% {
    transform: translateX(30%);
  }
}

在这里,为了体现竖线动画的随机性,我们给:before:after伪类分别设置了各自的动画步骤,在不同的时机点,竖线位置再水平方向上会进行平移。

关于keyframes帧动画的相关知识,可以参考文章详解CSS帧动画——keyframes,本文不在赘述。

接下来我们继续给噪点也添加上动画,同样也是通过keyframes帧动画来实现:

.grain:after {
  animation: grain 0.5s steps(1) infinite;
}
@keyframes grain {
  0%,
  100% {
    transform: translate(0, 0);
  }

  10% {
    transform: translate(-1%, -1%);
  }

  20% {
    transform: translate(1%, 1%);
  }

  30% {
    transform: translate(-2%, -2%);
  }

  40% {
    transform: translate(3%, 3%);
  }

  50% {
    transform: translate(-3%, -3%);
  }

  60% {
    transform: translate(4%, 4%);
  }

  70% {
    transform: translate(-4%, -4%);
  }

  80% {
    transform: translate(2%, 2%);
  }

  90% {
    transform: translate(-3%, -3%);
  }
}

到这里,我们现在就实现了老电影的界面效果了。是不是非常的具有复古风和怀旧感呢!

4. 其它效果

在前面的HTML结构中,我们还有两个input元素没有讲到。这两个type="checkbox"input元素是用来控制演示CSS中filter属性其它函数的。在我们实现老电影的效果中,我们只使用了了filter属性的blurdrop-shadow两个函数,实际上它还可以使用其它的函数,不同的函数代表着不同的效果。这两个按钮就是用来演示filter属性的sepia函数和invert函数的效果。

@property --pos {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 20%;
}

input {
  display: none;
}

label {
  position: absolute;
  background: #0008;
  bottom: 7vmin;
  z-index: 3;
  width: 12vmin;
  height: 5vmin;
  margin-left: 15vmin;
  border-radius: 5vmin;
  cursor: pointer;
  border-bottom: 1px solid #fff4;
  border-top: 1px solid var(--black);
}

label[for=invert] {
  margin-left: -15vmin;
}

label:before {
  width: 4vmin;
  height: 4vmin;
  background: #485637;
  content: "";
  border-radius: 100%;
  box-sizing: border-box;
  top: 0.5vmin;
  left: 0.75vmin;
  box-shadow: 2px 0px 4px 0px #000c;
}

label:after {
  font-family: Arial, Helvetica, serif;
  font-weight: 500;
  font-size: 1.25vmin;
  border-radius: 1px;
  content: "棕褐色滤镜";
  color: #fffd;
  bottom: 6.5vmin;
  background: linear-gradient(180deg, black 3.35vmin, #fff0 3.35vmin), conic-gradient(from -45deg at var(--pos) 100%, var(--black) 0 25%, #fff0 0 100%);
  padding: 1vmin 0.5vmin 2vmin;
  text-shadow: none;
  letter-spacing: 0.5px;
  margin-left: 0;
  opacity: 0.65;
  transition: var(--trsn);
  width: 100%;
  box-sizing: border-box;
  text-align: center;
}

label[for=invert]:after {
  content: "反转颗粒效果";
}

/*** checked ***/
input:checked+label:before {
  left: 7.25vmin;
  transition: var(--trsn);
  background: #39b13e;
}

input#sepia:checked~.content {
  filter: sepia(0.75);
}

input#invert:checked~.content .film {
  filter: invert(1)
}

input:checked+label:after {
  animation: arrow 0.5s ease 0s 1;
  animation-fill-mode: forwards;
  transition: var(--trsn);
  filter: invert(1);
}

input:checked+label[for=sepia]:after {
  filter: invert(0.75) sepia(1);
}

@keyframes arrow {
  0% {
    --pos: 20%;
  }

  100% {
    --pos: 80%;
  }
}

对于input元素和label元素的样式就不在解释,核心样式在于下面这部分:

input#sepia:checked~.content {
  filter: sepia(0.75);
}

input#invert:checked~.content .film {
  filter: invert(1)
}

第一个表示#sepiainput元素选中之后,给.content元素添加了一个0.75的黄褐色滤镜,第二个表示#invertinput元素选中之后,给.film元素添加了一个1的颗粒颜色反转效果,具体效果我们可以在最终的demo中体验一下。

小结

本文中,我们先演示了一下老电影的效果,然后分析效果实现所需要的HTML结构,再给HTML结构实现基础样式, 最后通过CSS中的filter属性和keyframs帧动画实现老电影的竖线和噪点的动画效果,从而完成了充满了复古风格的老电影特效。文中关于filter属性只是对用到的函数做了一些解释,如果需要了解更多相关知识,需要读者自行查阅资料。

文中如有错误,敬请指正。