vue 滑动拼图验证_ico

vue 滑动拼图验证_滑块_02

vue 滑动拼图验证_滑块_03

目录

​1. 新建文件 dragVerifyImgChip.vue ,复制粘贴下方代码​

​2. 在任意vue文件中使用​

 

使用方法 

​ 1. 新建文件 dragVerifyImgChip.vue ,复制粘贴下方代码​

<template>
<div class="drag-verify-container">

<div :style="dragVerifyImgStyle">
<img ref="checkImg" :src="imgsrc" @load="checkimgLoaded" style="width:100%">
<canvas ref="maincanvas" class="main-canvas"></canvas>
<canvas ref="movecanvas" :style="movecanvasStyle" :class="{goFirst:isOk, goKeep:isKeep}"></canvas>
<div class="refresh" v-if="showRefresh && !isPassing">
<i :class="refreshIcon" @click="refreshimg"></i>
</div>
<div class="tips success" v-if="showTips && isPassing">{{successTip}}</div>
<div class="tips danger" v-if="showTips && !isPassing && showErrorTip">{{failTip}}</div>
</div>
<div
ref="dragVerify"
class="drag_verify"
:style="dragVerifyStyle"
@mousemove="dragMoving"
@touchmove="dragMoving"
@mouseup="dragFinish"
@mouseleave="dragFinish"
@touchend="dragFinish"
>

<div
class="dv_progress_bar"
:class="{goFirst2:isOk}"
ref="progressBar"
:style="progressBarStyle"
>
{{isPassing ? successText : ""}}
</div>

{{isPassing ? "" : text}}

<div
class="dv_handler dv_handler_bg"
:class="{goFirst:isOk}"
@mousedown="dragStart"
@touchstart="dragStart"
ref="handler"
:style="handlerStyle"
>
<i :class="handlerIcon"></i>
</div>
</div>
</div>
</template>
<script>
export default {
name: "dragVerifyImgChip",
props: {
width: {
type: Number,
default: 250
},
height: {
type: Number,
default: 40
},
text: {
type: String,
default: "请按住滑块向右拖动"
},
successText: {
type: String,
default: "验证通过"
},
background: {
type: String,
default: "#eee"
},
progressBarBg: {
type: String,
default: "#76c61d"
},
completedBg: {
type: String,
default: "#76c61d"
},
handlerIcon: {
type: String,
default: "el-icon-d-arrow-right"
},
successIcon: {
type: String,
default: "el-icon-circle-check"
},
handlerBg: {
type: String,
default: "#fff"
},
textSize: {
type: String,
default: "14px"
},
textColor: {
type: String,
default: "#333"
},
imgsrc: {
type: String,
default: "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1593427296359&di=71d5f2b15199ed5389546528b1c61a83&imgtype=0&src=http%3A%2F%2Fphotocdn.sohu.com%2F20110814%2FImg316287483.jpg"
},
barWidth: {
type: Number,
default: 40
},
barRadius: {
type: Number,
default: 8
},
showRefresh: {
type: Boolean,
default: false
},
refreshIcon: {
type: String,
default: "el-icon-refresh white"
},
showTips: {
type: Boolean,
default: true
},
failTip: {
type: String,
default: "验证失败,请重试"
},
diffWidth: {
type: Number,
default: 2
}
},
mounted: function () {
const dragEl = this.$refs.dragVerify;
dragEl.style.setProperty("--textColor", this.textColor);
dragEl.style.setProperty("--width", Math.floor(this.width / 2) + "px");
dragEl.style.setProperty("--pwidth", -Math.floor(this.width / 2) + "px");
},
computed: {
movecanvasStyle: function () {
return {
left: -this.clipBarx + "px ",
position: 'absolute',
top: 0
};
},
handlerStyle: function () {
return {
left: "0px",
width: this.height + "px",
height: this.height + "px",
background: this.handlerBg
};
},
dragVerifyStyle: function () {
return {
width: this.width + "px",
height: this.height + "px",
lineHeight: this.height + "px",
background: this.background,

};
},
dragVerifyImgStyle: function () {
return {
width: this.width + "px",
position: 'relative',
overflow: 'hidden'
};
},
progressBarStyle: function () {
return {
background: this.progressBarBg,
height: this.height + "px",
color: "white"
};
},

},
data() {
return {
successTip: '',
beginTime: 0,
endTime: 0,
isPassing: false,
isMoving: false,
x: 0,
isOk: false,
isKeep: false,
clipBarx: 0,
showErrorTip: false
};
},
methods: {
draw: function (ctx, x, y, operation) {
let l = this.barWidth;
let r = this.barRadius;
const PI = Math.PI
ctx.beginPath()
ctx.moveTo(x, y)
ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI)
ctx.lineTo(x + l, y)
ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI)
ctx.lineTo(x + l, y + l)
ctx.lineTo(x, y + l)
ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true)
ctx.lineTo(x, y)
ctx.lineWidth = 2
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'
ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)'
ctx.stroke()
ctx[operation]()
ctx.globalCompositeOperation = 'destination-over'
},
checkimgLoaded: function () {
// 生成图片缺失位置
let barWidth = this.barWidth;
let imgHeight = this.$refs.checkImg.height;
let imgWidth = this.$refs.checkImg.width;
let halfWidth = Math.floor(this.width / 2);
let refreshHeigth = 25;
let tipHeight = 20;
let x = halfWidth + Math.ceil(Math.random() * (halfWidth - barWidth));
let y = refreshHeigth + Math.floor(Math.random() * (imgHeight - barWidth - refreshHeigth - tipHeight));
this.$refs.maincanvas.setAttribute('width', imgWidth)
this.$refs.maincanvas.setAttribute('height', imgHeight)
this.$refs.maincanvas.style.display = 'block'
let canvasCtx = this.$refs.maincanvas.getContext('2d')
this.draw(canvasCtx, x, y, 'fill');
this.clipBarx = x;

let moveCanvas = this.$refs.movecanvas;
moveCanvas.setAttribute('width', imgWidth)
this.$refs.movecanvas.style.display = 'block'
const L = barWidth + this.barRadius * 2 + 3; //实际宽度
let moveCtx = this.$refs.movecanvas.getContext('2d')
moveCtx.clearRect(0, 0, imgWidth, imgHeight)
this.draw(moveCtx, x, y, 'clip');
moveCtx.drawImage(this.$refs.checkImg, 0, 0, imgWidth, imgHeight)
y = y - this.barRadius * 2 - 1;
const ImageData = moveCtx.getImageData(x, y, L, L)
moveCanvas.setAttribute('width', L)
moveCanvas.setAttribute('height', imgHeight)
moveCtx.putImageData(ImageData, 0, y)
},
dragStart: function (e) {
this.beginTime = new Date().getTime()
if (!this.isPassing) {
this.isMoving = true;
let handler = this.$refs.handler;
this.x =
(e.pageX || e.touches[0].pageX) -
parseInt(handler.style.left.replace("px", ""), 10);
}
this.showErrorTip = false;
this.$emit("handlerMove");
},
dragMoving: function (e) {
if (this.isMoving && !this.isPassing) {
let _x = (e.pageX || e.touches[0].pageX) - this.x;

let handler = this.$refs.handler;
handler.style.left = _x + "px";
this.$refs.progressBar.style.width = _x + this.height / 2 + "px";
this.$refs.movecanvas.style.left = _x - this.clipBarx + "px";
}
},
dragFinish: function (e) {
if (this.isMoving && !this.isPassing) {
let _x = (e.pageX || e.changedTouches[0].pageX) - this.x;
if (Math.abs(_x - this.clipBarx) > this.diffWidth) {
this.isOk = true;
let that = this;
setTimeout(function () {
that.$refs.handler.style.left = "0";
that.$refs.progressBar.style.width = "0";

that.$refs.movecanvas.style.left = - that.clipBarx + "px",
that.isOk = false;
}, 500);
this.showErrorTip = true;
} else {
this.passVerify();
}
this.isMoving = false;
}
},
passVerify: function () {
this.endTime = new Date().getTime()
this.successTip = "耗时" + (this.endTime - this.beginTime) / 1000 + 's'
this.isPassing = true
this.isMoving = false;
let handler = this.$refs.handler;
handler.children[0].className = this.successIcon;
this.$refs.progressBar.style.background = this.completedBg;
this.$refs.progressBar.style.color = "#fff";
this.$refs.progressBar.style.fontSize = this.textSize;
this.isKeep = true;
this.$refs.maincanvas.style.display = 'none'
this.$refs.movecanvas.style.display = 'none'
this.$emit("passcallback");
},
reset: function () {
this.reImg();
this.checkimgLoaded();
},
reImg: function () {
this.$emit("update:isPassing", false);
const oriData = this.$options.data();
for (const key in oriData) {
if (oriData.hasOwnProperty(key)) {
this.$set(this, key, oriData[key]);
}
}
let handler = this.$refs.handler;
handler.style.left = "0";
this.$refs.progressBar.style.width = "0";
handler.children[0].className = this.handlerIcon;
this.$refs.movecanvas.style.left = "0px";
},
refreshimg: function () {
this.$emit('refresh')
}
},
watch: {
imgsrc: {
immediate: false,
handler: function () {
this.reImg();
}
}
}
};
</script>
<style scoped>
.drag_verify {
position: relative;
background-color: #e8e8e8;
text-align: center;
overflow: hidden;
}

.drag_verify .dv_handler {
position: absolute;
top: 0px;
left: 0px;
cursor: move;
}

.drag_verify .dv_handler i {
color: #666;
padding-left: 0;
font-size: 16px;
}

.drag_verify .dv_handler .el-icon-circle-check {
color: #6c6;
margin-top: 9px;
}

.drag_verify .dv_progress_bar {
position: absolute;
height: 34px;
width: 0px;
}

.drag_verify .dv_text {
position: absolute;
top: 0px;
color: transparent;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
-o-user-select: none;
-ms-user-select: none;
background: -webkit-gradient(
linear,
left top,
right top,
color-stop(0, let(--textColor)),
color-stop(0.4, let(--textColor)),
color-stop(0.5, #fff),
color-stop(0.6, let(--textColor)),
color-stop(1, let(--textColor))
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-webkit-text-size-adjust: none;
animation: slidetounlock 3s infinite;
}

.drag_verify .dv_text * {
-webkit-text-fill-color: let(--textColor);
}

.goFirst {
transition: left 0.5s;
}

.goKeep {
transition: left 0.2s;
}

.goFirst2 {
width: 0px !important;
transition: width 0.5s;
}

.drag-verify-container {
position: relative;
line-height: 0;
}

.refresh {
position: absolute;
right: 5px;
top: 5px;
cursor: pointer;
font-size: 20px;
z-index: 200;
}

.tips {
position: absolute;
bottom: 0;
height: 20px;
line-height: 20px;
text-align: center;
width: 100%;
font-size: 12px;
z-index: 200;
}

.tips.success {
background: rgba(255, 255, 255, 0.6);
color: green;
}

.tips.danger {
background: rgba(0, 0, 0, 0.6);
color: yellow;
}

.main-canvas {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}

.white {
color: white;
}

@-webkit-keyframes slidetounlock {
0% {
background-position: let(--pwidth) 0;
}
100% {
background-position: let(--width) 0;
}
}

@-webkit-keyframes slidetounlock2 {
0% {
background-position: let(--pwidth) 0;
}
100% {
background-position: let(--pwidth) 0;
}
}
</style>

2. 在任意vue文件中使用

<drag-verify-img-chip @passcallback="pass" />
import dragVerifyImgChip from "@/components/utils/verify/dragVerifyImgChip";
components: {dragVerifyImgChip},
pass() {
alert("验证通过!")
},

参数

width

宽度

number

-

250

height

高度

number

-

40

text

初始文字

string

-

swiping to the right side

successText

成功提示文字

string

-

success

background

滑块右侧背景色

string

#eee / red / rgba(52,52,52,0.7)

#eee

progressBarBg

滑块左侧背景色

string

#76c61d / white / rgba(52,52,52,0.7)

#76c61d

handlerBg

滑块背景色

string

#fff / white / rgb(255,255,255)

#fff

completedBg

验证通过背景色

string

#76c61d / white / rgba(52,52,52,0.7)

#76c61d

circle

两侧是否圆形

boolean

true / false

false

handlerIcon

滑块未验证通过时的图标,根据所选框架设置不同class

string

el-icon-d-arrow-right

-

successIcon

滑块验证通过时的图标,根据所选框架设置不同class

string

el-icon-circle-check

-

textSize

文字大小

string

14px / 4em

14px

textColor

文字颜色

string

#333 / gray / rgb(52,52,52)

#333

imgsrc

图片地址

string

-

-

barWidth

拼图宽度,同拼图高度

number

40

40

barRadius

拼图外部圆形半径

number

8

8

showRefresh

是否右上角显示刷新

boolean

true / false

false

refreshIcon

刷新按钮图标的class

string

el-icon-refresh-right

-

showTips

是否显示底部提示

boolean

true / false

true

failTip

底部验证失败提示

string

验证失败

验证失败,请重试

diffWidth

在此范围内松开计算验证成功(单位px)

number

10

2

事件

passcallback

验证通过回调