前提:之前写的vue版滑块验证码,可以在h5和web使用,但是在微信小程序运行就有问题。因此进行一系列的修改
一、vue版滑块验证码实现地址
二、微信小程序遇到问题
1. 小程序是没有window和document对象,所以要把之前代码中的document改成wx.createSelectorQuery()去获取dom,记住每一个dom都要写一遍wx.createSelectorQuery
2. 创建图片用createImage()
三、代码展示
1. code.vue(验证码组件)
<template>
<div id="slideVerify" class="slide-verify" :style="widthlable" onselectstart="return false;">
<canvas ref="canvas" type="2d" id="myCanvas" />
<canvas ref="block" type="2d" id="myBlock" class="slide-verify-block" :style="{ left: sliderLeft }" />
<van-icon class="slide-verify-refresh-icon el-icon-refresh" name="replay" @click="refresh" />
<div class="slide-verify-info" :class="{ fail: fail, show: showInfo }" @click="refresh">
{{ infoText }}
</div>
<div
class="slide-verify-slider"
:style="widthlable"
:class="{
'container-active': containerActive,
'container-success': containerSuccess,
'container-fail': containerFail
}"
>
<div class="slide-verify-slider-mask" :style="{ width: sliderMaskWidth }">
<div
class="slide-verify-slider-mask-item"
:style="{ left: sliderLeft }"
@mousedown="sliderDown"
@touchstart="touchStartEvent"
@touchmove="touchMoveEvent"
@touchend="touchEndEvent"
>
<div class="slide-verify-slider-mask-item-icon el-icon-s-unfold"></div>
</div>
</div>
<span class="slide-verify-slider-text">{{ sliderText }}</span>
</div>
</div>
</template>
<script>
function sum(x, y) {
return x + y
}
function square(x) {
return x * x
}
export default {
name: 'SlideVerify',
props: {
// block length
l: {
type: Number,
default: 42
},
// block radius
r: {
type: Number,
default: 10
},
// canvas width
w: {
// 背景图宽
type: [Number, String],
default: 350
},
// canvas height
h: {
// 背景图高
type: [Number, String],
default: 200
},
// canvas width
sw: {
// 小图宽
type: [Number, String],
default: 50
},
// canvas height
sh: {
type: [Number, String],
default: 50
},
// block_x: {
// type: Number,
// default: 155
// },
blocky: {
// 小图初始的垂直距离
type: [Number, String],
default: 20
},
sliderText: {
type: String,
default: 'Slide filled right'
},
imgurl: {
// 大图地址
type: String,
default: ''
},
miniimgurl: {
// 小图地址
type: String,
default: ''
},
fresh: {
type: Boolean,
default: false
}
},
data() {
return {
containerActive: false, // container active class
containerSuccess: false, // container success class
containerFail: false, // container fail class
canvasCtx: null,
blockCtx: null,
block: null,
canvasStr: null,
L: this.l + this.r * 2 + 3, // block real lenght
img: undefined,
originX: undefined,
originY: undefined,
isMouseDown: false,
trail: [],
widthlable: 'width: 300px;',
sliderLeft: 0, // block right offset
sliderMaskWidth: 0, // mask width
dialogVisible: false,
infoText: '验证成功',
fail: false,
showInfo: false
}
},
watch: {
fresh(val) {
if (val) {
this.init()
}
}
},
mounted() {
// 随机生成数this.block_x =
this.init()
},
methods: {
init() {
this.initDom()
},
initDom(h) {
const that = this
// 创建背景
const query = wx.createSelectorQuery()
query.select('#myCanvas')
.fields({ node: true, size: true })
.exec((res) => {
that.canvasStr = res[0].node;
that.canvasCtx = that.canvasStr.getContext("2d");
// 创建图片对象
let bgImage = that.canvasStr.createImage();
bgImage.src = that.imgurl;
bgImage.onload = () => {
// 画出背景
that.canvasCtx.drawImage(bgImage, 0, 0)
}
})
// 创建移动模块
const query1 = wx.createSelectorQuery()
query1.select('#myBlock')
.fields({ node: true, size: true, rect: true })
.exec((res1) => {
that.block = res1[0].node;
// 获取canvas 2d绘图上下文
that.blockCtx = that.block.getContext("2d");
// 创建图片对象
let bgImage1 = that.block.createImage();
var blocky = h || that.blocky
bgImage1.src = that.miniimgurl;
bgImage1.onload = () => {
// 画出背景
that.blockCtx.drawImage(bgImage1, 0, blocky)
}
})
},
// 刷新
refresh() {
this.$emit('refresh')
},
sliderDown(event) {
this.originX = event.clientX
this.originY = event.clientY
this.isMouseDown = true
},
touchStartEvent(e) {
this.originX = e.touches[0].pageX
this.originY = e.touches[0].pageY
this.isMouseDown = true
},
touchMoveEvent(e) {
if (!this.isMouseDown) return false
const moveX = e.touches[0].pageX - this.originX
const moveY = e.touches[0].pageY - this.originY
if (moveX < 0 || moveX + 38 >= this.w) return false
this.sliderLeft = moveX + 'px'
const blockLeft = ((this.w - 40 - 20) / (this.w - 40)) * moveX
this.containerActive = true
this.sliderMaskWidth = moveX + 'px'
this.trail.push(moveY)
},
touchEndEvent(e) {
if (!this.isMouseDown) return false
this.isMouseDown = false
if (e.mp.changedTouches[0].pageX === this.originX) return false
this.containerActive = false
this.verify()
},
verify() {
const arr = this.trail // drag y move distance
const average = arr.reduce(sum) / arr.length // average
const deviations = arr.map(x => x - average) // deviation array
const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length) // standard deviation
const left = parseInt(this.sliderLeft)
this.$emit('success', left, stddev)
},
reset(h) {
this.containerActive = false
this.containerActive = false
this.containerSuccess = false
this.containerFail = false
this.sliderLeft = 0
this.sliderMaskWidth = 0
this.canvasCtx.clearRect(0, 0, this.w, this.h)
this.blockCtx.clearRect(0, 0, this.w, this.h)
this.fail = false
this.showInfo = false
this.containerFail = false
this.containerSuccess = false
this.initDom(h)
}
}
}
</script>
<style scoped>
.slide-verify {
position: relative;
width: 100%;
overflow: hidden;
}
.slide-verify-block {
position: absolute;
left: 0;
top: 0;
}
.slide-verify-refresh-icon {
position: absolute;
right: 10px;
top: 0;
width: 34px;
height: 34px;
cursor: pointer;
content: '刷新';
font-size: 22px;
line-height: 34px;
text-align: center;
font-weight: bold;
color: #3fdeae;
/* background: url("../../assets/move/icon_light.png") 0 -437px; */
background-size: 34px 471px;
}
.slide-verify-refresh-icon:hover {
transform: rotate(180deg);
transition: all 0.2s ease-in-out;
}
.slide-verify-slider {
position: relative;
text-align: center;
width: 310px;
height: 40px;
line-height: 40px;
margin-top: 15px;
background: #f7f9fa;
color: #45494c;
border: 1px solid #e4e7eb;
}
.slide-verify-slider-mask {
position: absolute;
left: 0;
top: 0;
height: 40px;
border: 0 solid #1991fa;
background: #d1e9fe;
}
.slide-verify-info {
position: absolute;
top: 170px;
left: 0;
height: 30px;
width: 350px;
color: #fff;
text-align: center;
line-height: 30px;
background-color: #52ccba;
opacity: 0;
}
.slide-verify-info.fail {
background-color: #f57a7a;
}
.slide-verify-info.show {
animation: hide 1s ease;
}
@keyframes hide {
0% {
opacity: 0;
}
100% {
opacity: 0.9;
}
}
.slide-verify-slider-mask-item {
position: absolute;
top: 0;
left: 0;
width: 38px;
height: 38px;
background: #fff;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
cursor: pointer;
transition: background 0.2s linear;
}
.slide-verify-slider-mask-item:hover {
background: #1991fa;
}
.slide-verify-slider-mask-item:hover .slide-verify-slider-mask-item-icon {
background-position: 0 -13px;
}
.slide-verify-slider-mask-item-icon {
position: absolute;
top: 9px;
left: 7px;
width: 14px;
height: 12px;
content: '法币';
font-size: 22px;
color: #ddd;
/* text-align: center;
line-height: 12px; */
/* background: url("../../assets/move/icon_light.png") 0 -26px; */
/* background-size: 34px 471px; */
}
.container-active .slide-verify-slider-mask-item {
height: 38px;
top: -1px;
border: 1px solid #1991fa;
}
.container-active .slide-verify-slider-mask {
height: 38px;
border-width: 1px;
}
.container-success .slide-verify-slider-mask-item {
height: 38px;
top: -1px;
border: 1px solid #52ccba;
background-color: #52ccba !important;
}
.container-success .slide-verify-slider-mask {
height: 38px;
border: 1px solid #52ccba;
background-color: #d2f4ef;
}
.container-success .slide-verify-slider-mask-item-icon {
background-position: 0 0 !important;
}
.container-fail .slide-verify-slider-mask-item {
height: 38px;
top: -1px;
border: 1px solid #f57a7a;
background-color: #f57a7a !important;
}
.container-fail .slide-verify-slider-mask {
height: 38px;
border: 1px solid #f57a7a;
background-color: #fce1e1;
}
.container-fail .slide-verify-slider-mask-item-icon {
top: 14px;
background-position: 0 -82px !important;
}
.container-active .slide-verify-slider-text,
.container-success .slide-verify-slider-text,
.container-fail .slide-verify-slider-text {
display: none;
}
</style>
2. 引用组件
<template>
<div>
<van-button class="b-btn" type="info" size="small" @click="handleLogin">登 录</van-button>
<div v-show="isShowCode" class="box-code">
<div class="title">
<span>请完成安全验证</span>
<span class="close" @click="isShowCode = false">×</span>
</div>
<Captcha
ref="dialogopen"
:l="42"
:r="10"
:w="catcha.w"
:h="catcha.h"
:blocky="catcha.blocky"
:imgurl="catcha.imgurl"
:miniimgurl="catcha.miniimgurl"
:slider-text="catcha.text"
@success="onSuccess"
@fail="onFail"
@refresh="onRefresh"
/>
</div>
</div>
</template>
<script>
import Captcha from './code'
export default {
components: {
Captcha
},
data() {
return {
isShowCode: false,
catcha: {
blocky: 0,
imgurl: '',
miniimgurl: '',
text: '向右滑动完成拼图',
h: 200,
w: 350,
sh: 45,
sw: 55,
modifyImg: '',
unionKey: ''
} // 图片验证码传值
}
},
methods: {
handleLogin() {
if (!this.userInfo.ksh || !this.userInfo.idcard) {
Toast.fail('请填写账号密码')
return
}
this.isShowCode = true
this.getImageVerifyCode()
},
// 获取图形验证码
getImageVerifyCode() {
getPicCode().then(res => {
if (res && res.data) {
var imgobj = res.data
this.catcha.blocky = imgobj.y
this.catcha.imgurl = 'data:image/png;base64,' + imgobj.shadeImage
this.catcha.miniimgurl = 'data:image/png;base64,' + imgobj.cutoutImage
this.catcha.unionKey = imgobj.unionKey
}
this.$nextTick(() => {
if (this.$refs.dialogopen) {
this.$refs.dialogopen.reset(imgobj.y)
}
})
})
},
onSuccess(val) {
this.login({
x: val,
y: this.catcha.blocky,
unionKey: this.catcha.unionKey
})
},
onFail() {
console.log('失败')
},
// 刷新
onRefresh() {
this.catcha.imgurl = ''
this.catcha.miniimgurl = ''
this.getImageVerifyCode()
}
}
}
</script>
<style>
page {
background: #cce2fe;
}
</style>
<style scoped>
.item-login {
padding: 25px 0 0;
position: relative;
}
.lable-item-login {
padding: 0 15px 20px;
}
.pgo-top {
margin-bottom: 5px;
}
.pgo-top-img {
width: 100%;
}
.b-parent {
margin: 20px 15px 40px;
}
.box-login {
position: fixed;
top: 188px;
left: 0;
right: 0;
border: solid 12px #a5cefc;
margin: 0 45px;
background: #fff;
}
.login-top {
height: 59px;
width: 172px;
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, -20%);
z-index: 10;
}
.pt50 {
padding-top: 60px;
}
.pic-code{
position: absolute;
width: 80px;
height: 30px;
right: 20px;
bottom: 5px;
z-index: 10;
}
.box-code {
position: fixed;
left: 50%;
top: 50%;
width: 300px;
transform: translate(-50%, -50%);
background-color: #fff;
border-radius: 3px;
overflow: hidden;
padding-bottom: 10px;
}
.box-code .title {
font-size: 14px;
padding: 5px;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
}
.box-code .title .close {
font-size: 20px;
}
.box-code > .content {
margin: 0 auto;
}
</style>
四、总结
主要是小程序的部分写法和web的写法起冲突了,了解后就可以了。