版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/oncemore520/article/details/85327454
由于我们的官网之前是那种数字验证码,还是被扒了,所以现在我们打算要搞一个拼图验证码,交给我后,让我研究研究。
首先需求是这样的:
1.
两个图片都是后台传过来,所以不需要前端生成 ,小图的位置是随机的,把小图拖动到对应位置,释放小图,向后台发送请求,也就是把对应的x,y的坐标值发送给后台,这里我就不结合我们的项目的业务了,大概就是这样的。
2.分为pc站和m站,这里首先想到的就是pc站的图片拖拽我这里用的是mouse事件(做好浏览器的兼容),m站呢用touch事件,(注意取消到浏览器的默认行为),
3.我想知道为什么不用那个极验验证码的呢,好吧,我也不知道,需要自己写
其中遇到了好多好多小问题,先把代码贴出来吧,当然也在网上参考了其他大神写的东西。
首先m站的
<template>
<div>
<div v-show="hongbaoShow" class="yd-mask" @touchmove="pop($event)" style="overflow-y:hidden"></div>
<div class="yd-popup-center" style="height:auto;" :class="{'yd-popup-show':hongbaoShow}">
<div
class="yd-popup-content"
id="content"
@touchmove="pop($event)"
style="border-radius:8px;"
>
<div style="position: relative;border:1px solid #000;border-radius:8px">
<!--绑定按下事件-->
<img id="app" :src="urlBig">
<img
@touchstart="down"
@touchmove.stop="move"
@touchend="end"
:src="urlSmall"
width="40"
height="40"
:style="{'position':absolute,'top':top+'px','left':left+'px'}"
id="appsmall"
>
<slot></slot>
<div style="">
<span class="range">拖动滑块以完成验证</span>
<span class="range" style="float:right" @click="refresh()">刷新</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import cusPopup from "@components/public/popup";
import { consts } from "@config/consts";
import { getslider } from "@service/getData";
export default {
data() {
return {
positionX: 0,
positionY: 0,
odiv: null,
disX: 0,
disY: 0,
f1: null,
flag: false,
absolute: "absolute",
top: 0,
left: 0,
urlSmall: "", //小图url
urlBig: "",
flags: false,
position: { x: 0, y: 0 },
nx: "",
ny: "",
dx: "",
dy: "",
xPum: "",
yPum: "",
limitLeft: 100,
limitTop: 140
};
},
props: {
callback: {
type: Function,
default() {
return () => {
console.log("enterback");
};
}
},
type: {
type: String,
default: "phone" //默认手机号码登录
},
hongbaoShow: {
default: false
}
},
mounted() {
//禁止触发下拉刷新事件
this.$store.state.app.stopDrag = true;
document.body.style.overflow = 'hidden'
document.body.style.height = '100%'
this.getRandom();
this.getslider();
document.addEventListener(
"touchmove",
function() {
// 判断默认行为是否可以被禁用
if (event.cancelable) {
// 判断默认行为是否已经被禁用
if (!event.defaultPrevented) {
event.preventDefault();
}
}
},
false
);
},
created() {},
methods: {
getImgPath: consts.getImgPath,
refresh() {
this.getslider();
this.getRandom();
},
over() {
document.getElementById("appsmall").style.cursor = "move";
},
pop(event) {
if (event.cancelable) {
// 判断默认行为是否已经被禁用
if (!event.defaultPrevented) {
event.preventDefault();
}
}
},
//获取滑块验证图
async getslider() {
let res = await getslider();
if (res.code == 0) {
this.urlBig = res.data.image_data_big;
this.urlSmall = res.data.image_data_small;
}
},
//滑块位置随机
getRandom() {
this.left = Math.floor(Math.random() * this.limitLeft + 1);
this.top = Math.floor(Math.random() * this.limitTop + 1);
},
// 实现移动端拖拽
down() {
this.flags = true;
var touch;
if (event.touches) {
touch = event.touches[0];
} else {
touch = event;
}
this.position.x = touch.clientX;
this.position.y = touch.clientY;
this.dx = document.getElementById("appsmall").offsetLeft;
this.dy = document.getElementById("appsmall").offsetTop;
},
move() {
if (this.flags) {
var touch;
document.body.style.overflow = 'hidden'
document.body.style.height = '100%'
if (event.touches) {
touch = event.touches[0];
} else {
touch = event;
}
this.nx = touch.clientX - this.position.x;
this.ny = touch.clientY - this.position.y;
this.xPum = this.dx + this.nx;
this.yPum = this.dy + this.ny;
//判断不能超出便界
if (this.xPum < 0) {
this.xPum = 0;
}
if (this.xPum > 320 - 40) {
this.xPum = 280;
}
if (this.yPum < 0) {
this.yPum = 0;
}
if (this.yPum > 140) {
this.yPum = 140;
}
document.getElementById("appsmall").style.left = this.xPum + "px";
document.getElementById("appsmall").style.top = this.yPum + "px";
//阻止页面的滑动默认事件;如果碰到滑动问题,1.2 请注意是否获取到 touchmove
document.addEventListener(
"touchmove",
function() {
if (event.cancelable) {
// 判断默认行为是否已经被禁用
if (!event.defaultPrevented) {
event.preventDefault();
}
}
},
false
);
}
},
//鼠标释放时候的函数
end() {
this.flags = false;
let data = {
disX: this.xPum,
disY: this.yPum,
geturl: this.getslider()
};
this.callback(data);
this.getRandom();
}
}
};
</script>
<style>
#app {
width: 100%;
height: 180px;
/* background: #666; */
/* position: absolute; */
top: 0;
cursor: pointer;
}
body {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
html {
overflow: hidden;
}
.yd-mask {
pointer-events: auto !important;
}
#codeImg {
/*定位*/
top: 10px;
left: 10px;
}
.range {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #888888;
letter-spacing: 0.12px;
text-align: center;
}
#appsmall {
touch-action: none;
filter: drop-shadow(1px 1px 3px #000) drop-shadow(1px 1px 3px)
drop-shadow(1px 1px 3px);
-webkit-filter: drop-shadow(1px 1px 3px) drop-shadow(1px 1px 3px)
drop-shadow(1px 1px 3px);
}
.yd-popup-center {
width: 100%;
}
@media screen and (max-width: 320px) {
.yd-popup-center {
width: 100% !important;
}
}
@media screen and (min-width: 320px) and (max-width: 359px) {
.yd-popup-center {
width: 320px !important;
}
}
@media screen and (min-width: 360px) and (max-width: 374px) {
.yd-popup-center {
width: 320px !important;
}
}
@media screen and (min-width: 375px) and (max-width: 385px) {
.yd-popup-center {
width: 320px !important;
}
}
@media screen and (min-width: 386px) and (max-width: 392px) {
.yd-popup-center {
width: 320px !important;
}
}
@media screen and (min-width: 393px) and (max-width: 400px) {
.yd-popup-center {
width: 320px !important;
}
}
@media screen and (min-width: 401px) and (max-width: 414px) {
.yd-popup-center {
width: 320px !important;
}
}
@media screen and (min-width: 750px) and (max-width: 799px) {
.yd-popup-center {
width: 320px !important;
}
}
</style>
<style lang="less">
& .hongbao-popup {
& .yd-popup-content {
background: transparent !important;
& .hongbao-content {
height: 100%;
& > .hb-close {
position: absolute;
top: 5px;
right: 7px;
}
& > .hb-context {
padding: 150px 48px 0;
color: #333333;
& > .hb-datetime {
font-size: 15px;
border-bottom: 1px dashed #333333;
padding-bottom: 10px;
}
& > .hb-content {
font-size: 15px;
line-height: 1.6;
padding: 10px 0;
}
& > .hb-notes {
font-size: 13px;
}
}
}
}
}
</style>
这是自己封装好的一个组件,这里的弹窗用到了ydui的一个弹窗组件,popup弹窗。。也可以自己写弹窗。
这里解释其中几点:
A:
这里只回传touchend的回调函数,避免用$emit,尽可能做到在在使用组件的时候做到最简单
B:
注意这个样式,pointer-events这个属性是干嘛的,
所以啊,我用了弹窗之后,而这个弹窗恰巧人家封装的加上了这个属性,以至于我滑动滑块的时候就会触发下边的input的焦点触发事件。
基本上就是这些吧,现在直接粘贴出pc端的来吧
<template>
<div style="position: relative;">
<!--绑定按下事件-->
<img id="app" :src="urlBig">
<img
:src="urlSmall"
width="40"
height="40"
id="appsmall" :style="{'position':absolute,'top':top+'px','left':left+'px'}" @mouseover="over"
@mousedown="move"
>
<div style="margin-top:8px">
<span class="range">拖动滑块以完成验证</span>
<span class="range" style="float:right" @click="refresh()">刷新</span>
<p style="text-align:center;color:red">{{tipmes}}</p>
</div>
</div>
</template>
<script>
import myDialog from "@components/public/dialog";
import { consts } from "@config/consts";
import { getslider } from "@service/getData";
export default {
data() {
return {
positionX: 0,
positionY: 0,
odiv: null,
disX: 0,
disY: 0,
f1: null,
flag: false,
absolute: "absolute",
top: 0,
left: 0,
urlSmall: "", //小图url
urlBig: "",
limitLeft:100,
limitTop:140
};
},
props: {
callback: {
type: Function,
default() {
return () => {
console.log("enterback");
};
}
},
type: {
type: String,
default: "phone" //默认手机号码登录
},
tipmes:{
type:String
}
},
mounted() {
this.getRandom();
this.getslider();
},
created() {},
methods: {
getImgPath: consts.getImgPath,
refresh() {
this.getslider();
this.getRandom();
},
over() {
document.getElementById("appsmall").style.cursor = "move";
},
//获取滑块验证图
async getslider() {
let res = await getslider();
if (res.code == 0) {
this.urlBig = res.data.image_data_big;
this.urlSmall = res.data.image_data_small;
}
},
//滑块位置随机
getRandom() {
this.left = Math.floor(Math.random() * this.limitLeft + 1);
this.top = Math.floor(Math.random() * this.limitTop + 1);
},
move(e) {
var e = e ? e : window.e;
this.odiv = e.target; //获取目标元素
//算出鼠标相对元素的位置
this.disX = e.clientX - this.odiv.offsetLeft;
this.disY = e.clientY - this.odiv.offsetTop;
//浏览器的兼容性
if (document.addEventListener) {
let that = this;
e.preventDefault && e.preventDefault();
let f1 = function bodyScroll(e) {
that.init(e);
};
let f2 = function removeScroll(e) {
let data = {
disX: that.positionX,
disY: that.positionY,
geturl: this.tipmes == '校验成功!' ? '' : that.getslider()
};
that.callback(data);
that.getRandom();
document.removeEventListener("mousemove", f1, false);
document.removeEventListener("mouseup", f2, false);
};
document.addEventListener("mousemove", f1, false);
document.addEventListener("mouseup", f2, false);
} else if (document.attachEvent) {
let that = this;
e.preventDefault && e.preventDefault();
let f1 = function bodyScroll(e) {
that.init(e);
that.flag = true;
};
let f2 = function removeScroll(e) {
let data = {
disX: that.positionX,
disY: that.positionY,
geturl: that.getslider()
};
that.callback(data);
that.getRandom();
document.detachEvent("onmousemove", f1, false);
document.detachEvent("onmouseup", f2, false);
};
document.addEventListener("onmousemove", f1, false);
document.addEventListener("onmouseup", f2, false);
} else {
let that = this;
document.onmousemove = e => {
that.init(e);
};
document.onmouseup = e => {
let data = {
disX: that.positionX,
disY: that.positionY,
geturl: that.getslider()
};
that.callback(data);
that.getRandom();
that.initdown();
};
}
},
init(e) {
//用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - this.disX;
let top = e.clientY - this.disY;
//判断不能超出便界
if (left < 0) {
left = 0;
}
if (left > 320 - 40) {
left = 280;
}
if (top < 0) {
top = 0;
}
if (top > 140) {
top = 140;
}
//绑定元素位置到positionX和positionY上面
this.positionX = top;
this.positionY = left;
//移动当前元素
this.odiv.style.left = left + "px";
this.odiv.style.top = top + "px";
},
initdown(e) {
document.onmousemove = null;
document.onmouseup = null;
console.log(this.disX);
console.log(this.disY);
}
}
};
</script>
<style>
#app {
width: 320px;
height: 180px;
/* background: #666; */
/* position: absolute; */
top: 0;
cursor: pointer;
}
#codeImg {
/*定位*/
top: 10px;
left: 10px;
}
.range {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #888888;
letter-spacing: 0.12px;
text-align: center;
}
#appsmall{
filter: drop-shadow(1px 1px 3px #000) drop-shadow(1px 1px 3px) drop-shadow(1px 1px 3px);-webkit-filter: drop-shadow(1px 1px 3px) drop-shadow(1px 1px 3px) drop-shadow(1px 1px 3px);
}
</style>