介绍
对赛博朋克风格的模仿学习
# 赛博朋克的按钮
- 模仿赛博朋克2077 (opens new window)按钮的效果
- 主要实现步骤
- 设定
background
是 linear-gradient() (opens new window),角度设定为45deg
,然后由透明色的50%
,切换为红色的50%
,这样50%
前的部份就会透明,而50%
后的部份就是红色,做到一个颜色分割的效果。那我们要将红色的部份占大多数,只需要将50%
改为5%
,就可以做到左下的斜角了。 - 通过
::after
选择器设置一个和按钮一样的内容 通过position
设定为absolute
让其固定在主题按钮上, 并加入text-shadow
文字阴影,在左边加入一个黄色,右边加入一个蓝色,再通过clip-path (opens new window)剪裁属性设置inset() (opens new window)矩形剪裁 剪裁多个矩形小块用变量储存起来 - 最后通过
:hover
触碰到::after
后 执行animation
动画效果- 这个是故障的效果,动画怎么会这么流畅,我们为它加一个设定,让它变得十分卡顿。设定 animation-timing-function (opens new window)
设置为 steps(2, start)
,数字越小,就越卡顿,start
是从开始就跳到其最终状态。
- 这个是故障的效果,动画怎么会这么流畅,我们为它加一个设定,让它变得十分卡顿。设定 animation-timing-function (opens new window)
- 设定
<template>
<div class="button-box">
<button>{{ text.replace(/\'/g, '') }}</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 想让伪类选择器content的内容通过v-bind绑定 就需要 "''" 包裹
const text = ref("'赛博朋克'")
</script>
<script lang="ts">
export default {
name: 'ButtOn'
}
</script>
<style lang="scss" scoped>
// 不切割效果
$slice-0: inset(50% 50% 50% 50%);
$slice-1: inset(80% -6px 0 0);
$slice-2: inset(50% -6px 30% 0);
$slice-3: inset(10% -6px 85% 0);
$slice-4: inset(40% -6px 43% 0);
$slice-5: inset(80% -6px 5% 0);
.button-box {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
background-color: #f2f31f;
}
button,
button::after {
// 设置相对定位
position: relative;
width: 380px;
height: 86px;
line-height: 86px;
color: #fff;
font-size: 36px;
// 设置字体间具
letter-spacing: 3px;
cursor: pointer;
// 取消边框
border: none;
// 取消按钮默认点击时的阴影
outline: none;
// 设置一个渐变的背景并且进行旋转
background: linear-gradient(45deg, transparent 5%, #ff3040 5%);
// 设置右侧阴影
box-shadow: 6px 0px 0px 0px #00e6f6;
}
// 通过伪类设置一个相同的按钮
button::after {
// v-bind绑定
content: v-bind('text');
// 通过绝对定位将伪类放在原来按钮的上面
position: absolute;
top: 0;
left: 0;
// 比原来的按钮多出一点蓝色
background: linear-gradient(
45deg,
transparent 3%,
#00e6f6 3%,
5%,
#ff3040 5%
);
// 设置字体阴影 上下黄色 左右蓝色
text-shadow: -3px -3px 0px #f8f005, 3px 3px 0px #00e6f6;
// 设置按钮的默认切割(不切割效果)
clip-path: $slice-0;
// transform: translateX(-20px);
}
button:hover::after {
// 设置动画 1s 2帧(步长) 让动画卡顿一点
animation: 1s buttonFrames steps(2, start);
// animation-timing-function: steps(2, start);
}
// 定义一个动画
@keyframes buttonFrames {
0% {
clip-path: $slice-1;
transform: translate(-20px, -10px);
}
10% {
clip-path: $slice-3;
transform: translate(10px, 10px);
}
20% {
clip-path: $slice-1;
transform: translate(-10px, 10px);
}
30% {
clip-path: $slice-3;
transform: translate(0px, 5px);
}
40% {
clip-path: $slice-2;
transform: translate(-5px, 0px);
}
50% {
clip-path: $slice-3;
transform: translate(5px, 0px);
}
60% {
clip-path: $slice-4;
transform: translate(5px, 10px);
}
70% {
clip-path: $slice-2;
transform: translate(-10px, 10px);
}
80% {
clip-path: $slice-5;
transform: translate(20px, -10px);
}
90% {
clip-path: $slice-1;
transform: translate(-10px, 0px);
}
100% {
clip-path: $slice-1;
transform: translate(0);
}
}
</style>
# 赛博风格图片
- 主要实现步骤
- 相同点: 使用的是相同的剪切方式实现, 从视觉上效果差不多
- 不同点: 按钮是通过伪元素实现, 而赛博风格的图片是依赖两个相同元素背景图实现的, 总共有两个元素, 第一个元素不参与剪切正常展示, 第二个元素固定在第一个元素上, 进行剪切动画, 从而实现一个赛博朋克的效果
- 通过mouseenter (opens new window)鼠标移入监听(无冒泡)监听最外层的元素(图片的元素), 当鼠标移入时给第二个元素通过classList.add (opens new window)添加一个
animation
执行剪切动画的类名, 让其执行剪切动画, 在通过mouseleave (opens new window)鼠标移除监听(无冒泡), 当监听到鼠标移除最外层的元素时候, 通过classList.remove() (opens new window)移除剪切动画类名, 让其恢复正常
- 通过mouseenter (opens new window)鼠标移入监听(无冒泡)监听最外层的元素(图片的元素), 当鼠标移入时给第二个元素通过classList.add (opens new window)添加一个
<template>
<div class="cyberpunk-box">
<div class="image-box" @mouseenter="viewHover" @mouseleave="viewLeave">
<div class="view-image" />
<div ref="viewImageAfter" class="view-image-after" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 获取需要剪裁的元素
const viewImageAfter = ref()
// 图片的地址
const url = ref(
'https://jinyanlong-1305883696.cos.ap-hongkong.myqcloud.com/image/wallhaven-wq8zyp.png'
)
// 拼接图片的地址作为背景使用
const backgroundImage = ref(`url(${url.value})`)
// 鼠标移入图片时触发的事件
const viewHover = () => {
// 添加动画类名
viewImageAfter.value?.classList.add('view-image-after-animation')
}
// 鼠标移出图片时触发的事件
const viewLeave = () => {
// 移除动画类名
viewImageAfter.value?.classList.remove('view-image-after-animation')
}
</script>
<script lang="ts">
export default {
name: 'ButtOn'
}
</script>
<style lang="scss" scoped>
// 设置动画剪裁参数
$slice-0: inset(50% 50% 50% 50%); // 还原不切割
$slice-1: inset(80% -6px 0 0);
$slice-2: inset(50% -6px 30% 0);
$slice-3: inset(10% -6px 85% 0);
$slice-4: inset(40% -6px 43% 0);
$slice-5: inset(80% -6px 5% 0);
.cyberpunk-box {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 50px;
width: 100%;
background-color: #f2f31f;
}
// 图片的样式
.image-box {
position: relative;
}
.view-image,
.view-image-after {
width: 600px;
height: 380px;
background: v-bind('backgroundImage') no-repeat;
background-size: cover;
box-shadow: 6px 0px 0px 0px #00e6f6;
}
.view-image-after {
position: absolute;
top: 0;
left: 0;
// transform: translateX(-30px);
}
.view-image-after-animation {
// 设置动画 1s 2帧(步长)
animation: 1.5s buttonFrames infinite alternate steps(2, start);
// animation-timing-function: steps(2, start);
}
// 定义切割动画
@keyframes buttonFrames {
0% {
clip-path: $slice-1;
transform: translate(-20px, -10px);
filter: blur(2px);
}
10% {
clip-path: $slice-3;
transform: translate(10px, 10px);
}
20% {
clip-path: $slice-1;
transform: translate(-10px, 10px);
}
30% {
clip-path: $slice-3;
transform: translate(0px, 5px);
}
40% {
clip-path: $slice-2;
transform: translate(-5px, 0px);
}
50% {
clip-path: $slice-3;
transform: translate(5px, 0px);
}
60% {
clip-path: $slice-4;
transform: translate(5px, 10px);
}
70% {
clip-path: $slice-2;
transform: translate(-10px, 10px);
}
80% {
clip-path: $slice-5;
transform: translate(20px, -10px);
}
90% {
clip-path: $slice-1;
transform: translate(-10px, 0px);
}
100% {
clip-path: $slice-1;
transform: translate(0);
filter: blur(0px);
}
}
</style>
# 赛博朋克的标题
- 和按钮实现的方式一样, 通过
transform
中的倾斜属性skew() (opens new window) 设置文字的倾斜度, 再通过text-shadow (opens new window) 营造文字的致幻效果, 总共三个动画- 动画
glitched
是控制文字的倾斜度和致幻效果 - 动画2
beforeglitched
是文字底部横线的损坏效果 - 动画
underline
是_
下划线进行显示隐藏, 实现一个正在输入的效果, 这里的显示隐藏取消过度动画更真实
- 动画
<!-- 赛博文标题 -->
<div class="cyberpunk-text glitched">
请求失败
<span class="cyberpunk-underline">_</span>
</div>
$redColor: #ff3040;
.cyberpunk-title {
position: relative;
font-size: 28px;
color: $redColor;
width: 100%;
}
.cyberpunk-title:before {
content: "";
display: block;
position: absolute;
bottom: -10px;
left: 2px;
width: 100%;
height: 10px;
background-color: #000;
clip-path: polygon(0px 0px, 85px 0px, 90px 5px, 100% 5px, 100% 6px, 85px 6px, 80px 10px, 0px 10px);
}
.cyberpunk-title.glitched {
animation-name: glitched;
animation-duration: calc(0.9s * 1.4);
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-timing-function: steps(2, start), cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
.cyberpunk-title.glitched:before {
animation-name: beforeglitched;
animation-duration: calc(0.9s * 2);
animation-iteration-count: infinite;
animation-timing-function: linear;
animation-timing-function: steps(2, start), cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
.cyberpunk-underline {
animation: 1s underline infinite steps(2, start), cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
// 下划线动画
@keyframes underline {
0%,
50% {
opacity: 0;
}
51%,
100% {
opacity: 1;
}
}
// 文字故障动画
@keyframes glitched {
0% {
left: -4px;
transform: skew(-20deg);
text-shadow: 0 0 rgb(0 255 255 / 50%), 0 0 rgb(255 0 0 / 50%);
}
11% {
left: 2px;
transform: skew(0deg);
text-shadow: -2px 0 rgb(0 255 255 / 50%), 2px 0 rgb(255 0 0 / 50%);
}
50% {
transform: skew(0deg);
text-shadow: -5px 0 rgb(0 255 255 / 50%), 3px 0 rgb(255 0 0 / 50%);
}
51% {
transform: skew(10deg);
}
60% {
transform: skew(0deg);
}
100% {
transform: skew(0deg);
text-shadow: 3px 0 rgb(0 255 255 / 50%), 5px 0 rgb(255 0 0 / 50%);
}
}
// 背景故障动画
@keyframes beforeglitched {
0% {
left: -4px;
transform: skew(-20deg);
clip-path: polygon(0px 0px, 85px 0px, 90px 5px, 100% 5px, 100% 6px, 85px 6px, 80px 10px, 0px 10px);
}
11% {
left: 2px;
transform: skew(0deg);
clip-path: polygon(0px 0px, 85px 0px, 90px 5px, 100% 5px, 100% 6px, 85px 6px, 80px 10px, 0px 10px);
}
50% {
transform: skew(0deg);
clip-path: polygon(0px 0px, 85px 0px, 90px 5px, 100% 5px, 100% 6px, 85px 6px, 80px 10px, 0px 10px);
}
51% {
transform: skew(0deg);
clip-path: polygon(
0px 0px,
85px 0px,
90px 5px,
100% 5px,
40% 5px,
calc(40% - 30px) 0px,
calc(40% + 30px) 0px,
calc(45% - 15px) 5px,
100% 5px,
100% 6px,
calc(45% - 14px) 6px,
calc(40% + 29px) 1px,
calc(40% - 29px) 1px,
calc(40% + 1px) 6px,
85px 6px,
80px 10px,
0px 10px
);
}
60% {
transform: skew(0deg);
clip-path: polygon(0px 0px, 85px 0px, 90px 5px, 100% 5px, 100% 6px, 85px 6px, 80px 10px, 0px 10px);
}
100% {
transform: skew(0deg);
clip-path: polygon(0px 0px, 85px 0px, 90px 5px, 100% 5px, 100% 6px, 85px 6px, 80px 10px, 0px 10px);
}
}
# 赛博霓虹字
文字的霓虹效果主要通过
text-shadow
属性实现,闪烁效果也是通过添加与文字颜色相近的text-shadow
动画来实现- 第一块文字是
ease-in-out
运动曲线动画效果 - 第二块文字通过步长
steps(2, start)
实现一个卡顿的动画效果, 有种现实中的灯光切换的感觉
- 第一块文字是
<!-- 霓虹灯效果 -->
<div>
<span class="neon">CYBER</span>
<span class="flux">PUNK</span>
</div>
<div>
<span class="neon">Merry </span>
<span class="flux">Christmas</span>
</div>
// 要把字体设置的大一点否则阴影效果不是很好
$bigSize: 120px;
@font-face {
font-family: "Teko-Light";
src: url("https://jinyanlong-1305883696.cos.ap-hongkong.myqcloud.com/font_family/Teko-Light.ttf");
}
.flux {
font-family: Teko-Light;
font-size: $bigSize;
text-shadow: 0 0 3vw #179e05;
animation: flux 2s steps(2, start) infinite; // 卡住的效果
}
.neon {
font-family: Teko-Light;
font-size: $bigSize;
text-shadow: 0 0 3vw #f4bd0a;
animation: neon 2s ease-in-out infinite; // 闪烁的效果
}
// 霓虹效果的动画 通过文字阴影实现
@keyframes neon {
0%,
100% {
text-shadow: 0 0 1vw #fa1c16, 0 0 3vw #fa1c16, 0 0 10vw #fa1c16, 0 0 10vw #fa1c16, 0 0 0.4vw #fed128,
0.5vw 0.5vw 0.1vw #806914;
color: #fffc00;
}
50% {
text-shadow: 0 0 0.5vw #800e0b, 0 0 1.5vw #800e0b, 0 0 5vw #800e0b, 0 0 5vw #800e0b, 0 0 0.2vw #800e0b,
0.5vw 0.5vw 0.1vw #40340a;
color: #806914;
}
}
@keyframes flux {
0%,
100% {
text-shadow: 0 0 1vw #10ff4c, 0 0 3vw #1041ff, 0 0 10vw #1041ff, 0 0 10vw #1041ff, 0 0 0.4vw #8bfdfe,
0.5vw 0.5vw 0.1vw #147280;
color: #03c03c;
}
50% {
text-shadow: 0 0 0.5vw #024218, 0 0 1.5vw #024713, 0 0 5vw #023812, 0 0 5vw #012707, 0 0 0.2vw #022201,
0.5vw 0.5vw 0.1vw #011a06;
color: #179e05;
}
}
# 赛博不规则致幻文本
- 通过
clip-path
进行剪裁实现不规则的矩形, 再给剪裁后的四个内边距, 让文字不受剪裁影响, 通过radial-gradient() (opens new window)线性渐变和background-size (opens new window)背景图大小设置点状背景, 最后通过:hover
设置鼠标移入后致幻文字的动画效果
<!-- 不规则致幻文本 -->
<p class="cyberpunk-text dotted">
经典的赛博朋克角色是边缘且性格疏远的独行者。他们生活在社会群体的边缘,一个弥漫反乌托邦氛围的未来:日常生活受到急剧改变的科技影响,普及的计算机化信息笼罩全球,以及侵入性的人体改造。
</p>
$redColor: #ff3040;
$backGroundColor: #000;
$borderColorr: #8ae66e;
.cyberpunk-text {
position: relative;
padding: 5px;
font-size: 26px;
background: $redColor;
padding: 35px;
padding-right: 10px;
padding-left: 10px;
padding-bottom: 30px;
line-height: 1.2;
clip-path: polygon(
0px 25px,
26px 0px,
calc(60% - 25px) 0px,
60% 25px,
100% 25px,
100% calc(100% - 10px),
calc(100% - 15px) calc(100% - 10px),
calc(80% - 10px) calc(100% - 10px),
calc(80% - 15px) 100%,
80px calc(100% - 0px),
65px calc(100% - 15px),
0% calc(100% - 15px)
);
}
/* 文本框右侧小编号样式 */
.cyberpunk-text::before {
content: "P-14";
display: block;
position: absolute;
bottom: 10px;
right: 25px;
padding: 2px 2px 0px 2px;
font-size: 12px;
line-height: 12px;
background-color: $backGroundColor;
border-right: 2px solid #8ae66e;
}
.dotted {
background-image: radial-gradient(#00000021 1px, transparent 1px);
background-size: 5px 5px;
background-position: -13px -3px;
}
.cyberpunk-text:hover {
animation: textrain 0.5s reverse infinite;
animation-timing-function: steps(2, start), cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
@keyframes textrain {
0% {
text-shadow: 0 0 rgb(0 255 255 / 50%), 0 0 rgb(255 0 0 / 50%);
}
25% {
text-shadow: -2px 0 rgb(0 255 255 / 50%), 2px 0 rgb(255 0 0 / 50%);
}
50% {
text-shadow: -5px 0 rgb(0 255 255 / 50%), 3px 0 rgb(255 0 0 / 50%);
}
100% {
text-shadow: 3px 0 rgb(0 255 255 / 50%), 5px 0 rgb(255 0 0 / 50%);
}
}
# 参考文献
纯 CSS 制作赛博朋克 2077 “故障风”按钮 (opens new window)