滑动的时候不显示滑动块
<template>
<div class="yylp-slide-verify" :style="'width:' + w + 'px'" id="slideVerify" onselectstart="return false;">
<!-- 图片加载遮蔽罩 -->
<div :class="{ 'slider-verify-loading': loadBlock }"></div>
<canvas :style="{ width: w + 'px', height: h + 'px' }" type="2d" canvas-id="yylp-slide-canvas" ref="canvas"></canvas>
<div v-if="show" @click="refresh" class="slide-verify-refresh-icon"><text class="refresh_icon yylp yylp-icon_refresh"></text></div>
<canvas :style="{ width: w + 'px', height: h + 'px' }" type="2d" canvas-id="yylp-slide-block" ref="block" class="slide-verify-block"></canvas>
<!-- container -->
<div class="slide-verify-slider" :class="{ 'container-active': containerActive, 'container-success': containerSuccess, 'container-fail': containerFail }">
<div class="slide-verify-slider-mask" :style="{ width: sliderMaskWidth }">
<!-- slider -->
<div
@mousedown="sliderDown"
@touchstart="touchStartEvent"
@touchmove="touchMoveEvent"
@touchend="touchEndEvent"
class="slide-verify-slider-mask-item"
:style="{ left: sliderLeft }"
>
<text
class="slide_icon yylp"
:class="{ 'yylp-you-2': !containerSuccess && !containerFail, 'yylp-': containerSuccess, 'yylp-cuo': containerFail }"
></text>
</div>
</div>
<span class="slide-verify-slider-text">{{ sliderText }}</span>
</div>
</div>
</template>
<script>
const PI = Math.PI;
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,
default: 310
},
// canvas height
h: {
type: Number,
default: 155
},
sliderText: {
type: String,
default: '向右滑动>'
},
accuracy: {
type: Number,
default: 5 // 若为 -1 则不进行机器判断
},
show: {
type: Boolean,
default: true
},
imgs: {
type: Array,
default: () => ['img0.jpg', 'img1.jpg', 'img2.jpg', 'img3.jpg', 'img4.jpg', 'img5.jpg']
}
},
data() {
return {
containerActive: false, // container active class
containerSuccess: false, // container success class
containerFail: false, // container fail class
canvasCtx: null,
blockCtx: null,
block: null,
block_x: undefined, // container random position
block_y: undefined,
L: this.l + this.r * 2 + 3, // block real lenght
img: undefined,
originX: undefined,
originY: undefined,
isMouseDown: false,
trail: [],
sliderLeft: 0, // block right offset
sliderMaskWidth: 0, // mask width,
success: false, // Bug Fixes 修复了验证成功后还能滑动
loadBlock: true, // Features 图片加载提示,防止图片没加载完就开始验证
timestamp: null
};
},
mounted() {
this.init();
},
methods: {
init() {
this.initDom();
setTimeout(() => {
this.initImg();
this.bindEvents();
}, 500);
},
initDom() {
this.block = this.$refs.block.$el;
this.canvasCtx = uni.createCanvasContext('yylp-slide-canvas', this);
this.blockCtx = uni.createCanvasContext('yylp-slide-block', this);
},
initImg() {
let that = this;
const img = that.createImg(() => {
// 图片加载完关闭遮蔽罩
that.loadBlock = false;
that.drawBlock();
that.canvasCtx.drawImage(that.img.src, 0, 0, that.w, that.h);
that.blockCtx.drawImage(that.img.src, 0, 0, that.w, that.h);
that.canvasCtx.draw();
that.blockCtx.draw();
let { block_x: x, block_y: y, r, L } = that;
let _y = y - r * 2 - 1;
that.$nextTick(function() {
uni.canvasGetImageData(
{
canvasId: 'yylp-slide-block',
x: x,
y: _y,
width: L,
height: L,
success(ImageData) {
console.log('复制图片成功');
that.block.style.width = L + 'px';
uni.canvasPutImageData(
{
canvasId: 'yylp-slide-block',
x: 0,
y: _y,
data: ImageData.data,
width: L,
success(res) {
console.log('设置图片成功', res);
},
fail(err) {
console.log('设置图片错误', err);
}
},
that
);
},
fail(err) {
console.log('复制图片错误', err);
}
},
that
);
});
});
that.img = img;
},
drawBlock() {
this.block_x = this.getRandomNumberByRange(this.L + 10, this.w - (this.L + 10));
this.block_y = this.getRandomNumberByRange(10 + this.r * 2, this.h - (this.L + 10));
this.draw(this.canvasCtx, this.block_x, this.block_y, 'fill');
this.draw(this.blockCtx, this.block_x, this.block_y, 'clip');
},
draw(ctx, x, y, operation) {
let { l, r } = this;
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.7)';
ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
ctx.stroke();
ctx[operation]();
// Bug Fixes 修复了火狐和ie显示问题
ctx.globalCompositeOperation = 'destination-over';
},
createImg(onload) {
const img = document.createElement('img');
img.crossOrigin = 'Anonymous';
img.onload = onload;
img.onerror = () => {
img.src = this.getRandomImg();
};
img.src = this.getRandomImg();
return img;
},
// 随机生成img src
getRandomImg() {
const len = this.imgs.length;
const img_src = this.imgs[this.getRandomNumberByRange(0, len - 1)];
if (img_src) {
return require('@/uni_modules/yylp-ui/static/images/slide_verify/' + img_src);
}
},
getRandomNumberByRange(start, end) {
return Math.round(Math.random() * (end - start) + start);
},
refresh() {
console.log('刷新验证码!');
this.reset();
this.$emit('action_change', { action: 'refresh', msg: '刷新校验' });
},
sliderDown(event) {
if (this.success) return;
this.originX = event.clientX;
this.originY = event.clientY;
this.isMouseDown = true;
this.timestamp = +new Date();
},
touchStartEvent(e) {
if (this.success) return;
this.originX = e.changedTouches[0].pageX;
this.originY = e.changedTouches[0].pageY;
this.isMouseDown = true;
this.timestamp = +new Date();
},
bindEvents() {
document.addEventListener('mousemove', e => {
if (!this.isMouseDown) return false;
const moveX = e.clientX - this.originX;
const moveY = e.clientY - this.originY;
if (moveX < 0 || moveX + 38 >= this.w) return false;
this.sliderLeft = moveX + 'px';
let blockLeft = ((this.w - 40 - 20) / (this.w - 40)) * moveX;
this.block.style.left = blockLeft + 'px';
this.containerActive = true; // add active
this.sliderMaskWidth = moveX + 'px';
this.trail.push(moveY);
});
document.addEventListener('mouseup', e => {
if (!this.isMouseDown) return false;
this.isMouseDown = false;
if (e.clientX === this.originX) return false;
this.containerActive = false; // remove active
this.timestamp = +new Date() - this.timestamp;
const { spliced, TuringTest } = this.verify();
if (spliced) {
if (this.accuracy === -1) {
this.containerSuccess = true;
this.success = true;
this.$emit('action_change', { action: 'success', time: this.timestamp, msg: '校验成功' });
return;
}
if (TuringTest) {
// succ
this.containerSuccess = true;
this.success = true;
this.$emit('action_change', { action: 'success', time: this.timestamp, msg: '校验成功' });
} else {
this.containerFail = true;
this.$emit('action_change', { action: 'again', msg: '检测到非人为操作的' });
}
} else {
this.containerFail = true;
this.$emit('action_change', { action: 'fail', msg: '校验失败!' });
setTimeout(() => {
this.reset();
}, 1000);
}
});
},
touchMoveEvent(e) {
if (!this.isMouseDown) return false;
const moveX = e.changedTouches[0].pageX - this.originX;
const moveY = e.changedTouches[0].pageY - this.originY;
if (moveX < 0 || moveX + 38 >= this.w) return false;
this.sliderLeft = moveX + 'px';
let blockLeft = ((this.w - 40 - 20) / (this.w - 40)) * moveX;
this.block.style.left = blockLeft + 'px';
this.containerActive = true;
this.sliderMaskWidth = moveX + 'px';
this.trail.push(moveY);
},
touchEndEvent(e) {
if (!this.isMouseDown) return false;
this.isMouseDown = false;
if (e.changedTouches[0].pageX === this.originX) return false;
this.containerActive = false;
this.timestamp = +new Date() - this.timestamp;
const { spliced, TuringTest } = this.verify();
if (spliced) {
if (this.accuracy === -1) {
this.containerSuccess = true;
this.success = true;
this.$emit('action_change', { action: 'success', time: this.timestamp, msg: '校验成功' });
return;
}
if (TuringTest) {
// succ
this.containerSuccess = true;
this.success = true;
this.$emit('action_change', { action: 'success', time: this.timestamp, msg: '校验成功' });
} else {
this.containerFail = true;
this.$emit('action_change', { action: 'again', msg: '检测到非人为操作的' });
}
} else {
this.containerFail = true;
this.$emit('action_change', { action: 'fail', msg: '校验失败!' });
setTimeout(() => {
this.reset();
}, 1000);
}
},
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.block.style.left);
const accuracy = this.accuracy <= 1 ? 1 : this.accuracy > 10 ? 10 : this.accuracy;
return {
spliced: Math.abs(left - this.block_x) <= accuracy,
TuringTest: average !== stddev // equal => not person operate
};
},
reset() {
this.success = false;
this.containerActive = false;
this.containerSuccess = false;
this.containerFail = false;
this.sliderLeft = 0;
this.block.style.left = 0;
this.sliderMaskWidth = 0;
// canvas
let { w, h } = this;
this.canvasCtx.clearRect(0, 0, w, h);
this.blockCtx.clearRect(0, 0, w, h);
this.block.style.width = w + 'px';
this.canvasCtx.draw();
this.blockCtx.draw();
// generate img
this.img.src = this.getRandomImg();
this.$emit('action_change', { action: 'fulfilled', msg: '刷新校验完成' });
}
}
};
</script>
<style lang="scss">
.yylp-slide-verify {
position: relative;
margin: 0rpx auto;
background-color: #ffffff;
/* 图片加载样式 */
.slider-verify-loading {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
z-index: 999;
animation: loading 1.5s infinite;
}
@keyframes loading {
0% {
opacity: 0.7;
}
100% {
opacity: 9;
}
}
.slide-verify-block {
position: absolute;
left: 0;
top: 0;
}
.slide-verify-refresh-icon {
position: absolute;
right: 10rpx;
top: 10rpx;
z-index: 2;
.refresh_icon {
font-size: 56rpx;
color: #666666;
text-shadow: 2rpx 2rpx 2rpx #ffffff;
}
}
.slide-verify-slider {
position: relative;
text-align: center;
width: 100%;
height: 40px;
line-height: 40px;
margin-top: 15px;
background: #f7f9fa;
color: #45494c;
border: 1px solid #e4e7eb;
.slide_icon {
font-size: 56rpx;
}
}
.slide-verify-slider-mask {
position: absolute;
left: 0;
top: 0;
height: 40px;
border: 0 solid #1991fa;
background: #d1e9fe;
}
.slide-verify-slider-mask-item {
position: absolute;
top: 0;
left: 0;
width: 40px;
height: 40px;
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-color: #1991fa;
.slide_icon {
color: #ffffff;
}
}
.container-active {
.slide-verify-slider-mask-item {
height: 38px;
top: -1px;
border: 1px solid #1991fa;
}
.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;
}
.slide-verify-slider-mask {
height: 38px;
border: 1px solid #52ccba;
background-color: #d2f4ef;
}
.slide_icon {
color: #ffffff;
}
}
.container-fail {
.slide-verify-slider-mask-item {
height: 38px;
top: -1px;
border: 1px solid #f57a7a;
background-color: #f57a7a !important;
}
.slide-verify-slider-mask {
height: 38px;
border: 1px solid #f57a7a;
background-color: #fce1e1;
}
.slide_icon {
color: #ffffff;
}
}
.container-active .slide-verify-slider-text,
.container-success .slide-verify-slider-text,
.container-fail .slide-verify-slider-text {
display: none;
}
}
</style>
0 个回复