经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » jQuery » 查看文章
Vue 前端验证码 - canvas Rem布局自适应 - 跨域问题
来源:cnblogs  作者:J1ay  时间:2021/3/1 9:13:10  对本文有异议

?前言


在vue项目中,登录界面必不可少。简单项目里,验证码通常由数字字母构成。一般有两种产生方式:前端,后端。后端生成,前端直接调用接口,将返回的url放入a标签即可。而前端生成,则大多用canvas画布实现,如何让验证码随屏幕大小变化,还能保持原样不失真,这就是我们要实现的功能。当然,在创建vue项目时,我们必须得克服跨域问题。No ' Access-Control-Allow-Origin'的解决方案,在文章最后。若有错误和建议,请积极指正!


?canvas 生成验证码 (vue)


按照需求,一步步实现验证码生成。(源码贴在后面)



下面,进入正题。


组件需要什么?


首先,我们自己注册一个组件 Identify.vue ,用来实现验证码的生成。


第一步, 我们要明确,这个组件需要什么?


显然,我们需要一个画布,在画布上进行绘制,生成验证码就好。


自然而然, <canvas></canvas> 就布局上去了。


  1. <template>
  2. <div class="canvas">
  3. <canvas id="canvas" class="yanzheng1"></canvas>
  4. </div>
  5. </template>

再者,我们需定义组件属性


  1. props: {
  2. identifyCode: { // 默认注册码
  3. type: String,
  4. default: '1234'
  5. },
  6. fontSizeMin: { // 字体最小值
  7. type: Number,
  8. default: 130
  9. },
  10. fontSizeMax: { // 字体最大值
  11. type: Number,
  12. default: 140
  13. }
  14. }

组件的实现


接下来,我们可以进行绘制验证码。内容包括如下:


1、随机数(验证码内容:一般为数字字母组合)


2、随机颜色 (rgb实现)


3、干扰线


4、干扰点


随机数


  1. randomNum (min, max) {
  2. return Math.floor(Math.random() * (max - min) + min)
  3. },

随机色


  1. randomColor (min, max) {
  2. const r = this.randomNum(min, max)
  3. const g = this.randomNum(min, max)
  4. const b = this.randomNum(min, max)
  5. return 'rgb(' + r + ',' + g + ',' + b + ')'
  6. },

干扰线


  1. drawLine (ctx) {
  2. const canvas = document.getElementById('canvas')
  3. for (let j = 0; j < 4; j++) {
  4. ctx.strokeStyle = this.randomColor(100, 200)
  5. ctx.beginPath()
  6. ctx.moveTo(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height))
  7. ctx.lineTo(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height))
  8. // lineWidth 控制线的粗细
  9. ctx.lineWidth = 3
  10. ctx.stroke()
  11. }
  12. },

干扰点


  1. drawDot (ctx) {
  2. const canvas = document.getElementById('canvas')
  3. for (let k = 0; k < 30; k++) {
  4. ctx.fillStyle = this.randomColor(0, 255)
  5. ctx.beginPath()
  6. // 可以改变 3 来实现改变干扰点的大小
  7. ctx.arc(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height), 3, 0, 2 * Math.PI)
  8. ctx.fill()
  9. }
  10. }

绘制验证码


  1. drawPic () {
  2. const canvas = document.getElementById('canvas')
  3. const ctx = canvas.getContext('2d')
  4. ctx.textBaseline = 'bottom'
  5. // console.log(canvas.width)
  6. // 绘制背景(颜色)
  7. ctx.fillStyle = '#e6ecfd'
  8. ctx.fillRect(0, 0, canvas.width, canvas.height)
  9. // 绘制文字
  10. for (let i = 0; i < this.identifyCode.length; i++) {
  11. this.drawText(ctx, this.identifyCode[i], i)
  12. }
  13. this.drawLine(ctx)
  14. this.drawDot(ctx)
  15. },
  1. drawText (ctx, txt, i) {
  2. const canvas = document.getElementById('canvas')
  3. ctx.fillStyle = this.randomColor(50, 160) // 随机生成字体颜色
  4. ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei' // 随机生成字体大小
  5. // x,y控制生成字体在画布上分布的位置。如下的0.5/1,可根据实际情况进行增减。
  6. const x = (i + 0.5 ) * (canvas.width / (this.identifyCode.length + 1))
  7. const y = this.randomNum(this.fontSizeMax, canvas.height - 5)
  8. var deg = this.randomNum(-30, 30)
  9. // 修改坐标原点和旋转角度
  10. ctx.translate(x, y)
  11. ctx.rotate(deg * Math.PI / 180)
  12. ctx.fillText(txt, 0, 0)
  13. // 恢复坐标原点和旋转角度
  14. ctx.rotate(-deg * Math.PI / 180)
  15. ctx.translate(-x, -y)
  16. },

注意:


  1. const canvas = document.getElementById('canvas')
  2. // canvas.width是为了获取到画布的宽度,实现适配。高度亦是如此。

组件源码


原理搞懂,直接上手。新建一个vue文件。


Identify.vue


代码如下:


  1. <template>
  2. <div class="canvas">
  3. <canvas id="canvas" class="yanzheng1"></canvas>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: 'Identify',
  9. props: {
  10. identifyCode: { // 默认注册码
  11. type: String,
  12. default: '1234'
  13. },
  14. fontSizeMin: { // 字体最小值
  15. type: Number,
  16. default: 130
  17. },
  18. fontSizeMax: { // 字体最大值
  19. type: Number,
  20. default: 140
  21. }
  22. },
  23. methods: {
  24. // 生成一个随机数
  25. randomNum (min, max) {
  26. return Math.floor(Math.random() * (max - min) + min)
  27. },
  28. // 生成一个随机的颜色
  29. randomColor (min, max) {
  30. const r = this.randomNum(min, max)
  31. const g = this.randomNum(min, max)
  32. const b = this.randomNum(min, max)
  33. return 'rgb(' + r + ',' + g + ',' + b + ')'
  34. },
  35. drawPic () {
  36. const canvas = document.getElementById('canvas')
  37. const ctx = canvas.getContext('2d')
  38. ctx.textBaseline = 'bottom'
  39. // console.log(canvas.width)
  40. // 绘制背景
  41. ctx.fillStyle = '#e6ecfd'
  42. ctx.fillRect(0, 0, canvas.width, canvas.height)
  43. // 绘制文字
  44. for (let i = 0; i < this.identifyCode.length; i++) {
  45. this.drawText(ctx, this.identifyCode[i], i)
  46. }
  47. this.drawLine(ctx)
  48. this.drawDot(ctx)
  49. },
  50. drawText (ctx, txt, i) {
  51. const canvas = document.getElementById('canvas')
  52. ctx.fillStyle = this.randomColor(50, 160) // 随机生成字体颜色
  53. ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei' // 随机生成字体大小
  54. const x = (i + 0.5 ) * (canvas.width / (this.identifyCode.length + 1))
  55. const y = this.randomNum(this.fontSizeMax, canvas.height - 5)
  56. var deg = this.randomNum(-30, 30)
  57. // 修改坐标原点和旋转角度
  58. ctx.translate(x, y)
  59. ctx.rotate(deg * Math.PI / 180)
  60. ctx.fillText(txt, 0, 0)
  61. // 恢复坐标原点和旋转角度
  62. ctx.rotate(-deg * Math.PI / 180)
  63. ctx.translate(-x, -y)
  64. },
  65. drawLine (ctx) {
  66. // 绘制干扰线
  67. const canvas = document.getElementById('canvas')
  68. for (let j = 0; j < 4; j++) {
  69. ctx.strokeStyle = this.randomColor(100, 200)
  70. ctx.beginPath()
  71. ctx.moveTo(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height))
  72. ctx.lineTo(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height))
  73. ctx.lineWidth = 3
  74. ctx.stroke()
  75. }
  76. },
  77. drawDot (ctx) {
  78. // 绘制干扰点
  79. const canvas = document.getElementById('canvas')
  80. for (let k = 0; k < 30; k++) {
  81. ctx.fillStyle = this.randomColor(0, 255)
  82. ctx.beginPath()
  83. ctx.arc(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height), 3, 0, 2 * Math.PI)
  84. ctx.fill()
  85. }
  86. }
  87. },
  88. watch: {
  89. identifyCode () {
  90. this.drawPic()
  91. }
  92. },
  93. mounted () {
  94. this.drawPic()
  95. }
  96. }
  97. </script>
  98. <style scoped>
  99. .yanzheng1{
  100. width: 100px;
  101. height: 34px;
  102. }
  103. </style>

之后,在要引入的页面,引用。


引用验证码.vue


这里用的是 element UI elementUI官网


  1. <el-form-item prop="code">
  2. <el-input @keyup.enter.native="checkCode" type="text" v-model="code" placeholder=" - - - -">
  3. <template slot="suffix">
  4. <div class="yanzheng" @click="refreshCode">
  5. <!-- Identify就是注册组件-->
  6. <Identify class="yanzheng1" :identifyCode="identifyCode"></Identify>
  7. </div>
  8. </template>
  9. </el-input>
  10. </el-form-item>
  11. <!-- @keyup.enter.native 实现输入完,按回车执行checkCode函数,或者按钮实现-->

  1. <script>
  2. import Identify from '../components/Identify.vue'
  3. export default {
  4. components: {
  5. Identify
  6. },
  7. data () {
  8. return {
  9. identifyCodes: '1234567890abcdefjhijk1234567890linopqrsduvwxyz', // 验证码组成元素
  10. identifyCode: '', // 验证码生成结果
  11. code:'', // 验证码输入内容
  12. }
  13. },
  14. mounted () {
  15. // 初始化验证码
  16. this.identifyCode = ''
  17. this.makeCode(this.identifyCodes, 4)
  18. },
  19. methods:{
  20. // 重置验证码
  21. refreshCode () {
  22. this.identifyCode = ''
  23. this.makeCode(this.identifyCodes, 4)
  24. },
  25. makeCode (o, l) {
  26. // o代表随机数元素集合,l代表验证码位数(现在为4位验证码)
  27. for (let i = 0; i < l; i++) {
  28. this.identifyCode += this.identifyCodes[this.randomNum(0, this.identifyCodes.length)]
  29. }
  30. },
  31. randomNum (min, max) {
  32. return Math.floor(Math.random() * (max - min) + min)
  33. },
  34. checkCode(){
  35. if (this.code.toLowerCase() !== this.identifyCode.toLowerCase()) {
  36. this.$message.error('请填写正确验证码')
  37. this.refreshCode()
  38. }
  39. else {
  40. this.$message.success('验证成功!')
  41. // 执行功能函数
  42. }
  43. }
  44. }
  45. }
  46. </script>
  47. <style scoped>
  48. .yanzheng{
  49. width: 100px;
  50. height: 34px;
  51. text-align: center;
  52. margin: 5px 30px;
  53. border: 2px solid #009199;
  54. }
  55. .yanzheng1{
  56. width: 100px;
  57. height: 34px;
  58. }
  59. </style>

css样式,可自行调整。实现效果如下:



?vue自适应,可采用rem布局


参考链接


安装postcss-px2rem以及px2rem-loader


  1. npm install postcss-px2rem px2rem-loader --save

创建 rem.js 文件


可在src目录下创建utils文件夹,放入其中(/src/utils/rem.js)


  1. // rem等比适配配置文件
  2. // 基准大小
  3. const baseSize = 16
  4. // 设置 rem 函数
  5. function setRem () {
  6. if (document.documentElement.clientWidth < 768) {
  7. // 当前页面宽度相对于750宽的缩放比例。
  8. document.documentElement.style.fontSize = baseSize *(document.documentElement.clientWidth/ 750 - 0.36) + 'px'
  9. }
  10. else {
  11. // 当前页面宽度相对于1920宽的缩放比例。
  12. const scale = document.documentElement.clientWidth / 1920
  13. // 设置页面根节点字体大小
  14. document.documentElement.style.fontSize = baseSize * Math.min(scale, 1.7) + 'px'
  15. }
  16. }
  17. // 初始化
  18. setRem()
  19. // 改变窗口大小时重新设置 rem
  20. window.onresize = function () {
  21. setRem()
  22. }

在main.js中引入


  1. import '../src/util/rem'

最后,在 vue.config.js 中配置插件


  1. // 引入等比适配插件
  2. const px2rem = require('postcss-px2rem')
  3. // 配置基本大小
  4. const postcss = px2rem({
  5. // 基准大小 baseSize,需要和rem.js中相同
  6. remUnit: 16
  7. })
  8. // 使用等比适配插件
  9. module.exports = {
  10. lintOnSave: true,
  11. css: {
  12. loaderOptions: {
  13. postcss: {
  14. plugins: [
  15. postcss
  16. ]
  17. }
  18. }
  19. }
  20. }

不要忘记重启!!重启方可生效!


?vue跨域问题,No ' Access-Control-Allow-Origin'


在开始vue项目构建之前的必经之路,就是避免跨域问题。


直接上手:


vue.config.js配置


创建好vue工程后,找到 vue.config.js 文件,双击点击进入项目即可(与src目录同级)


若没有该配置文件,则新建 vue.config.js 文件,与src目录同级。


  1. module.exports = {
  2. devServer: {
  3. host: '127.0.0.1', // 本地
  4. open: true,
  5. port: 8082, // 本地开放端口
  6. overlay: true,
  7. headers: {
  8. 'Access-Control-Allow-Origin': '*'
  9. },
  10. hotOnly: false,
  11. disableHostCheck: true,
  12. proxy: {
  13. '/api': {
  14. target: 'https://xxx.xxx.xxx.xxx:8181', // 目标服务器,api请求地址(https和http要区分)
  15. ws: true,
  16. changeOrigin: true,
  17. pathRewrite: {
  18. '^/api': '/'
  19. }
  20. }
  21. }
  22. }
  23. }

一般情况下,都会安装 axios运行依赖 , 官网


  1. npm install axios

main.js配置


main.js 中配置如下:


  1. // 引入axios
  2. import axios from 'axios'
  3. Vue.prototype.$axios = axios
  4. Vue.prototype.$http = axios
  5. // 利用'/api'为基准地址,实现跨域。
  6. axios.defaults.baseURL='/api'
  7. Vue.config.productionTip = false

接口调用举例


/test/all/ 为接口请求


  1. async getTestList () {
  2. const { data: res } = await this.$http.get('/test/all/')
  3. if (res.status !== 200) {
  4. return this.$message.error(res.msg)
  5. }
  6. this.$message.success('成功!')
  7. },

如下,出现api,即成功!



注意!!配置完记得重启!!否则不生效!!


关于以上链接引用【侵权删】


【转载请放链接】 https://www.cnblogs.com/Jlay/p/vue_canvas.html

原文链接:http://www.cnblogs.com/Jlay/p/vue_canvas.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号