开发工具:Webstorm
技术栈:vue、html、canvas
实现效果(其实这里面的是动态变换的,只是没有截成GIF动图):
实现步骤:
(1)在这里的项目我是用VueCli3脚手架进行搭建的。
(2)关于动态背景的源码则是在github找的源码。
(3)总体是在App.vue中进行背景的添加
(4)因为在这里是在App.vue中添加的,所以在这里给出了不想以这种动态背景为背景的解决方案。
(5)在最后我会附上源码
(6)因为这个是我用来练手的,所以只有前台页面,而且很多不完善,但是大家可以借鉴一下这个代码,然后在这个基础上大家可以*发挥。如果大家想下载源码文件的话。
需要注意,因为我在登陆的时候与后端进行了交互,大家可以改一下Login.vue的登录的代码即可,修改成直接跳转就可以,因为时间关系,在这里我就不一一阐述了。
1、黑客帝国实现源码:
(1)大家可以创建一个vue文件,将其复制进去即可,给大家贴一个结构目录。
<template>
<div class="main">
<canvas id="vue-matrix-raindrop"></canvas>
</div>
</template>
<script>
export default {
name: 'vue-matrix-raindrop',
props:{
canvasWidth:{
type:Number,
default:1900
},
canvasHeight:{
type:Number,
default:1200
},
fontSize:{
type:Number,
default:20
},
fontFamily:{
type:String,
default:'arial'
},
textContent:{
type:String,
default:'abcdefghijklmnopqrstuvwxyz'
},
textColor:{
type:String,
default:'#0F0',
validator:function(value){
var colorReg = /^#([0-9a-fA-F]{6})|([0-9a-fA-F]{3})$/g
return colorReg.test(value)
}
},
backgroundColor:{
type:String,
default:'rgba(0,0,0,0.1)',
validator:function(value){
var reg = /^[rR][gG][Bb][Aa][\(]((2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?),){2}(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?),?(0\.\d{1,2}|1|0)?[\)]{1}$/;
return reg.test(value);
}
},
speed:{
type:Number,
default:2,
validator:function(value){
return value%1 === 0;
}
}
},
mounted:function(){
this.initRAF();
this.initCanvas();
this.initRainDrop();
this.animationUpdate();
},
methods:{
initRAF(){
window.requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
window.cancelAnimationFrame = (function () {
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
function (id) {
window.clearTimeout(id);
};
})();
},
initCanvas(){
this.canvas = document.getElementById('vue-matrix-raindrop');
//需要判断获取到的canvas是否是真的canvas
if(this.canvas.tagName.toLowerCase() !== 'canvas'){
console.error("Error! Invalid canvas! Please check the canvas's id!")
}
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
console.log(this.canvas.height )
this.canvasCtx = this.canvas.getContext('2d');
this.canvasCtx.font = this.fontSize+'px '+this.fontFamily;
this.columns = this.canvas.width / this.fontSize;
},
initRainDrop(){
for(var i=0;i<this.columns;i++){
this.rainDropPositionArray.push(0);
}
},
animationUpdate(){
this.speedCnt++;
//speed为1最快,越大越慢
if(this.speedCnt===this.speed){
this.speedCnt = 0;
//绘制背景
this.canvasCtx.fillStyle=this.backgroundColor;
this.canvasCtx.fillRect(0,0,this.canvas.width,this.canvas.height);
//绘制文字
this.canvasCtx.fillStyle=this.textColor;
for(var i=0,len=this.rainDropPositionArray.length;i<len;i++){
this.rainDropPositionArray[i]++;
var randomTextIndex = Math.floor(Math.random()*this.textContent.length);
var randomText = this.textContent[randomTextIndex];
var textYPostion = this.rainDropPositionArray[i]*this.fontSize;
this.canvasCtx.fillText(randomText,i*this.fontSize,textYPostion);
if(textYPostion>this.canvasHeight){
if(Math.random()>0.9){
this.rainDropPositionArray[i]=0;
}
}
}
}
window.requestAnimationFrame(this.animationUpdate)
}
},
data () {
return {
canvasCtx:null,
canvas:null,
columns:0,
rainDropPositionArray:[],
speedCnt:0
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
canvas {
position: absolute;
}
.component{
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
}
.userName {
position: absolute;
top: 40%;
left: 20%;
}
</style>
2、对于App.vue文件中的修改:
(1)注意上一个特效文件的路径的修改。
<template>
<div id="app" style="width: 100%;height:100%">
<VueMatrixRaindrop></VueMatrixRaindrop>
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
//这里根据自己的前面的特效vue项目的具体目录合理选择
import VueMatrixRaindrop from './views/vue-matrix-digit-rain'
export default {
name: 'app',
components: {
VueMatrixRaindrop,
},
methods:{
}
}
</script>
3、但是如果是后台文件不想以这种动态背景为背景,那怎么办呢?
在这里主要借助z-index属性进行解决。在这里我主要以后台的管理页面为例子,来简单说明一下。
效果实现(可以自定义背景):
copyMenu.vue 代码实现:
<template>
<el-container class="home-container">
<!-- 头部区域 -->
<el-header>
<div>
<div class="home">
<img src="../assets/logo.png" alt="" />
</div>
<span>电商后台管理系统</span>
</div>
<el-button type="info" @click="logout">退出</el-button>
</el-header>
<!-- 页面主体区域 -->
<el-container>
<!-- 侧边栏 -->
<el-aside :width="isCollapse ? '64px' : '200px'">
<div class="toggle-button" @click="toggleCollapse">|||</div>
<!-- 侧边栏菜单区域 -->
<el-menu background-color="#333744" text-color="#fff" active-text-color="#409EFF" unique-opened :collapse="isCollapse" :collapse-transition="false" router :default-active="activePath">
<!-- 一级菜单 -->
<el-submenu :index="item.id + ''" v-for="item in menulist" :key="item.id">
<!-- 一级菜单的模板区域 -->
<template slot="title">
<!-- 图标 -->
<i :class="iconsObj[item.id]"></i>
<!-- 文本 -->
<span>{{ item.authName }}</span>
</template>
<!-- 二级菜单 -->
<el-menu-item :index="'/' + subItem.path" v-for="subItem in item.children" :key="subItem.id" @click="saveNavState('/' + subItem.path)">
<template slot="title">
<!-- 图标 -->
<i class="el-icon-menu"></i>
<!-- 文本 -->
<span>{{ subItem.authName }}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<!-- 右侧内容主体 -->
<el-main>
<!-- 路由占位符 -->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
<script>
export default {
name: "copy-menu",
data() {
return {
// 左侧菜单数据
menulist: [],
iconsObj: {
'125': 'iconfont icon-user',
'103': 'iconfont icon-tijikongjian',
'101': 'iconfont icon-shangpin',
'102': 'iconfont icon-danju',
'145': 'iconfont icon-baobiao'
},
// 是否折叠
isCollapse: false,
// 被激活的链接地址
activePath: ''
}
},
created() {
this.getMenuList()
this.activePath = window.sessionStorage.getItem('activePath')
},
methods: {
logout() {
window.sessionStorage.clear()
this.$router.push('/login')
},
// 获取所有的菜单
getMenuList() {
console.log("测试")
//const router = useRouter()
var bmenus =
[
{
"id": 125,
"authName": "用户管理",
"path": "users",
"children": [
{
"id": 110,
"authName": "用户列表",
"path": "user",
"children": [],
"order": null
},
{
"id": 210,
"authName": "异步状态",
"path": "netTick",
"children": [],
"order": null
},
{
"id": 211,
"authName": "表格测试",
"path": "table",
"children": [],
"order": null
}
],
"order": 1
},
{
"id": 103,
"authName": "权限管理",
"path": "rights",
"children": [
{
"id": 111,
"authName": "角色列表",
"path": "roles",
"children": [],
"order": null
},
{
"id": 112,
"authName": "权限列表",
"path": "rights",
"children": [],
"order": null
}
],
"order": 2
},
{
"id": 101,
"authName": "商品管理",
"path": "goods",
"children": [
{
"id": 104,
"authName": "商品列表",
"path": "goods",
"children": [],
"order": 1
},
{
"id": 115,
"authName": "分类参数",
"path": "params",
"children": [],
"order": 2
},
{
"id": 121,
"authName": "商品分类",
"path": "categories",
"children": [],
"order": 3
}
],
"order": 3
},
{
"id": 102,
"authName": "订单管理",
"path": "orders",
"children": [
{
"id": 107,
"authName": "订单列表",
"path": "orders",
"children": [],
"order": null
}
],
"order": 4
},
{
"id": 145,
"authName": "数据统计",
"path": "reports",
"children": [
{
"id": 146,
"authName": "数据报表",
"path": "reports",
"children": [],
"order": null
}
],
"order": 5
}
];
this.menulist = bmenus;
//const { data: res } = await this.$http.get('menus')
//if (res.meta.status !== 200) return this.$message.error(res.meta.msg)
//this.menulist = res.data
//console.log(res)
},
// 点击按钮,切换菜单的折叠与展开
toggleCollapse() {
this.isCollapse = !this.isCollapse
},
// 保存链接的激活状态
saveNavState(activePath) {
window.sessionStorage.setItem('activePath', activePath)
this.activePath = activePath
}
}
}
</script>
<style lang="less" scoped>
.outLine{
width: 100%;
height: 100%;
z-index: 1;
}
.home-container {
height: 100%;
width: 100%;
z-index: 1;
}
.el-header {
z-index: 1;
background-color: #4a5064;
display: flex;
justify-content: space-between;
padding-left: 0;
align-items: center;
color: #fff;
font-size: 20px;
> div {
display: flex;
align-items: center;
span {
margin-left: 15px;
}
}
}
.el-aside {
z-index: 1;
background-color: #333744;
.el-menu {
border-right: none;
}
}
.el-main {
z-index: 1;
background-color: #eaedf1;
}
.iconfont {
margin-right: 10px;
}
.toggle-button {
background-color: #4a5064;
font-size: 10px;
line-height: 24px;
color: #fff;
text-align: center;
letter-spacing: 0.2em;
cursor: pointer;
}
.home{
width:60px;
height: 60px;
img{
width:100%;
height: 100%;
}
}
</style>
在这里我将这个练习源码上传到CSDN(不需要积分),大家可以下载下来有时间实现一下。
https://download.csdn.net/download/weixin_43388691/86932762