用canvas实现验证码的绘制

login.vue主文件

  1 <template>
  2     <div class="login-wrapper">
  3         <img src="../../assets/images/logo.png" class="logo" />
  4         <div class="login-box">
  5             <ul class="login-title-list">
  6                 <li class="login-title-item active">账号登录</li>
  7                 <li class="login-title-item">二维码登录</li>
  8             </ul>
  9             <div class="login-body">
 10                 <el-form
 11                     autocomplete="off"
 12                     :model="loginForm"
 13                     :rules="loginRules"
 14                     ref="loginForm"
 15                     label-position="left"
 16                     label-width="0px"
 17                     class="card-box login-form"
 18                 >
 19                     <el-form-item prop="username">
 20                         <svg-icon icon-class="user" class="icon-svg" />
 21                         <el-input placeholder="请输入邮箱" type="text" v-model="loginForm.username" />
 22                     </el-form-item>
 23                     <el-form-item prop="password">
 24                         <svg-icon icon-class="password" class="icon-svg" />
 25                         <el-input placeholder="请输入密码" type="password" v-model="loginForm.password" />
 26                     </el-form-item>
 27                     <el-form-item prop="verifycode" class="form-item-captcha">
 28                         <el-input placeholder="请输入验证码" type="captcha" v-model="loginForm.verifycode" />
 29                         <span @click="refreshCode" class="yzm">
 30                             <s-identify :identifyCode="identifyCode"></s-identify>
 31                         </span>
 32                     </el-form-item>
 33                     <el-form-item class="login-wrap">
 34                         <el-button type="primary" :loading="loading" style="width:100%;" @click="handleLogin"
 35                             >登录</el-button
 36                         >
 37                     </el-form-item>
 38                 </el-form>
 39                 <div class="login-footer">
 40                     <el-checkbox v-model="rememberPassword">记住密码</el-checkbox>
 41                     <span>忘记密码?</span>
 42                 </div>
 43             </div>
 44         </div>
 45     </div>
 46 </template>
 47 
 48 <script>
 49 import { mapState } from 'vuex';
 50 import SIdentify from './identify.vue';
 51 import { encryptDes } from '../../assets/js/utils/des';
 52 
 53 export default {
 54     name: 'Login',
 55     components: {
 56         SIdentify
 57     },
 58     data() {
 59         // 验证码自定义验证规则
 60         const validateVerifycode = (rule, value, callback) => {
 61             let newVal = value.toLowerCase();
 62             let identifyStr = this.identifyCode.toLowerCase();
 63             if (newVal === '') {
 64                 callback(new Error('请输入验证码'));
 65             } else if (newVal !== identifyStr) {
 66                 callback(new Error('验证码不正确!'));
 67             } else {
 68                 callback();
 69             }
 70         };
 71         return {
 72             loginForm: {
 73                 username: '',
 74                 password: '',
 75                 verifycode: ''
 76             },
 77             identifyCodes: '1234567890ABCDEFGHIGKLMNOPQRSTUVWXYZ',
 78             identifyCode: '',
 79             rememberPassword: true,
 80             loading: false,
 81             loginRules: {
 82                 username: [{ required: true, trigger: 'blur', message: '账号不能为空' }],
 83                 password: [
 84                     { required: true, message: '请输入密码', trigger: 'blur' },
 85                     { min: 6, message: '密码长度最少为6位', trigger: 'blur' }
 86                 ],
 87                 verifycode: [{ required: true, trigger: 'blur', validator: validateVerifycode }]
 88             }
 89         };
 90     },
 91     methods: {
 92         // 生成随机数
 93         randomNum(min, max) {
 94             return Math.floor(Math.random() * (max - min) + min);
 95         },
 96         // 切换验证码
 97         refreshCode() {
 98             this.identifyCode = '';
 99             this.makeCode(this.identifyCodes, 4);
100         },
101         // 生成四位随机验证码
102         makeCode(o, l) {
103             for (let i = 0; i < l; i++) {
104                 this.identifyCode += this.identifyCodes[this.randomNum(0, this.identifyCodes.length)];
105             }
106         },
107         handleLogin() {
108             this.$refs.loginForm.validate(valid => {
109                 if (valid) {
110                     this.loading = true;
111                     const loginParams = {
112                         loginUserId: this.loginForm.username,
113                         password: encryptDes(this.loginForm.password)
114                     };
115                     this.$store.dispatch('login', loginParams).then(res => {
116                         this.loading = false;
117                         if (res.status === 200) {
118                             this.$store.commit('SET_USER_INFO', res.data.onlineUserInfo);
119                             this.$router.push({ path: '/' });
120                         }
121                     });
122                 } else {
123                     return false;
124                 }
125             });
126         }
127     },
128     mounted() {
129         // 验证码初始化
130         this.identifyCode = '';
131         this.makeCode(this.identifyCodes, 4);
132     }
133 };
134 </script>

identify.vue文件

  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: 28
 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: 40
 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       let deg = this.randomNum(-30, 30);
 95       // 修改坐标原点和旋转角度
 96       ctx.translate(x, y);
 97       ctx.rotate((deg * Math.PI) / 270);
 98       ctx.fillText(txt, 0, 0);
 99       // 恢复坐标原点和旋转角度
100       ctx.rotate((-deg * Math.PI) / 270);
101       ctx.translate(-x, -y);
102     },
103     drawLine(ctx) {
104       // 绘制干扰线
105       for (let i = 0; i < 2; 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 < 20; i++) {
116         ctx.fillStyle = this.randomColor(0, 255);
117         ctx.beginPath();
118         ctx.arc(
119           this.randomNum(0, this.contentWidth),
120           this.randomNum(0, this.contentHeight),
121           1,
122           0,
123           2 * Math.PI
124         );
125         ctx.fill();
126       }
127     }
128   },
129   watch: {
130     identifyCode() {
131       this.drawPic();
132     }
133   },
134   mounted() {
135     this.drawPic();
136   }
137 };
138 </script>

猜你喜欢

转载自www.cnblogs.com/guwufeiyang/p/12956229.html