滑动验证(login.vue 和 hd.vue 相关)
login.vue
<template>
<body id="poster">
<el-form class="login-container" label-position="left"
label-width="0px" :rules="loginFormRules" :model="loginForm">
<h3 class="login_title">系统登录</h3>
<el-form-item prop="username" >
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号" ></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="密码" ></el-input>
</el-form-item>
<el-form-item prop="phone">
<el-input style="width:70% " placeholder="手机号码" v-model="loginForm.phone" ></el-input>
<!-- dialog = true, bbc = true-->
<el-button class="send" @click="checkV">发送短信</el-button>
<!-- <el-button class="el-icon-refresh-right" @click="refreshPhone"></el-button>-->
</el-form-item>
<el-form-item style="width: 100%">
<el-button type="primary" style="width: 100%;background: #505458;border: none" v-on:click="login">登录</el-button>
</el-form-item>
</el-form>
<el-dialog v-if="bbc" title="请完成安全验证" :visible.sync="dialog" width="350px" append-to-body center destroy-on-close>
<Hd v-on:judgment="updateBool"></Hd>
</el-dialog>
</body>
</template>
<script>
import Hd from './Hd'
export default {
components:{ Hd },
name: 'Login',
data () {
return {
loginFormRules: {
username: [{ required: true, message: '请输入用户名', trigger: 'blur'}],
password: [{ required: true, message: '请输入密码', trigger: 'blur'}],
phone: [{
pattern: /^(13[0-9]|14[01456879]|15[0-3|5-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
required: true,
message: '请输入正确的手机号',
trigger: 'blur'
}]
},
loginForm: {
username: '',
password: '',
phone: ''
},
responseResult: [],
dialog:false,
bbc:true,
// this.state.isVerified
// isVerified:this.$store.state.isVerified,
}
},
methods: {
checkV(){
var check = /^(13[0-9]|14[01456879]|15[0-3|5-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
if(!check.test(this.loginForm.phone)){
this.$message.error('手机号码格式不正确, 请重新输入');
}else {
this.dialog = true
this.bbc = true
}
},
updateBool(val){
this.bbc = val
},
refreshPhone(){
this.bbc = true
},
login () {
this.$axios
.post('/login', {
username: this.loginForm.username,
password: this.loginForm.password
})
.then(successResponse => {
if (successResponse.data.code === 200) {
this.$router.replace({path: '/index'})
}
})
.catch(failResponse => {
})
},
}
}
</script>
<style scoped>
/*.send{*/
/* width:;*/
/*}*/
.login-container {
border-radius: 15px;
background-clip: padding-box;
margin: 90px auto;
width: 350px;
padding: 35px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.login_title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
#poster {
background:url("../assets/Lighthouse.jpg") no-repeat;
/*background-position: center;*/
height: 100%;
width: 100%;
background-size: cover;
position: fixed;
top:0px;
left:0px;
}
html,body{
margin: 0px;
padding: 0px;
}
</style>
hd.vue
<template>
<div class="verification" ref="verification">
<!-- 画布部分 -->
<canvas ref="slideVerify" class="slide-img"></canvas>
<div style="display:none">
<img ref="imgs" :src="imgList[imgIndex]"/>
</div>
<!-- 下面滑块部分 -->
<div class="slide-wrapper bg-start">
<!-- 滑块 -->
<div class="btn" ref="btn" @mousedown="mouseDown" @mouseup="mouseUp"></div>
<p class="text" ref="text">{{content}}</p>
<div class="bg" ref="bg"></div>
</div>
<!-- 刷新按钮 -->
<!-- <button class="refresh" @click="refresh"></button>-->
</div>
</template>
<script>
export default {
name:'Hd',
data () {
return {
imgIndex: 0,
blockCanvas: null,
imgList: [require('../assets/1.jpg'),
require('../assets/2.jpg'),
require('../assets/3.jpg'),
require('../assets/4.jpg') ],
content: '滑动滑块',
isDown: false, // 鼠标是否按下
btnX: 0,
imgX: 0,
// dialogNotVisible: false
}
},
mounted () {
this.imgIndex = this.randomNumber(0, 4)
document.addEventListener('mousemove', this.mouseMove)
this.imageCanvas()
},
methods: {
// 生成随机数字
randomNumber (min, max) {
return Math.floor(Math.random() * (max - min) + min)
},
// 鼠标按下时
mouseDown (e) {
this.isDown = true
this.btnX = e.clientX - this.$refs.btn.offsetLeft
},
// 鼠标滑动时
mouseMove (e) {
// 滑块左端向右边移动的距离
let moveX = e.clientX - this.btnX
if (this.isDown) {
// 滑块滑动时不能超过的距离
if (this.$refs.btn.offsetLeft <= 259 && this.$refs.btn.offsetLeft >= 0) {
this.$refs.btn.style.left = `${moveX}px`
this.blockCanvas.style.left = `${moveX - this.imgX}px`
this.$refs.bg.style.width = `${moveX}px`
}
}
},
// 滑动中松开
mouseUp () {
let leftX = this.$refs.btn.offsetLeft
// 方块的位置和缺失的位置重合允许左右2px的误差
if (this.imgX >= leftX - 1 && this.imgX <= leftX + 1) {
// 滑动成功时的逻辑
this.$refs.btn.classList.add('btnsuccess')
this.isDown = false
this.$message({
message: '验证成功',
type: 'success'
})
this.$message({
message: '验证码已发送',
type: 'success',
offset:60
});
// this.$store.state.isVerified = false
this.$emit('judgment',false)
//成功向后端拿验证码
// this.axios.get('./verifications').then(resp =>{
// this.$store.state.verifications = resp.data
// })
}
// 如果滑动失败,滑块自动回到左边
if (this.isDown) {
this.$refs.btn.style.left = 0
this.blockCanvas.style.left = `-${this.imgX}px`
this.$refs.bg.style.width = 0
}
// 开关原则
this.isDown = false
},
// 画图
imageCanvas () {
this.blockCanvas = this.blockCanvas ? this.blockCanvas.remove() : null
this.blockCanvas = this.createCanvas(300, 150)
this.$refs.verification.insertBefore(this.blockCanvas, this.$refs.slideVerify)
let x = this.randomNumber(60, 200)
let y = 40
this.imgX = x
let c = this.$refs.slideVerify
let bg = c.getContext('2d')
let img = this.$refs.imgs
let bk = this.blockCanvas.getContext('2d')
// 在两块画布上都放上相同的图片
img.onload = () => {
bg.drawImage(img, 0, 0)
bk.drawImage(img, 0, 0)
}
this.drawBlock(bg, x, y, 'fill')
this.drawBlock(bk, x, y, 'clip')
},
// 画抠出来的方块
drawBlock (ctx, x, y, type) {
ctx.beginPath()
ctx.moveTo(x, y)
ctx.arc(x + 42 / 2, y - 9 + 2, 9, 0.72 * Math.PI, 2.26 * Math.PI)
ctx.lineTo(x + 42, y)
ctx.arc(x + 42 + 9 - 2, y + 42 / 2, 9, 1.21 * Math.PI, 2.78 * Math.PI)
ctx.lineTo(x + 42, y + 42)
ctx.lineTo(x, y + 42)
ctx.arc(x + 9 - 2, y + 42 / 2, 9 + 0.4, 2.76 * Math.PI, 1.24 * Math.PI, true)
ctx.lineTo(x, y)
ctx.lineWidth = 2
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'
ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'
ctx.stroke()
ctx[type]()
ctx.globalCompositeOperation = 'destination-over'
// 解决进入页面时不自动扣拼图样式的麻烦(有时需要鼠标点击后才会出现裁剪后的拼图)
this.blockCanvas.style.left = `-${x}px`
},
// 刷新
refresh () {
// 有时会出现点击刷新,randomNumber返回的数字和上次存储的一样,画布清空后但是填充时没有改变;所以当一样时,不会执行刷新操作
if (this.imgIndex == this.randomNumber(0, 4)) {
return false
}
this.clean()
this.$refs.btn.style.left = 0
this.$refs.bg.style.width = 0
this.$refs.btn.classList.remove('btnsuccess')
this.isDown = false // 鼠标是否按下
this.btnX = 0 // 鼠标点击的水平位置与滑块移动水平位置的差
this.imgX = 0
this.imageCanvas('restore')
this.imgIndex = this.randomNumber(0, 4)
},
// 清空canvas
clean () {
let cxt2 = this.$refs.slideVerify.getContext('2d')
cxt2.clearRect(0, 0, 300, 150)
},
// 新建canvas
createCanvas (width, height) {
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
canvas.style.position = 'absolute'
return canvas
}
}
}
</script>
<style scoped>
.verification {
position: relative;
width: 300px;
margin: 0 auto;
}
.slide-wrapper {
position: relative;
width: 300px;
height: 40px;
}
.bg-start {
background: cadetblue;
}
.bg {
position: absolute;
height: 40px;
background: #ccc;
}
.text {
position: absolute;
width: 100%;
height: 40px;
text-align: center;
line-height: 40px;
margin: 0;
/* z-index: 1; */
}
.text-success {
color: white;
z-index: 2;
}
.btn {
position: absolute;
width: 40px;
height: 40px;
z-index: 1;
border-radius: 5px;
background: rgb(143, 145, 148);
text-align: center;
font-size: 24px;
color: white;
box-shadow: 0 0 1px 1px #fff;
background: #fff no-repeat center url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0ZDhlNWY5My05NmI0LTRlNWQtOGFjYi03ZTY4OGYyMTU2ZTYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NTEyNTVEMURGMkVFMTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NTEyNTVEMUNGMkVFMTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2MTc5NzNmZS02OTQxLTQyOTYtYTIwNi02NDI2YTNkOWU5YmUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NGQ4ZTVmOTMtOTZiNC00ZTVkLThhY2ItN2U2ODhmMjE1NmU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+YiRG4AAAALFJREFUeNpi/P//PwMlgImBQkA9A+bOnfsIiBOxKcInh+yCaCDuByoswaIOpxwjciACFegBqZ1AvBSIS5OTk/8TkmNEjwWgQiUgtQuIjwAxUF3yX3xyGIEIFLwHpKyAWB+I1xGSwxULIGf9A7mQkBwTlhBXAFLHgPgqEAcTkmNCU6AL9d8WII4HOvk3ITkWJAXWUMlOoGQHmsE45ViQ2KuBuASoYC4Wf+OUYxz6mQkgwAAN9mIrUReCXgAAAABJRU5ErkJggg==");
}
.btnsuccess {
background: #fff no-repeat center url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo0ZDhlNWY5My05NmI0LTRlNWQtOGFjYi03ZTY4OGYyMTU2ZTYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDlBRDI3NjVGMkQ2MTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDlBRDI3NjRGMkQ2MTFFNEI5NDBCMjQ2M0ExMDQ1OUYiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDphNWEzMWNhMC1hYmViLTQxNWEtYTEwZS04Y2U5NzRlN2Q4YTEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NGQ4ZTVmOTMtOTZiNC00ZTVkLThhY2ItN2U2ODhmMjE1NmU2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+k+sHwwAAASZJREFUeNpi/P//PwMyKD8uZw+kUoDYEYgloMIvgHg/EM/ptHx0EFk9I8wAoEZ+IDUPiIMY8IN1QJwENOgj3ACo5gNAbMBAHLgAxA4gQ5igAnNJ0MwAVTsX7IKyY7L2UNuJAf+AmAmJ78AEDTBiwGYg5gbifCSxFCZoaBMCy4A4GOjnH0D6DpK4IxNSVIHAfSDOAeLraJrjgJp/AwPbHMhejiQnwYRmUzNQ4VQgDQqXK0ia/0I17wJiPmQNTNBEAgMlQIWiQA2vgWw7QppBekGxsAjIiEUSBNnsBDWEAY9mEFgMMgBk00E0iZtA7AHEctDQ58MRuA6wlLgGFMoMpIG1QFeGwAIxGZo8GUhIysmwQGSAZgwHaEZhICIzOaBkJkqyM0CAAQDGx279Jf50AAAAAABJRU5ErkJggg==");
}
.refresh {
cursor: pointer;
width: 20px;
height: 20px;
position: absolute;
z-index: 1;
top: 0;
right: 10px;
opacity: .6;
background: url('../assets/Desert.jpg') no-repeat;
background-size: cover;
}
</style>
验证码验证(login2.vue 和 SIdentify2.vue相关)
login2.vue
<template>
<body id="poster">
<el-form class="login-container" label-position="left"
label-width="0px" :rules="loginFormRules" :model="loginForm">
<h3 class="login_title">系统登录</h3>
<el-form-item prop="username" >
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号" ></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="密码" ></el-input>
</el-form-item>
<el-form-item prop="inputCode">
<el-input type="text" v-model="inputCode" auto-complete="off" placeholder="验证码" style="width: 55%;margin-left: -158px"></el-input>
<SIdentify2 v-bind:identifyCode="identifyCode" @click.native="refreshCode" style="background-color: #ebffdd;margin: -40px 0 0 200px"></SIdentify2>
<el-row style="width:100px;margin-top: -40px;margin-left: 85px;height: 40px">{{tip}}</el-row>
</el-form-item>
<el-form-item prop="phone">
<el-input style="width:70%;margin-left: -105px" placeholder="手机号码" v-model="loginForm.phone" ></el-input>
<el-tooltip effect="dark" content="请先完成上方图形验证" placement="top-start" :disabled="IsVer">
<el-row style="width: 40px;margin-top: -40px;margin-left: 250px">
<el-button @click="checkV2" :disabled="!IsVer">发送短信</el-button>
</el-row>
</el-tooltip>
</el-form-item>
<el-form-item style="width: 100%">
<el-button type="primary" style="width: 100%;background: #505458;border: none" v-on:click="login">登录</el-button>
</el-form-item>
</el-form>
</body>
</template>
<script>
import SIdentify2 from './SIdentify2'
export default {
components: {SIdentify2},
name: 'Login2',
data () {
return {
identifyCode:this.refreshCode(),
inputCode: '',
loginFormRules: {
username: [{required: true, message: '请输入用户名', trigger: 'blur'}],
password: [{required: true, message: '请输入密码', trigger: 'blur'}],
},
loginForm: {
username: '',
password: '',
},
responseResult: [],
dialog: false,
bbc: true,
IsVer: false,
tip: '',
}
},
watch: {
inputCode: function (val) {
this.checkVer(val)
this.debouncedCheckVer()
}
},
created: function () {
this.debouncedCheckVer = _.debounce(this.checkVer, 500)
},
//预加载
mounted:function(){
this.refreshCode()
},
methods: {
refreshCode() {
var code = 'abcdefghijklm0123456789nopqrstuvwxyz'
var num = Math.round(Math.random()*18)
var num1 = Math.round(Math.random()*24)
var num2 = Math.round(Math.random()*30)
var num3 = Math.round(Math.random()*36)
this.identifyCode = (code.charAt(num)+code.charAt(num1)+code.charAt(num2)+code.charAt(num3))
this.IsVer = false
this.tip = ''
},
checkV2 () {
var check = /^(13[0-9]|14[01456879]|15[0-3|5-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
if (!check.test(this.loginForm.phone)) {
this.$message.error('手机号码格式不正确, 请重新输入');
} else {
this.$message({
message: '验证码已发送',
type: 'success'
})
}
},
//验证码校验
checkVer: function () {
if (this.inputCode === this.identifyCode) {
this.tip = '验证码正确'
return this.IsVer = (this.inputCode === this.identifyCode)
} else {
this.tip = '验证码输入错误'
}
},
updateBool (val) {
this.bbc = val
},
login () {
this.$axios
.post('/login', {
username: this.loginForm.username,
password: this.loginForm.password
})
.then(successResponse => {
if (successResponse.data.code === 200) {
this.$router.replace({path: '/index'})
}
})
.catch(failResponse => {
})
},
}
}
</script>
<style scoped>
/*.send{*/
/* width:;*/
/*}*/
.login-container {
border-radius: 15px;
background-clip: padding-box;
margin: 90px auto;
width: 350px;
padding: 35px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.login_title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
#poster {
background:url("../assets/Lighthouse.jpg") no-repeat;
/*background-position: center;*/
height: 100%;
width: 100%;
background-size: cover;
position: fixed;
top:0px;
left:0px;
}
html,body{
margin: 0px;
padding: 0px;
}
</style>
SIdentify2.vue
<template>
<div class="s-canvas">
<canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas>
</div>
</template>
<script>
export default {
name: 'SIdentify2',
props: {
identifyCode: {
type: String,
default: '45df'
},
fontSizeMin: {
type: Number,
default: 25
},
fontSizeMax: {
type: Number,
default: 30
},
backgroundColorMin: {
type: Number,
default: 255
},
backgroundColorMax: {
type: Number,
default: 255
},
colorMin: {
type: Number,
default: 0
},
colorMax: {
type: Number,
default: 160
},
lineColorMin: {
type: Number,
default: 100
},
lineColorMax: {
type: Number,
default: 255
},
dotColorMin: {
type: Number,
default: 0
},
dotColorMax: {
type: Number,
default: 255
},
contentWidth: {
type: Number,
default: 132
},
contentHeight: {
type: Number,
default: 31
}
},
methods: {
// 生成一个随机数
randomNum(min, max) {
return Math.floor(Math.random() * (max - min) + min)
},
// 生成一个随机的颜色
randomColor(min, max) {
let r = this.randomNum(min, max)
let g = this.randomNum(min, max)
let b = this.randomNum(min, max)
return 'rgb(' + r + ',' + g + ',' + b + ')'
},
drawPic() {
let canvas = document.getElementById('s-canvas')
let ctx = canvas.getContext('2d')
ctx.textBaseline = 'bottom'
// 绘制背景
ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
// 绘制文字
for (let i = 0; i < this.identifyCode.length; i++) {
this.drawText(ctx, this.identifyCode[i], i)
}
this.drawLine(ctx)
this.drawDot(ctx)
},
drawText(ctx, txt, i) {
ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei'
let x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1))
let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
var deg = this.randomNum(-45, 45)
// 修改坐标原点和旋转角度
ctx.translate(x, y)
ctx.rotate(deg * Math.PI / 180)
ctx.fillText(txt, 0, 0)
// 恢复坐标原点和旋转角度
ctx.rotate(-deg * Math.PI / 180)
ctx.translate(-x, -y)
},
drawLine(ctx) {
// 绘制干扰线
for (let i = 0; i < 5; i++) {
ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
ctx.beginPath()
ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.stroke()
}
},
drawDot(ctx) {
// 绘制干扰点
for (let i = 0; i < 80; i++) {
ctx.fillStyle = this.randomColor(0, 255)
ctx.beginPath()
ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
ctx.fill()
}
}
},
watch: {
identifyCode() {
this.drawPic()
}
},
mounted() {
this.drawPic()
}
}
</script>
<style scoped>
.s-canvas {
height: 38px;
}
.s-canvas canvas{
margin-top: 1px;
margin-left: 8px;
}
</style>
尾部