说实话,这种水平的页面完全没必要用Vue来写,直接写H5再用个jQuery反到更顺手一点。那为啥我非要用Vue呢?正所谓学了就要用嘛,寒假好不容易花大半个月看完的感觉不整个活来用用久而久之就会忘了。

先从背景入手

主页的背景图一定要是高质量的壁纸嘛,不仅如此,还要有动画~

于是我就大概用CSS实现了一个缓慢拉近的动画:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.bg-scaled {
animation: background-scale 30s cubic-bezier(0.14, 0.26, 0.51, 0.96);
-webkit-animation: background-scale 30s cubic-bezier(0.14, 0.26, 0.51, 0.96);
transform: scale(1.2);
}

@keyframes background-scale {
from {
transform: scale(1.0);
}

to {
transform: scale(1.2);
}
}

因为要实现的是缓慢拉近,所以不能直接套个正弦的easeInOut曲线上去,这里我用工具生成了一个导数逐渐递减(我也不知道我为啥要这样表述)的贝塞尔曲线作为动画的曲线。至于怎么生成的嘛,那我只能说Chrome的DevTools真香~

因为背景太亮了不能突出主体,所以加了一个filter: brightness(0.7)的属性来调暗。

然后是主体部分

主体部分采用flex布局的方式居中于整个页面。一开始因为不咋懂Flex布局,用的是下面这个方案:

1
2
3
4
5
6
.main {
position: absoule;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}

绝对布局加上位移,确实能居中是不错,但居中之后非常鸡肋,甚至对后面的部分有影响,最后还是干脆去把Flex布局复习了一遍,弃用了这个方案。

带动画的头像框

头像框的设计灵感来自于Skyil的主页 (得抄,但又不能完全抄

3D翻转动画的实现:

1
2
3
4
5
6
7
8
<div class="avatar-container" @mouseover="rotate(true)" @mouseout="rotate(false)">
<a href="/">
<img :src="config.avatarImage" :class="{ overturnAnimated : overturnAnimated }" class="avatar-img">
<div class="info-text" :class="{ overturnAnimated: true, infoTextTurned : overturnAnimated }">
{{ config.avatarText }}
</div>
</a>
</div>

这个info-text的div实际上是还是使用了绝对布局加位移来与头像框叠加的:

1
2
3
4
5
6
7
8
9
10
11
.info-text {
background: rgba(0,0,0,.5);
position: absolute;
border-radius: 50%;
backface-visibility: hidden;
text-align: center;
line-height: 120px;
font-size: 21px;
top: 0;
font-family: MoeFonts,sans-serif;
}

值得一提的是backface-visibility: hidden属性,它使得这个div在翻转到背面时不可见。

在鼠标悬浮时添加类overturnAnimated

1
2
3
4
5
6
7
8
9
10
data(){
return {
overturnAnimated: false
}
},
methods: {
rotate(animate){
this.overturnAnimated = animate
}
}

为了分别管理,overturnAnimated这个类我单独放在了animate.css里面:

1
2
3
.overturnAnimated {
transform: rotateY(-180deg);
}

还有一点需要注意的地方,因为一开始背面的字是不可见而触发函数之后才可见,所以背面的css属性变化要与正面相反。也就是说,正面需要从0度转到180度,那么背面就要从180度转到360度(或者从-180度转到0度),所以这里还得给背面的动画单独指定一个class:

1
2
3
.infoTextTurned {
transform: rotateY(-360deg);
}

列表部分

中间的几个链接都是用列表实现的,方法也很简单,用list-style: none可以去掉列表前面的圆点,用float: left可以让列表横向排列。除此之外ul这个标签自带了2em的上下边距,用margin: 0去掉即可。

有一个细节的地方,就是列表上面与一句话连接的那条细线。这个实现的方式是我从另一个页面中无意发现的,用到了CSS伪元素:

1
2
3
4
5
6
7
8
9
10
.columns:before {
content: '';
height: 41px;
display: block;
position: absolute;
left: 50%;
transform: translateY(-100%);
width: 1px;
background: white;
}

这个伪元素运用的很巧妙,就是把元素位置设置为绝对,然后用位移来移动到主元素的上面,左绝对距离是主元素的百分之五十,然后将宽度设置为1像素,背景为白色。另外content: ''这个属性一定要有,否则会因为没有内容而不渲染这个元素。

其他部分

其他也就剩下一句话和图标的部分了。一句话是在一个给定的列表中随机选取的,后面可能会接入一言接口啥的。

图标部分用的是Font Awesome的Vue组件包,直接导入就能用(知道我为啥要用Vue了吧

不过在使用这个组件的时候也踩了一个坑:我的Vue是3.x版本的,而这个组件模块好像只支持Vue2.x版本。一开始我的想法是把Vue降级到2.x版本,然后发现降级之后CLI也得跟着降级,然后Vite框架貌似就会出问题。最后折腾了半天,在组件的GitHub主页下面的说明里发现了针对Vue3.x版本的pre-release版本,一安装果然能用,这就是用第三方模块不看文档的后果。。。

全局动画

在模仿(抄袭)Skyil的主页的时候,发现它的所有元素逐渐上浮的动画特别好看。一开始我没分析它的源码,自己想办法实现,结果实现了半天都实现不出来(我承认我CSS学的太菜了

无奈之下打开控制台看了一下源码,发现是通过计时器来添加class来实现了,看来是老夫失算了,它竟然不讲武德的用了js。但是问题来了,在Vue里用js来控制DOM怎么写都感觉特别别扭。。jQuery又不能用,看来只能将就一下了。

于是我干脆把动画逻辑放到了单独的animate.js里面,防止跟Vue的代码混在一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
export default function () {
let contents = document.getElementById("main").children
let extra = document.getElementsByClassName("up-float")
let items = [...contents]
items.push(...extra)

for (let i = 0; i < items.length; i++) {
setTimeout(() => {
console.log(items[i]);
items[i].classList.add("up-floated")
}, i * 200)
}
}

然后只需要在Vue的app挂载之后调用这个函数就行了。

1
2
3
4
5
6
7
import loadAnimation from 'src/utils/animate.js'

...
mounted() {
loadAnimation()
}
...

CSS部分还是用的位移,只不过这次有动画:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.up-floated{
animation: up-float 0.9s cubic-bezier(0.07, 0.49, 0.4, 0.97);
opacity: 1!important;
}

@keyframes up-float {
from {
transform: translateY(120px);
opacity: 0;
}
to {
transform: none;
opacity: 1;
}
}

HTML注入

既然是网页,那得有标题和图标才行。标题一开始是通过在Vue的app创建之后执行document.title = "xxx"来实现的。但是图标呢?

要知道,Vue的控制区域只有app这个div,而link标签是要放在HTML的head标签里面的,Vue完全控制不了。

于是在我思索良久之后(加戏ing),想到了以前用webpack时,有个东西叫webpack-html-plugin可以直接在打包的时候向index.html中注入任意代码,那么Vite是不是·也有类似的插件呢?

后来还真被我找到了:https://github.com/anncwb/vite-plugin-html

甚至名字都差不多跟webpack的一样,这样就可以在vite里直接配置页面的标题和图标了。

index.html里插入:

1
2
3
4
5
6
<head>
<meta charset="UTF-8">
<%- favico %>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%- title %></title>
</head>

在vite.config.js里配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...

plugins: [
vue(),
minifyHtml(),
injectHtml({
injectData: {
title: config.title,
favico: `<link rel="icon" href="${config.favico}"/>`
},
}),
]

...

因为我所有的配置都是放在config.js里面的,Vue和Vite都从这里导入,这样需要修改的时候就只需要改这一个文件就行了。

另外,在使用这个插件的时候还把Vite升级到了2.0.0版本,话说版本这一块真的玄学,Vite的版本2.3.1不行,2.2.0不行,偏偏降到2.0.0又行了。不能太高也不能低,要不是老子有耐心愿意多试几次,不然就没得用了。。

其他

除了主要的东西之外,页面的右下角还加了一个没啥用的东西,具体是啥我就不提了,懂得都懂(