Vue 前端驗證碼 - canvas Rem布局自適應 - 跨域問題


⭐前言


在vue項目中,登錄界面必不可少。簡單項目里,驗證碼通常由數字字母構成。一般有兩種產生方式:前端,后端。后端生成,前端直接調用接口,將返回的url放入a標簽即可。而前端生成,則大多用canvas畫布實現,如何讓驗證碼隨屏幕大小變化,還能保持原樣不失真,這就是我們要實現的功能。當然,在創建vue項目時,我們必須得克服跨域問題。No ' Access-Control-Allow-Origin'的解決方案,在文章最后。若有錯誤和建議,請積極指正!


⭐canvas 生成驗證碼 (vue)


按照需求,一步步實現驗證碼生成。(源碼貼在后面)



下面,進入正題。


組件需要什么?


首先,我們自己注冊一個組件 Identify.vue ,用來實現驗證碼的生成。


第一步, 我們要明確,這個組件需要什么?


顯然,我們需要一個畫布,在畫布上進行繪制,生成驗證碼就好。


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


<template>
  <div class="canvas">
    <canvas id="canvas" class="yanzheng1"></canvas>
  </div>
</template>

再者,我們需定義組件屬性


props: {
    identifyCode: { // 默認注冊碼
      type: String,
      default: '1234'
    },
    fontSizeMin: { // 字體最小值
      type: Number,
      default: 130
    },
    fontSizeMax: { // 字體最大值
      type: Number,
      default: 140
    }
}

組件的實現


接下來,我們可以進行繪制驗證碼。內容包括如下:


1、隨機數(驗證碼內容:一般為數字字母組合)


2、隨機顏色 (rgb實現)


3、干擾線


4、干擾點


隨機數


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

隨機色


randomColor (min, max) {
    const r = this.randomNum(min, max)
    const g = this.randomNum(min, max)
    const b = this.randomNum(min, max)
    return 'rgb(' + r + ',' + g + ',' + b + ')'
},

干擾線


drawLine (ctx) {
    const canvas = document.getElementById('canvas')
    for (let j = 0; j < 4; j++) {
    ctx.strokeStyle = this.randomColor(100, 200)
    ctx.beginPath()
    ctx.moveTo(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height))
    ctx.lineTo(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height))
    // lineWidth 控制線的粗細
    ctx.lineWidth = 3
    ctx.stroke()
    }
},

干擾點


drawDot (ctx) {
    const canvas = document.getElementById('canvas')
    for (let k = 0; k < 30; k++) {
        ctx.fillStyle = this.randomColor(0, 255)
        ctx.beginPath()
        // 可以改變 3 來實現改變干擾點的大小
        ctx.arc(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height), 3, 0, 2 * Math.PI)
        ctx.fill()
    }
}

繪制驗證碼


drawPic () {
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')
    ctx.textBaseline = 'bottom'
    // console.log(canvas.width)
    // 繪制背景(顏色)
    ctx.fillStyle = '#e6ecfd'
    ctx.fillRect(0, 0, canvas.width, canvas.height)
    // 繪制文字
    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) {
    const canvas = document.getElementById('canvas')
    ctx.fillStyle = this.randomColor(50, 160) // 隨機生成字體顏色
    ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei' // 隨機生成字體大小
    // x,y控制生成字體在畫布上分布的位置。如下的0.5/1,可根據實際情況進行增減。
    const x = (i + 0.5 ) * (canvas.width / (this.identifyCode.length + 1))
    const y = this.randomNum(this.fontSizeMax, canvas.height - 5)
    var deg = this.randomNum(-30, 30)
    // 修改坐標原點和旋轉角度
    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)
},

注意:


const canvas = document.getElementById('canvas')
// canvas.width是為了獲取到畫布的寬度,實現適配。高度亦是如此。

組件源碼


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


Identify.vue


代碼如下:


<template>
  <div class="canvas">
    <canvas id="canvas" class="yanzheng1"></canvas>
  </div>
</template>

<script>
export default {
  name: 'Identify',
  props: {
    identifyCode: { // 默認注冊碼
      type: String,
      default: '1234'
    },
    fontSizeMin: { // 字體最小值
      type: Number,
      default: 130
    },
    fontSizeMax: { // 字體最大值
      type: Number,
      default: 140
    }
  },
  methods: {
    // 生成一個隨機數
    randomNum (min, max) {
      return Math.floor(Math.random() * (max - min) + min)
    },

    // 生成一個隨機的顏色
    randomColor (min, max) {
      const r = this.randomNum(min, max)
      const g = this.randomNum(min, max)
      const b = this.randomNum(min, max)
      return 'rgb(' + r + ',' + g + ',' + b + ')'
    },

    drawPic () {
      const canvas = document.getElementById('canvas')
      const ctx = canvas.getContext('2d')
      ctx.textBaseline = 'bottom'
      // console.log(canvas.width)
      // 繪制背景
      ctx.fillStyle = '#e6ecfd'
      ctx.fillRect(0, 0, canvas.width, canvas.height)
      // 繪制文字
      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) {
      const canvas = document.getElementById('canvas')
      ctx.fillStyle = this.randomColor(50, 160) // 隨機生成字體顏色
      ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax) + 'px SimHei' // 隨機生成字體大小
      const x = (i + 0.5 ) * (canvas.width / (this.identifyCode.length + 1))
      const y = this.randomNum(this.fontSizeMax, canvas.height - 5)
      var deg = this.randomNum(-30, 30)
      // 修改坐標原點和旋轉角度
      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) {
      // 繪制干擾線
      const canvas = document.getElementById('canvas')
      for (let j = 0; j < 4; j++) {
        ctx.strokeStyle = this.randomColor(100, 200)
        ctx.beginPath()
        ctx.moveTo(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height))
        ctx.lineTo(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height))
        ctx.lineWidth = 3
        ctx.stroke()
      }
    },

    drawDot (ctx) {
      // 繪制干擾點
      const canvas = document.getElementById('canvas')
      for (let k = 0; k < 30; k++) {
        ctx.fillStyle = this.randomColor(0, 255)
        ctx.beginPath()
        ctx.arc(this.randomNum(0, canvas.width), this.randomNum(0, canvas.height), 3, 0, 2 * Math.PI)
        ctx.fill()
      }
    }
  },
  watch: {
    identifyCode () {
      this.drawPic()
    }
  },
  mounted () {
    this.drawPic()
  }
}
</script>

<style scoped>
  .yanzheng1{
    width: 100px;
    height: 34px;
  }
</style>


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


引用驗證碼.vue


這里用的是 element UI elementUI官網


<el-form-item prop="code">
    <el-input @keyup.enter.native="checkCode"  type="text" v-model="code" placeholder=" - - - -">
        <template slot="suffix">
            <div class="yanzheng"  @click="refreshCode">
                <!--        Identify就是注冊組件-->
                <Identify class="yanzheng1"  :identifyCode="identifyCode"></Identify>
            </div>
        </template>
    </el-input>
</el-form-item>

<!--  @keyup.enter.native 實現輸入完,按回車執行checkCode函數,或者按鈕實現--> 

<script>
  import Identify from '../components/Identify.vue'
  export default {
      components: {
          Identify
      },
      data () {
          return {
              identifyCodes: '1234567890abcdefjhijk1234567890linopqrsduvwxyz', // 驗證碼組成元素
              identifyCode: '', // 驗證碼生成結果
              code:'', // 驗證碼輸入內容
          }
      },
      mounted () {
          // 初始化驗證碼
          this.identifyCode = ''
          this.makeCode(this.identifyCodes, 4)
      },
      methods:{
           // 重置驗證碼
          refreshCode () {
            this.identifyCode = ''
            this.makeCode(this.identifyCodes, 4)
          },
          makeCode (o, l) {
              // o代表隨機數元素集合,l代表驗證碼位數(現在為4位驗證碼)
            for (let i = 0; i < l; i++) {
              this.identifyCode += this.identifyCodes[this.randomNum(0, this.identifyCodes.length)]
            }
          },
          randomNum (min, max) {
            return Math.floor(Math.random() * (max - min) + min)
          },
          checkCode(){
              if (this.code.toLowerCase() !== this.identifyCode.toLowerCase()) {
                  this.$message.error('請填寫正確驗證碼')
                  this.refreshCode()
              }
              else {
                  this.$message.success('驗證成功!')
                  // 執行功能函數
              }
          }
      }
  }
</script>

<style scoped>
    .yanzheng{
        width: 100px;
        height: 34px;
        text-align: center;
        margin: 5px 30px;
        border: 2px solid #009199;
    }
    .yanzheng1{
        width: 100px;
        height: 34px;
    }
</style>
    

css樣式,可自行調整。實現效果如下:



⭐vue自適應,可采用rem布局


參考鏈接


安裝postcss-px2rem以及px2rem-loader


npm install postcss-px2rem px2rem-loader --save

創建 rem.js 文件


可在src目錄下創建utils文件夾,放入其中(/src/utils/rem.js)


// rem等比適配配置文件
// 基准大小
const baseSize = 16
// 設置 rem 函數
function setRem () {
    if (document.documentElement.clientWidth < 768) {
        // 當前頁面寬度相對於750寬的縮放比例。
        document.documentElement.style.fontSize = baseSize *(document.documentElement.clientWidth/ 750 - 0.36)  + 'px'
    }
    else {
    	// 當前頁面寬度相對於1920寬的縮放比例。
        const scale = document.documentElement.clientWidth / 1920
        // 設置頁面根節點字體大小
        document.documentElement.style.fontSize = baseSize * Math.min(scale, 1.7) + 'px'
    }
}
// 初始化
setRem()
// 改變窗口大小時重新設置 rem
window.onresize = function () {
    setRem()
}

在main.js中引入


import '../src/util/rem'

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


// 引入等比適配插件
const px2rem = require('postcss-px2rem')

// 配置基本大小
const postcss = px2rem({
  // 基准大小 baseSize,需要和rem.js中相同
  remUnit: 16
})

// 使用等比適配插件
module.exports = {
  lintOnSave: true,
  css: {
    loaderOptions: {
      postcss: {
        plugins: [
          postcss
        ]
      }
    }
  }
}

不要忘記重啟!!重啟方可生效!


⭐vue跨域問題,No ' Access-Control-Allow-Origin'


在開始vue項目構建之前的必經之路,就是避免跨域問題。


直接上手:


vue.config.js配置


創建好vue工程后,找到 vue.config.js 文件,雙擊點擊進入項目即可(與src目錄同級)


若沒有該配置文件,則新建 vue.config.js 文件,與src目錄同級。


module.exports = {
  devServer: {
    host: '127.0.0.1', // 本地
    open: true,
    port: 8082, // 本地開放端口
    overlay: true,
    headers: {
      'Access-Control-Allow-Origin': '*'
    },
    hotOnly: false,
    disableHostCheck: true,
    proxy: {
      '/api': {
        target: 'https://xxx.xxx.xxx.xxx:8181', // 目標服務器,api請求地址(https和http要區分)
        ws: true,
        changeOrigin: true,
        pathRewrite: {
          '^/api': '/'
        }
      }
    }
  }
}

一般情況下,都會安裝 axios運行依賴 , 官網


npm install axios

main.js配置


main.js 中配置如下:


// 引入axios
import axios from 'axios'
Vue.prototype.$axios = axios
Vue.prototype.$http = axios

// 利用'/api'為基准地址,實現跨域。
axios.defaults.baseURL='/api'

Vue.config.productionTip = false

接口調用舉例


/test/all/ 為接口請求


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

如下,出現api,即成功!



注意!!配置完記得重啟!!否則不生效!!


關於以上鏈接引用【侵權刪】


【轉載請放鏈接】 https://www.cnblogs.com/Jlay/p/vue_canvas.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM