效果图
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.login-canvas {
top: 0;
position: absolute;
width: 100%;
height: 100%;
opacity: 0.45;
}
</style>
</head>
<body>
<canvas id="canvas" class="login-canvas" />
<script>
var canvas = window.document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var squids = new Array(30);
var bubbles = new Array(50);
var t = 0;
var colors = new Array(6);
var step = 0;
var colorIndices = [0, 1, 2, 3];
var gradientSpeed = 0.01;
var codeUrl = "";
var cookiePassword = "";
initCanvas();
moving();
function moving() {
setInterval(() => {
animate();
updateGradient();
}, 10);
}
function initCanvas() {
// create squids
for (let i = 0; i < squids.length; i++) {
const s = 20;
const e = 160;
squids[i] = {
re: s + Math.random() * e,
g: s + Math.random() * e,
b: s + Math.random() * e,
x: Math.random() * innerWidth,
y: Math.random() * innerHeight,
vx: (0.5 - Math.random()) / 4,
vy: 0.1 - Math.random(),
r: 10 + Math.random() * 40,
a: [],
};
}
// create bubbles
for (var j = 0; j < bubbles.length; j++) {
bubbles[j] = {
x: Math.random() * innerWidth,
y: Math.random() * innerHeight,
vx: 0.5 - Math.random(),
vy: -0.2 - Math.random(),
o: 0.05 + Math.random() * 0.1,
r: 3 + Math.random() * 20,
};
}
colors[0] = [62, 35, 255];
colors[1] = [60, 255, 60];
colors[2] = [255, 35, 98];
colors[3] = [45, 175, 230];
colors[4] = [255, 0, 255];
colors[5] = [255, 128, 0];
}
function animate() {
t++;
canvas.width = innerWidth;
canvas.height = innerHeight;
// const that = this;
bubbles.forEach((b) => {
b.x += b.vx;
b.y += b.vy;
limitRang(b);
ctx.fillStyle = "rgba(255,255,255," + b.o + ")";
ctx.beginPath();
ctx.arc(b.x, b.y, b.r, Math.PI * 2, 0);
ctx.fill();
});
squids.forEach((d) => {
var w = Math.sin((t + d.r * 100) / 10);
d.x += d.vx * 4;
d.y -= w + 1;
d.y += d.vy;
limitRang(d);
var color1 = "rgba(" + d.re + "," + d.g + "," + d.b + ",0.4)";
var color2 = "rgba(" + d.re + "," + d.g + "," + d.b + ",0.2)";
ctx.fillStyle = color1;
ctx.beginPath();
ctx.arc(
d.x,
d.y,
d.r,
Math.PI + (-0.5 + d.vx) - w / 4,
0.5 + d.vx + w / 4
);
ctx.fill();
d.a.push({ x: d.x, y: d.y - d.r * 0.2 });
if (d.a.length > d.r * 3) d.a.splice(0, 1);
d.a.forEach((p, i) => {
ctx.fillStyle = color2;
ctx.fillRect(p.x, p.y, 2, 2);
if (i > d.a.length / 2) {
ctx.fillRect(p.x - d.r / 4, p.y, 2, 2);
ctx.fillRect(p.x + d.r / 4, p.y, 2, 2);
}
if (i > d.a.length / 3) {
ctx.fillRect(p.x + d.r / 10, p.y - 10, 2, 2);
ctx.fillRect(p.x - d.r / 10, p.y - 10, 2, 2);
}
});
});
}
function limitRang(d) {
if (d.x < -d.r) d.x = innerWidth + d.r;
if (d.x > innerWidth + d.r) d.x = -d.r;
if (d.y < -d.r) d.y = innerHeight + d.r;
if (d.y > innerHeight + d.r) d.y = -d.r;
}
function updateGradient() {
var c = colors[colorIndices[0]];
var d = colors[colorIndices[1]];
var e = colors[colorIndices[2]];
var f = colors[colorIndices[3]];
var k = 1 - step;
var l = Math.round(k * c[0] + step * d[0]);
var i = Math.round(k * c[1] + step * d[1]);
var a = Math.round(k * c[2] + step * d[2]);
var g = "rgb(" + l + "," + i + "," + a + ")";
var m = Math.round(k * e[0] + step * f[0]);
var j = Math.round(k * e[1] + step * f[1]);
var b = Math.round(k * e[2] + step * f[2]);
var h = "rgb(" + m + "," + j + "," + b + ")";
if (canvas) {
canvas.style.background =
"-webkit-gradient(linear, left top, right top, from(" +
g +
"), to(" +
h +
"))";
canvas.style.background =
"-moz-linear-gradient(left, " + g + " 0%, " + h + " 100%)";
}
step += gradientSpeed;
if (step >= 1) {
step %= 1;
colorIndices[0] = colorIndices[1];
colorIndices[2] = colorIndices[3];
colorIndices[1] =
(colorIndices[1] +
Math.floor(1 + Math.random() * (colors.length - 1))) %
colors.length;
colorIndices[3] =
(colorIndices[3] +
Math.floor(1 + Math.random() * (colors.length - 1))) %
colors.length;
}
}
</script>
</body>
</html>
vue写法,多余变量和样式可自行去除
<template>
<div class="login">
<canvas id="canvas" ref="canvas" class="login-canvas" />
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
class="login-form"
>
<h3 class="title">XXXX平台</h3>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
type="text"
auto-complete="off"
placeholder="账号"
>
<svg-icon
slot="prefix"
icon-class="user"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
auto-complete="off"
placeholder="密码"
@keyup.enter.native="handleLogin"
>
<svg-icon
slot="prefix"
icon-class="password"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
<el-form-item prop="code">
<el-input
v-model="loginForm.code"
auto-complete="off"
placeholder="验证码"
style="width: 63%"
@keyup.enter.native="handleLogin"
>
<svg-icon
slot="prefix"
icon-class="validCode"
class="el-input__icon input-icon"
/>
</el-input>
<div class="login-code">
<img :src="codeUrl" @click="getCode" />
</div>
</el-form-item>
<el-checkbox
v-model="loginForm.rememberMe"
style="margin:0px 0px 25px 0px;"
>记住密码</el-checkbox
>
<router-link target="_blank" class="box medium-a" to="/merchantReg"
>helloworld</router-link
>
<el-form-item style="width:100%;">
<el-button
:loading="loading"
size="medium"
type="primary"
style="width:100%;"
@click.native.prevent="handleLogin"
>
<span v-if="!loading">登 录</span>
<span v-else>登 录 中...</span>
</el-button>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<!-- <span>Copyright © 2020-2021 xxx版权所有.</span> -->
</div>
</div>
</template>
<style>
.box {
color: #f30;
animation: changeshadow 1s ease-in infinite;
-webkit-animation: changeshadow 1s linear infinite;
-moz-animation: changeshadow 1s linear infinite;
-ms-animation: changeshadow 1s linear infinite;
-o-animation: changeshadow 1s linear infinite;
}
.medium-a {
float: right;
border: 0;
font-size: 15px;
text-decoration: underline;
}
.medium-a:hover {
text-decoration: underline;
color: #1890ff;
}
</style>
<script>
import { getCodeImg } from '@/api/login';
import Cookies from 'js-cookie';
export default {
name: 'Login',
data() {
return {
ctx: {},
canvas: undefined,
squids: new Array(30),
bubbles: new Array(50),
t: 0,
colors: new Array(6),
step: 0,
colorIndices: [0, 1, 2, 3],
gradientSpeed: 0.01,
codeUrl: '',
cookiePassword: '',
loginForm: {
username: '',
password: '',
rememberMe: false,
code: '',
uuid: ''
},
loginRules: {
username: [
{ required: true, trigger: 'blur', message: '用户名不能为空' }
],
password: [
{ required: true, trigger: 'blur', message: '密码不能为空' }
],
code: [{ required: true, trigger: 'change', message: '验证码不能为空' }]
},
loading: false,
redirect: undefined
};
},
watch: {
$route: {
handler: function(route) {
this.redirect = route.query && route.query.redirect;
},
immediate: true
}
},
created() {
this.getCode();
this.getCookie();
},
mounted() {
this.canvas = window.document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
this.initCanvas();
this.moving();
},
methods: {
initCanvas() {
// create squids
for (let i = 0; i < this.squids.length; i++) {
const s = 20;
const e = 160;
this.squids[i] = {
re: s + Math.random() * e,
g: s + Math.random() * e,
b: s + Math.random() * e,
x: Math.random() * innerWidth,
y: Math.random() * innerHeight,
vx: (0.5 - Math.random()) / 4,
vy: 0.1 - Math.random(),
r: 10 + Math.random() * 40,
a: []
};
}
// create bubbles
for (var j = 0; j < this.bubbles.length; j++) {
this.bubbles[j] = {
x: Math.random() * innerWidth,
y: Math.random() * innerHeight,
vx: 0.5 - Math.random(),
vy: -0.2 - Math.random(),
o: 0.05 + Math.random() * 0.1,
r: 3 + Math.random() * 20
};
}
this.colors[0] = [62, 35, 255];
this.colors[1] = [60, 255, 60];
this.colors[2] = [255, 35, 98];
this.colors[3] = [45, 175, 230];
this.colors[4] = [255, 0, 255];
this.colors[5] = [255, 128, 0];
},
limitRang(d) {
if (d.x < -d.r) d.x = innerWidth + d.r;
if (d.x > innerWidth + d.r) d.x = -d.r;
if (d.y < -d.r) d.y = innerHeight + d.r;
if (d.y > innerHeight + d.r) d.y = -d.r;
},
async moving() {
setInterval(() => {
this.animate();
this.updateGradient();
}, 10);
},
animate() {
this.t++;
this.canvas.width = innerWidth;
this.canvas.height = innerHeight;
const that = this;
this.bubbles.forEach(function(b) {
b.x += b.vx;
b.y += b.vy;
that.limitRang(b);
that.ctx.fillStyle = 'rgba(255,255,255,' + b.o + ')';
that.ctx.beginPath();
that.ctx.arc(b.x, b.y, b.r, Math.PI * 2, 0);
that.ctx.fill();
});
this.squids.forEach(function(d) {
var w = Math.sin((that.t + d.r * 100) / 10);
d.x += d.vx * 4;
d.y -= w + 1;
d.y += d.vy;
that.limitRang(d);
var color1 = 'rgba(' + d.re + ',' + d.g + ',' + d.b + ',0.4)';
var color2 = 'rgba(' + d.re + ',' + d.g + ',' + d.b + ',0.2)';
that.ctx.fillStyle = color1;
that.ctx.beginPath();
that.ctx.arc(
d.x,
d.y,
d.r,
Math.PI + (-0.5 + d.vx) - w / 4,
0.5 + d.vx + w / 4
);
that.ctx.fill();
d.a.push({ x: d.x, y: d.y - d.r * 0.2 });
if (d.a.length > d.r * 3) d.a.splice(0, 1);
d.a.forEach(function(p, i) {
that.ctx.fillStyle = color2;
that.ctx.fillRect(p.x, p.y, 2, 2);
if (i > d.a.length / 2) {
that.ctx.fillRect(p.x - d.r / 4, p.y, 2, 2);
that.ctx.fillRect(p.x + d.r / 4, p.y, 2, 2);
}
if (i > d.a.length / 3) {
that.ctx.fillRect(p.x + d.r / 10, p.y - 10, 2, 2);
that.ctx.fillRect(p.x - d.r / 10, p.y - 10, 2, 2);
}
});
});
},
updateGradient() {
var c = this.colors[this.colorIndices[0]];
var d = this.colors[this.colorIndices[1]];
var e = this.colors[this.colorIndices[2]];
var f = this.colors[this.colorIndices[3]];
var k = 1 - this.step;
var l = Math.round(k * c[0] + this.step * d[0]);
var i = Math.round(k * c[1] + this.step * d[1]);
var a = Math.round(k * c[2] + this.step * d[2]);
var g = 'rgb(' + l + ',' + i + ',' + a + ')';
var m = Math.round(k * e[0] + this.step * f[0]);
var j = Math.round(k * e[1] + this.step * f[1]);
var b = Math.round(k * e[2] + this.step * f[2]);
var h = 'rgb(' + m + ',' + j + ',' + b + ')';
if (this.canvas) {
this.canvas.style.background =
'-webkit-gradient(linear, left top, right top, from(' +
g +
'), to(' +
h +
'))';
this.canvas.style.background =
'-moz-linear-gradient(left, ' + g + ' 0%, ' + h + ' 100%)';
}
this.step += this.gradientSpeed;
if (this.step >= 1) {
this.step %= 1;
this.colorIndices[0] = this.colorIndices[1];
this.colorIndices[2] = this.colorIndices[3];
this.colorIndices[1] =
(this.colorIndices[1] +
Math.floor(1 + Math.random() * (this.colors.length - 1))) %
this.colors.length;
this.colorIndices[3] =
(this.colorIndices[3] +
Math.floor(1 + Math.random() * (this.colors.length - 1))) %
this.colors.length;
}
},
getCode() {
getCodeImg().then(res => {
this.codeUrl = 'data:image/gif;base64,' + res.data.data.img;
this.loginForm.uuid = res.data.data.uuid;
});
},
getCookie() {
const username = Cookies.get('username');
const password = Cookies.get('password');
const rememberMe = Cookies.get('rememberMe');
this.loginForm = {
username: username === undefined ? this.loginForm.username : username,
password: password === undefined ? this.loginForm.password : password,
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
};
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true;
if (this.loginForm.rememberMe) {
Cookies.set('username', this.loginForm.username, { expires: 30 });
Cookies.set('password', this.loginForm.password, { expires: 30 });
Cookies.set('rememberMe', this.loginForm.rememberMe, {
expires: 30
});
} else {
Cookies.remove('username');
Cookies.remove('password');
Cookies.remove('rememberMe');
}
this.$store
.dispatch('LoginByUsername', this.loginForm)
.then(isImproved => {
this.loading = false;
if (isImproved === true) {
this.$router.push({ path: this.redirect || '/' });
} else {
// 品牌信息为完善 先完善信息
this.$router.push({ path: '/brandInfo' || '/' });
}
})
.catch(response => {
this.$notify.error({
title: '失败',
message: response.data.errmsg
});
this.loading = false;
this.getCode();
});
}
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
.login-canvas {
top: 0;
position: absolute;
width: 100%;
height: 100%;
opacity: 0.45;
}
.login {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background-size: cover;
}
.title {
margin: 0px auto 30px auto;
text-align: center;
color: #707070;
}
.login-form {
z-index: 999;
border-radius: 6px;
background: #ffffff;
opacity: 0.65;
width: 400px;
padding: 25px 25px 5px 25px;
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 38px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
z-index: 999;
}
</style>