wux-weapp
Version:
一套组件化、可复用、易扩展的微信小程序 UI 组件库
180 lines (163 loc) • 5.44 kB
JavaScript
import { getSystemInfoSync } from '../helpers/hooks/useNativeAPI'
import { toDataURL, getCanvasRef } from '../helpers/hooks/useCanvasAPI'
/**
* 获取范围内的随机数
* @param {Number} min 最小值
* @param {Number} max 最大值
*/
const randomNum = (min, max) => {
return Math.floor(Math.random() * (max - min) + min)
}
/**
* 获取范围内的随机颜色值
* @param {Number} min 最小值
* @param {Number} max 最大值
*/
const randomColor = (min, max) => {
const _r = randomNum(min, max)
const _g = randomNum(min, max)
const _b = randomNum(min, max)
return `rgb(${_r}, ${_g}, ${_b})`
}
/**
* 创建 canvas 绘图上下文
* @param {Object} ctx canvas 绘图上下文
* @param {Object} props 配置项
* @param {String} props.str 验证码范围
* @param {Number} props.num 验证码长度,默认值 6
* @param {Number} props.width 画布宽度,默认值 120
* @param {Number} props.height 画布高度,默认值 40
* @param {String} props.bgColor 画布背景色
* @param {String} props.fontColor 画布字体颜色
* @param {Boolean} props.hasPoint 是否显示干扰点,默认 true
* @param {Boolean} props.hasLine 是否显示干扰线,默认 true
*/
const render = (ctx, props = {}) => {
const { str, num, width, height, bgColor, fontColor, hasPoint, hasLine } = props
const ratio = getSystemInfoSync(['window']).pixelRatio
let vcode = ''
// 绘制矩形,并设置填充色
ctx.textBaseline = 'bottom'
ctx.fillStyle = bgColor ? bgColor : randomColor(180, 240)
ctx.scale(ratio, ratio)
ctx.fillRect(0, 0, width, height)
// 绘制随机生成 n 位的验证码
for (let i = 0; i < num; i++) {
const x = (width - 10) / num * i + 10
const y = randomNum(height / 2, height)
const deg = randomNum(-45, 45)
const txt = str[randomNum(0, str.length)]
const fontSize = randomNum(16, 40)
const halfHeight = parseInt(height / 2)
vcode += txt
ctx.fillStyle = fontColor ? fontColor : randomColor(10, 100)
ctx.font = `normal normal normal ${fontSize > halfHeight ? halfHeight : fontSize}px sans-serif`
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)
}
// 绘制干扰线
if (!!hasLine) {
for (let i = 0; i < num; i++) {
ctx.strokeStyle = randomColor(90, 180)
ctx.beginPath()
ctx.moveTo(randomNum(0, width), randomNum(0, height))
ctx.lineTo(randomNum(0, width), randomNum(0, height))
ctx.stroke()
}
}
// 绘制干扰点
if (!!hasPoint) {
for (let i = 0; i < num * 10; i++) {
ctx.fillStyle = randomColor(0, 255)
ctx.beginPath()
ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI)
ctx.fill()
}
}
return vcode
}
Component({
properties: {
str: {
type: String,
value: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
},
num: {
type: Number,
value: 6,
},
width: {
type: Number,
value: 120,
},
height: {
type: Number,
value: 40,
},
bgColor: {
type: String,
value: '',
},
fontColor: {
type: String,
value: '',
},
hasPoint: {
type: Boolean,
value: true,
},
hasLine: {
type: Boolean,
value: true,
},
canvasId: {
type: String,
value: 'wux-vcode',
},
},
methods: {
/**
* 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中
*/
createCanvasContext(props) {
const { width, height, canvasId } = props
const setBase64Url = ({ value, base64Url }) => {
this.triggerEvent('change', { value, base64Url })
}
const renderCanvas = () => getCanvasRef(canvasId, this).then((canvas) => {
const ctx = canvas.getContext('2d')
const ratio = getSystemInfoSync(['window']).pixelRatio
const canvasWidth = width * ratio
const canvasHeight = height * ratio
canvas.width = canvasWidth
canvas.height = canvasHeight
const value = render(ctx, props)
return toDataURL({ width, height }, canvas)
.then((base64Url) => {
ctx.restore()
return { value, base64Url }
})
})
let promise = Promise.resolve()
promise = promise.then(() => {
return renderCanvas()
})
promise = promise.then(({ value, base64Url }) => {
setBase64Url({ value, base64Url })
}, (err) => {
this.triggerEvent('error', err)
// console.error(err)
})
return promise
},
draw() {
this.createCanvasContext(this.data)
},
},
ready() {
this.createCanvasContext(this.data)
},
})