vue-particles:
安装
npm install vue-particles --save-dev
引入
main.ts
import Vue from 'vue'
import VueParticles from 'vue-particles'
Vue.use(VueParticles)
属性
别人总结的,直接拿来了,方便查看
- particlesNumber: Number类型。默认80。粒子数量。
- shapeType: String类型。默认’circle’。可用的粒子外观类型有:“circle”,“edge”,“triangle”, “polygon”,“star”。
- particleSize: Number类型。默认80。单个粒子大小。
- linesColor: String类型。默认’#dedede’。线条颜色。
- linesWidth: Number类型。默认1。线条宽度。
- lineLinked: 布尔类型。默认true。连接线是否可用。
- lineOpacity: Number类型。默认0.4。线条透明度。
- linesDistance: Number类型。默认150。线条距离。
- moveSpeed: Number类型。默认3。粒子运动速度。
- hoverEffect: 布尔类型。默认true。是否有hover特效。
- hoverMode: String类型。默认true。可用的hover模式有: “grab”, “repulse”, “bubble”。
- clickEffect: 布尔类型。默认true。是否有click特效。
- clickMode: String类型。默认true。可用的click模式有: “push”, “remove”, “repulse”, “bubble”。
实例
重要在实践!!!
BackgroundParticles.vue:
子组件
<template>
<vue-particles
id="bg-particles"
:clickEffect="true"
:options="particlesOpts"
/>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { ParticlesComponent as VueParticles } from 'particles.vue3'
export default defineComponent({
name: 'BackgroundParticles',
components: {
VueParticles,
},
setup() {
const particlesOpts = {
particles: {
number: {
value: 50,
density: {
enable: true,
valueArea: 400,
},
},
color: {
value: '#3CB2BE',
},
shape: {
type: 'circle',
stroke: {
width: 0,
color: '#000000',
},
polygon: {
nbSides: 5,
},
},
opacity: {
value: 1,
random: false,
anim: {
enable: false,
speed: 1,
opacityMin: 0.1,
sync: false,
},
},
size: {
value: 5,
random: true,
anim: {
enable: false,
speed: 40,
sizeMin: 0.1,
sync: false,
},
},
lineLinked: {
enable: true,
distance: 160,
color: '#3CB2BE',
opacity: 0.5,
width: 1,
},
move: {
enable: true,
speed: 1,
direction: 'none',
random: true,
straight: false,
outMode: 'out',
bounce: false,
attract: {
enable: false,
rotateX: 600,
rotateY: 1200,
},
},
},
interactivity: {
detectsOn: 'canvas',
events: {
onHover: {
enable: true,
mode: 'bubble',
},
onClick: {
enable: false,
mode: 'push',
},
resize: true,
},
modes: {
grab: {
distance: 400,
lineLinked: {
opacity: 1,
},
},
bubble: {
distance: 400,
size: 8,
duration: 10,
opacity: 0.248,
speed: 3,
},
repulse: {
distance: 200,
duration: 0.4,
},
push: {
particlesNb: 4,
},
remove: {
particlesNb: 2,
},
},
},
detectRetina: true
}
return {
particlesOpts,
}
},
})
</script>
<style scoped>
#bg-particles {
position: absolute;
width: 100%;
height: 100%;
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
}
</style>
Login.vue:
父组件
<template>
<div class="login-container">
<background-particles/>
<n-form
ref="loginFormRef"
:model="loginForm"
:rules="loginRules"
label-placement="left"
size="small"
class="login-form"
>
<h1 class="form-title">智慧城市3D可视化平台</h1>
<n-form-item path="username">
<n-input
v-model:value="loginForm.username"
placeholder="用户名"
type="text"
size="large"
:style="inputStyles"
>
<template #prefix>
<n-icon>
<IconUser/>
</n-icon>
</template>
</n-input>
</n-form-item>
<n-form-item path="password">
<n-tooltip :show="capsTooltip" placement="top-start">
<template #trigger>
<n-input
v-model:value="loginForm.password"
placeholder="请输入密码"
type="password"
size="large"
show-password-on="click"
:style="inputStyles"
@keydown="checkCapslock"
@blur="capsTooltip = false"
@keyup.enter="handleLogin"
>
<template #prefix>
<n-icon>
<IconKey/>
</n-icon>
</template>
</n-input>
</template>
<span> 大写锁定已打开 </span>
</n-tooltip>
</n-form-item>
<n-button
:loading="loading"
type="primary"
size="large"
style="width: 100%; margin-bottom: 20px;"
@click="handleLogin"
>
登录
</n-button>
<div style="position: relative;">
<div class="tips">
<span class="tips-text">忘记密码?</span>
</div>
<div class="lang-select">
<span class="lang-chang">中英切换</span>
<g-lang-select/>
</div>
</div>
</n-form>
</div>
</template>
<script lang='ts'>
import {defineComponent, ref, watch, defineAsyncComponent} from 'vue'
import {useRouter, useRoute} from 'vue-router'
import {UserStore} from '@/domains/user'
import {IconUser, IconKey} from '@/icons'
const validateUsername = (rule: any, value: string, callback: Function) => {
if (!['admin', 'editor'].includes(value)) {
callback(new Error('请输入正确的用户名'))
} else {
callback()
}
}
const validatePassword = (rule: any, value: string, callback: Function) => {
if (value.length < 6) {
callback(new Error('密码不能少于6位'))
} else {
callback()
}
}
const getOtherQuery = (query: any) => {
return Object.keys(query).reduce((acc: any, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc
}, {})
}
export default defineComponent({
name: 'Login',
components: {
BackgroundParticles: defineAsyncComponent(() => import('./background-particles.vue')),
IconUser,
IconKey,
},
setup() {
const loginForm = ref({
username: 'admin',
password: '123456',
})
const loginRules = ref({
username: [{required: true, trigger: 'blur', validator: validateUsername}],
password: [{required: true, trigger: 'blur', validator: validatePassword}],
})
const loginFormRef = ref(null)
const capsTooltip = ref(false)
const loading = ref(false)
const redirect = ref('')
const otherQuery = ref({})
const inputStyles = {
'--caret-color': '#fff',
'--text-color': '#fff',
'--border': '1px solid rgba(76, 206, 240, 0.5)',
'--color': 'transparent',
'--color-focus': 'transparent',
'--color-focus-error': 'transparent',
'--color-focus-warning': 'transparent',
}
const route = useRoute()
const router = useRouter()
const checkCapslock = ({shiftKey, key}: any) => {
if (key && key.length === 1) {
if (shiftKey && (key >= 'a' && key <= 'z') || !shiftKey && (key >= 'A' && key <= 'Z')) {
capsTooltip.value = true
} else {
capsTooltip.value = false
}
}
if (key === 'CapsLock' && capsTooltip.value === true) {
capsTooltip.value = false
}
}
const handleLogin = () => {
(loginFormRef.value as any).validate((errors: any) => {
if (!errors) {
// ....
})
}
return {
loginForm,
loginRules,
loginFormRef,
capsTooltip,
inputStyles,
}
},
})
</script>
<style lang="scss">
.login-container {
width: 100%;
min-height: 100%;
height: 100vh;
overflow: hidden;
background-color: #0F2133;
position: relative;
color: #ffffff;
.login-form {
width: 500px;
max-width: 100%;
padding: 25px 40px;
height: 360px;
margin: 10% auto;
overflow: hidden;
background-color: rgba(0, 0, 0, 0.22);
border: 2px solid;
-moz-border-image:url("src/assets/login/form-bg-border.png") stretch; /* Old Firefox */
-webkit-border-image:url("src/assets/login/form-bg-border.png") stretch; /* Safari and Chrome */
-o-border-image:url("src/assets/login/form-bg-border.png") stretch; /* Opera */
border-image:url("src/assets/login/form-bg-border.png") stretch;
-moz-box-shadow: 1px 1px 12px rgba(76, 206, 240, 0.5);
-webkit-box-shadow: 1px 1px 12px rgba(76, 206, 240, 0.5);
box-shadow: 1px 1px 12px rgba(76, 206, 240, 0.5);
border: 1px solid rgba(76, 206, 240, 0.5);
.form-title {
font-size: 36px;
color: #ffffff;
font-family: 'YouSheBiaoTiHei' !important;
text-align: left;
padding-bottom: 10px;
font-weight: normal;
}
}
.tips {
margin-bottom: 10px;
font-size: 14px;
color: #fff;
span {
margin-right: 16px;
}
.tips-text {
color: #ffffff;
}
}
.lang-select {
float: right;
margin-top: -24px;
cursor: pointer;
color: #ffffff;
font-size: 14px;
.lang-chang {
padding-right: 10px;
}
}
}
</style>