vue项目 - 基于canvas的数字验证码

  1 <template>
  2     <el-form class="login-form" status-icon :rules="loginRules" ref="loginForm" :model="loginForm" label-width="80px">
  3         <el-form-item prop="username" label="用户:">
  4             <el-input v-model="loginForm.username" auto-complete="off" placeholder="请输入用户名" size="medium" @keyup.enter.native="submitForm('loginForm')"></el-input>
  5         </el-form-item>
  6         <el-form-item prop="password" label="密码:">
  7             <el-input :type="passwordType" v-model="loginForm.password" auto-complete="off" placeholder="请输入密码" size="medium">
  8                 <i class="el-icon-view el-input__icon" slot="suffix" @click="showPassword"></i>
  9             </el-input>
 10         </el-form-item>
 11         <el-form-item prop="code">
 12             <el-row :span="24">
 13                 <el-col :span="12">
 14                     <el-input v-model="loginForm.code" auto-complete="off" placeholder="请输入验证码" size="" @keyup.enter.native="submitForm('loginForm')"></el-input>
 15                 </el-col>
 16                 <el-col :span="12">
 17                     <div class="login-code" @click="refreshCode">
 18                         <!--验证码组件-->
 19                         <s-identify :identifyCode="identifyCode"></s-identify>
 20                     </div>
 21                 </el-col>
 22             </el-row>
 23         </el-form-item>
 24         <el-form-item>
 25             <el-button type="primary" @click="submitForm('loginForm')" class="login-submit">登录</el-button>
 26         </el-form-item>
 27     </el-form>
 28 </template>
 29 
 30 <script>
 31 import { isLoginName } from '@/util/validate'
 32 import SIdentify from './sidentify'
 33 export default {
 34     name: 'userLogin',
 35     components: { SIdentify },
 36     data() {
 37         const validateCode = (rule, value, callback) => {
 38             if (this.identifyCode !== value) {
 39                 this.loginForm.code = ''
 40                 this.refreshCode()
 41                 callback(new Error('请输入正确的验证码'))
 42             } else {
 43                 callback()
 44             }
 45         }
 46         return {
 47             isDebugLogin: false,
 48             loginForm: {
 49                 username: '',
 50                 password: '',
 51                 code: ''
 52             },
 53             identifyCodes: '1234567890',
 54             identifyCode: '',
 55             loginRules: {
 56                 username: [
 57                     { required: true, trigger: 'blur', message: '请输入用户名' },
 58                     { min: 1, max: 200, message: '长度在1-200字符之间', trigger: 'blur' },
 59                     { validator: isLoginName, trigger: 'blur' }
 60                 ],
 61                 password: [
 62                     { required: true, message: '请输入密码', trigger: 'blur' },
 63                     { min: 1, max: 100, message: '长度在1-100字符之间', trigger: 'blur' }
 64                 ],
 65                 code: [
 66                     { required: true, message: '请输入验证码', trigger: 'blur' },
 67                     { validator: validateCode, trigger: 'blur' }
 68                 ]
 69             },
 70             passwordType: 'password'
 71         }
 72     },
 73     watch: {
 74         isDebugLogin(v) {
 75             if (v) {
 76                 this.loginForm.password = '123'
 77                 this.refreshCode()
 78             }
 79         },
 80         identifyCode(v) {
 81             this.isDebugLogin && (this.loginForm.code = v)
 82         }
 83     },
 84     methods: {
 85         randomNum(min, max) {
 86             return Math.floor(Math.random() * (max - min) + min)
 87         },
 88         refreshCode() {
 89             this.identifyCode = ''
 90             this.makeCode(this.identifyCodes, 4)
 91         },
 92         makeCode(o, l) {
 93             for (let i = 0; i < l; i++) {
 94                 this.identifyCode += this.identifyCodes[
 95                     this.randomNum(0, this.identifyCodes.length)
 96                 ]
 97             }
 98         },
 99         showPassword() {
100             this.passwordType === ''
101                 ? (this.passwordType = 'password')
102                 : (this.passwordType = '')
103         },
104         submitForm(formName) {
105             this.$refs[formName].validate(valid => {
106                 if (valid) {
107                     console.log('授权成功')
108                 } else {
109                     return false
110                 }
111             })
112         }
113     },
114     created() {
115         this.refreshCode()
116     }
117 }
118 </script>
119 
120 <style>
121 .login-form .el-input__inner {
122     height: 40px;
123     line-height: 40px;
124 }
125 .login-form .el-form-item__label {
126     line-height: 40px;
127 }
128 .login-submit.el-button {
129     padding: 12px 20px;
130 }
131 </style>
132 <style lang="scss" scoped>
133 .login-form {
134     padding: 10px 0;
135     .el-form-item + .el-form-item {
136         margin-top: 30px;
137     }
138     .login-code {
139         cursor: pointer;
140         .login-code-img {
141             width: 100px;
142             height: 38px;
143             background-color: #eee;
144             border: 1px solid #f0f0f0;
145             color: #333;
146             font-size: 18px;
147             font-weight: bold;
148             letter-spacing: 2px;
149             text-indent: 2px;
150             text-align: center;
151         }
152     }
153     .login-submit {
154         width: 100%;
155         border: 0;
156     }
157 }
158 </style>

验证码组件SIdentify

  1 <template>
  2     <div class="s-canvas">
  3         <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas>
  4     </div>
  5 </template>
  6 <script>
  7 export default {
  8     name: 'SIdentify',
  9     props: {
 10         identifyCode: {
 11             type: String,
 12             default: '1234'
 13         },
 14         fontSizeMin: {
 15             type: Number,
 16             default: 16
 17         },
 18         fontSizeMax: {
 19             type: Number,
 20             default: 40
 21         },
 22         backgroundColorMin: {
 23             type: Number,
 24             default: 180
 25         },
 26         backgroundColorMax: {
 27             type: Number,
 28             default: 240
 29         },
 30         colorMin: {
 31             type: Number,
 32             default: 50
 33         },
 34         colorMax: {
 35             type: Number,
 36             default: 160
 37         },
 38         lineColorMin: {
 39             type: Number,
 40             default: 40
 41         },
 42         lineColorMax: {
 43             type: Number,
 44             default: 180
 45         },
 46         dotColorMin: {
 47             type: Number,
 48             default: 0
 49         },
 50         dotColorMax: {
 51             type: Number,
 52             default: 255
 53         },
 54         contentWidth: {
 55             type: Number,
 56             default: 112
 57         },
 58         contentHeight: {
 59             type: Number,
 60             default: 38
 61         }
 62     },
 63     methods: {
 64         // 生成一个随机数
 65         randomNum(min, max) {
 66             return Math.floor(Math.random() * (max - min) + min)
 67         },
 68         // 生成一个随机的颜色
 69         randomColor(min, max) {
 70             let r = this.randomNum(min, max)
 71             let g = this.randomNum(min, max)
 72             let b = this.randomNum(min, max)
 73             return 'rgb(' + r + ',' + g + ',' + b + ')'
 74         },
 75         drawPic() {
 76             let canvas = document.getElementById('s-canvas')
 77             let ctx = canvas.getContext('2d')
 78             ctx.textBaseline = 'bottom'
 79             // 绘制背景
 80             ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
 81             ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
 82             // 绘制文字
 83             for (let i = 0; i < this.identifyCode.length; i++) {
 84                 this.drawText(ctx, this.identifyCode[i], i)
 85             }
 86             this.drawLine(ctx)
 87             this.drawDot(ctx)
 88         },
 89         drawText(ctx, txt, i) {
 90             ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
 91             ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei'
 92             let x = (i + 1) * (this.contentWidth / (this.identifyCode.length + 1))
 93             let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
 94             var deg = this.randomNum(-45, 45)
 95             // 修改坐标原点和旋转角度
 96             ctx.translate(x, y)
 97             ctx.rotate(deg * Math.PI / 180)
 98             ctx.fillText(txt, 0, 0)
 99             // 恢复坐标原点和旋转角度
100             ctx.rotate(-deg * Math.PI / 180)
101             ctx.translate(-x, -y)
102         },
103         drawLine(ctx) {
104             // 绘制干扰线
105             for (let i = 0; i < 8; i++) {
106                 ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
107                 ctx.beginPath()
108                 ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
109                 ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
110                 ctx.stroke()
111             }
112         },
113         drawDot(ctx) {
114             // 绘制干扰点
115             for (let i = 0; i < 100; i++) {
116                 ctx.fillStyle = this.randomColor(0, 255)
117                 ctx.beginPath()
118                 ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
119                 ctx.fill()
120             }
121         }
122     },
123     watch: {
124         identifyCode() {
125             this.drawPic()
126         }
127     },
128     mounted() {
129         this.drawPic()
130     }
131 }
132 </script>
133 <style lang="scss" scoped>
134 .s-canvas {
135     height: 38px;
136     canvas {
137         margin-top: 1px;
138         margin-left: 8px;
139     }
140 }
141 </style>

 采用;https://www.jianshu.com/p/99c6e2f3e457

posted @ 2020-08-17 15:20  肥龙啃锅盔  阅读(200)  评论(0编辑  收藏  举报