登陆加入拼图验证

master
toesbieya 6 years ago
parent 1d0fd1032b
commit f323fc3546
  1. 0
      vue/src/assets/images/403.gif
  2. 0
      vue/src/assets/images/404.png
  3. 0
      vue/src/assets/images/404_cloud.png
  4. BIN
      vue/src/assets/images/sprite.1.2.4.png
  5. 218
      vue/src/assets/styles/transition.scss
  6. 20
      vue/src/components/PuzzleVerify/index.js
  7. 370
      vue/src/components/PuzzleVerify/index.vue
  8. 519
      vue/src/components/PuzzleVerify/main.vue
  9. 2
      vue/src/main.js
  10. 6
      vue/src/router/modules/example.js
  11. 2
      vue/src/views/app/403.vue
  12. 8
      vue/src/views/app/404.vue
  13. 8
      vue/src/views/app/login.vue
  14. 20
      vue/src/views/example/components/verify.vue

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -1,3 +1,5 @@
$transition-easing: cubic-bezier(0.215, 0.61, 0.355, 1);
.right-out, .right-out,
.left-out { .left-out {
&-leave-active, &-leave-active,
@ -30,15 +32,46 @@
} }
} }
.breadcrumb-enter-active,
.breadcrumb-leave-active,
.breadcrumb-move {
transition: all .5s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-leave-active {
position: absolute;
}
.slide-right, .slide-right,
.slide-left { .slide-left,
&-enter-active, .slide-up,
.slide-down,
.jump-right,
.jump-left,
.jump-up,
.jump-down,
.fade,
.scale,
.rotate,
.flip {
&-leave-active { &-leave-active {
transition: transform .3s cubic-bezier(0.215, 0.61, 0.355, 1) position: absolute
}
} }
.slide-right,
.slide-left,
.slide-up,
.slide-down {
&-enter-active,
&-leave-active { &-leave-active {
position: absolute; transition: transform .3s $transition-easing
} }
} }
@ -62,19 +95,178 @@
} }
} }
.slide-up {
&-enter {
transform: translate3d(0, 100%, 0)
}
.breadcrumb-enter-active, &-leave-to {
.breadcrumb-leave-active, transform: translate3d(0, -100%, 0)
.breadcrumb-move { }
transition: all .5s;
} }
.breadcrumb-enter, .slide-down {
.breadcrumb-leave-active { &-enter {
transform: translate3d(0, -100%, 0)
}
&-leave-to {
transform: translate3d(0, 100%, 0)
}
}
.jump-right,
.jump-left,
.jump-up,
.jump-down {
&-enter-active,
&-leave-active {
transition: opacity .3s, transform .3s
}
&-enter,
&-leave-to {
opacity: 0
}
}
.jump-right {
&-enter {
transform: translate3d(-15px, 0, 0)
}
&-leave-to {
transform: translate3d(15px, 0, 0)
}
}
.jump-left {
&-enter {
transform: translate3d(15px, 0, 0)
}
&-leave-to {
transform: translateX(-15px)
}
}
.jump-up {
&-enter {
transform: translate3d(0, 15px, 0)
}
&-leave-to {
transform: translate3d(0, -15px, 0)
}
}
.jump-down {
&-enter {
transform: translate3d(0, -15px, 0)
}
&-leave-to {
transform: translate3d(15px, 0, 0)
}
}
.fade {
&-enter-active,
&-leave-active {
transition: opacity .3s ease-out
}
&-enter,
&-leave,
&-leave-to {
opacity: 0
}
}
.scale {
&-enter-active,
&-leave-active {
transition: opacity .3s, transform .3s $transition-easing
}
&-enter,
&-leave,
&-leave-to {
opacity: 0; opacity: 0;
transform: translateX(20px); transform: scale3d(0, 0, 1)
}
} }
.breadcrumb-leave-active { .rotate {
position: absolute; &-enter-active,
&-leave-active {
transition: opacity .3s, transform .3s $transition-easing;
transform-style: preserve-3d
}
&-enter,
&-leave,
&-leave-to {
opacity: 0;
transform: scale3d(0, 0, 1) rotate3d(0, 0, 1, 90deg)
}
}
.flip-right,
.flip-left,
.flip-up,
.flip-down {
&-enter-active,
&-leave-active {
transition: transform .3s;
backface-visibility: hidden
}
&-enter-to,
&-leave {
transform: perspective(400px) rotate3d(1, 1, 0, 0deg)
}
}
.flip-right {
&-enter {
transform: perspective(400px) rotate3d(0, 1, 0, -180deg)
}
&-leave-to {
transform: perspective(400px) rotate3d(0, 1, 0, 180deg)
}
}
.flip-left {
&-enter {
transform: perspective(400px) rotate3d(0, 1, 0, 180deg)
}
&-leave-to {
transform: perspective(400px) rotate3d(0, 1, 0, -180deg)
}
}
.flip-up {
&-enter {
transform: perspective(400px) rotate3d(1, 0, 0, -180deg)
}
&-leave-to {
transform: perspective(400px) rotate3d(1, 0, 0, 180deg)
}
}
.flip-down {
&-enter {
transform: perspective(400px) rotate3d(1, 0, 0, 180deg)
}
&-leave-to {
transform: perspective(400px) rotate3d(1, 0, 0, -180deg)
}
} }

@ -0,0 +1,20 @@
import Vue from 'vue'
import Main from './main.vue'
import {PopupManager} from 'element-ui/src/utils/popup'
let Constructor = Vue.extend(Main)
const PuzzleVerify = function ({left = 0, top = 0} = {}) {
const instance = new Constructor({data: {positionLeft: left}})
instance.positionTop = top - Math.round((instance.height + 130) / 2)
instance.$mount()
document.body.appendChild(instance.$el)
instance.$el.style.zIndex = PopupManager.nextZIndex()
instance.visible = true
return new Promise((resolve, reject) => {
instance.resolve = resolve
instance.reject = reject
})
}
export default PuzzleVerify

@ -1,370 +0,0 @@
<template>
<div class="puzzle-container">
<div class="puzzle-header">
<span class="puzzle-header-title">拖动下方滑块完成拼图</span>
<div class="puzzle-header-tool">
<el-button @click="refreshImg" icon="el-icon-refresh" size="small" type="text"/>
<el-button @click="$emit('close')" icon="el-icon-close" size="small" type="text"/>
</div>
</div>
<div :style="'width:' + width + 'px'" class="puzzle-view">
<img :src="imgRandom"
:style="'width:' + width + 'px;height:' + height + 'px;'"
id="scream"
ref="scream"
/>
<canvas :height="height" :width="width" id="puzzle-box" ref="puzzleBox"/>
<div :style="'left:' + left_Num+ 'px'" class="puzzle-lost-box">
<canvas :height="height" :width="width" id="puzzle-shadow" ref="puzzleShadow"/>
<canvas :height="height" :width="width" id="puzzle-lost" ref="puzzleLost"/>
</div>
<span :class="{'hide':displayTips}" class="verify-result" ref="verTips">
<span class="success" v-show="verification">
<i class="el-icon-circle-check"/>验证通过
</span>
<span class="error" v-show="!verification">
<i class="el-icon-circle-close"/>验证失败
</span>
</span>
</div>
<div class="slider-container">
<div class="slider-bar"/>
<el-button @mousedown.native.prevent.stop="startMove"
circle
class="slider-btn"
icon="el-icon-rank"
ref="sliderBtn"
type="success"/>
</div>
</div>
</template>
<script>
/*
* https://github.com/Kevin-269581661/vue-puzzle-verification
* 网上搬的例子实际开发建议用极验
* */
export default {
name: "PuzzleVerify",
props: {
//
width: {
type: Number,
default: 260
},
height: {
type: Number,
default: 120
},
//
puzzleImgList: {
type: Array,
default: () => []
},
//
blockSize: {
type: Number,
default: 40
},
//
deviation: {
type: Number,
default: 4
},
//
padding: {
type: Number,
default: 20
}
},
data() {
return {
moveStart: null,
displayTips: false,
verification: false,
randomX: null,
randomY: null,
imgRandom: "",
left_Num: 0
}
},
methods: {
//
refreshImg() {
let imgRandomIndex = Math.round(Math.random() * (this.puzzleImgList.length - 1))
this.imgRandom = this.puzzleImgList[imgRandomIndex]
this.initCanvas()
},
//
initCanvas() {
this.clearCanvas()
let w = this.width
let h = this.height
let PL_Size = this.blockSize
let padding = this.padding
let MinN_X = padding + PL_Size
let MaxN_X = w - padding - PL_Size - PL_Size / 6
let MaxN_Y = padding
let MinN_Y = h - padding - PL_Size - PL_Size / 6
this.randomX = Math.round(Math.random() * (MaxN_X - PL_Size) + MinN_X)
this.randomY = Math.round(Math.random() * MaxN_Y + MinN_Y)
let X = this.randomX
let Y = this.randomY
this.left_Num = -X + 10
let d = PL_Size / 3
let c = this.$refs.puzzleBox
let c_l = this.$refs.puzzleLost
let c_s = this.$refs.puzzleShadow
let ctx = c.getContext("2d")
let ctx_l = c_l.getContext("2d")
let ctx_s = c_s.getContext("2d")
ctx.globalCompositeOperation = "xor"
ctx.shadowBlur = 10
ctx.shadowColor = "#fff"
ctx.shadowOffsetX = 3
ctx.shadowOffsetY = 3
ctx.fillStyle = "rgba(0,0,0,0.7)"
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = "rgba(0,0,0,0)"
ctx.moveTo(X, Y)
ctx.lineTo(X + d, Y)
ctx.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx.lineTo(X + 3 * d, Y)
ctx.lineTo(X + 3 * d, Y + d)
ctx.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx.lineTo(X + 3 * d, Y + 3 * d)
ctx.lineTo(X, Y + 3 * d)
ctx.closePath()
ctx.stroke()
ctx.fill()
let img = new Image()
img.src = this.imgRandom
img.onload = function () {
ctx_l.drawImage(img, 0, 0, w, h)
}
ctx_l.beginPath()
ctx_l.strokeStyle = "rgba(0,0,0,0)"
ctx_l.moveTo(X, Y)
ctx_l.lineTo(X + d, Y)
ctx_l.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx_l.lineTo(X + 3 * d, Y)
ctx_l.lineTo(X + 3 * d, Y + d)
ctx_l.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx_l.lineTo(X + 3 * d, Y + 3 * d)
ctx_l.lineTo(X, Y + 3 * d)
ctx_l.closePath()
ctx_l.stroke()
ctx_l.shadowBlur = 10
ctx_l.shadowColor = "black"
ctx_l.clip()
ctx_s.beginPath()
ctx_s.lineWidth = 1
ctx_s.strokeStyle = "rgba(0,0,0,0)"
ctx_s.moveTo(X, Y)
ctx_s.lineTo(X + d, Y)
ctx_s.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx_s.lineTo(X + 3 * d, Y)
ctx_s.lineTo(X + 3 * d, Y + d)
ctx_s.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx_s.lineTo(X + 3 * d, Y + 3 * d)
ctx_s.lineTo(X, Y + 3 * d)
ctx_s.closePath()
ctx_s.stroke()
ctx_s.shadowBlur = 20
ctx_s.shadowColor = "black"
ctx_s.fill()
},
//
clearCanvas() {
let c = this.$refs.puzzleBox
let c_l = this.$refs.puzzleLost
let c_s = this.$refs.puzzleShadow
c.setAttribute("height", c.getAttribute("height"))
c_l.setAttribute("height", c.getAttribute("height"))
c_s.setAttribute("height", c.getAttribute("height"))
},
//
startMove(e) {
this.moveStart = e.pageX
this.addMouseMoveListener()
},
//
moving(e) {
let moveX = e.pageX
let d = moveX - this.moveStart
if (this.moveStart === null
|| d < 0
|| d > this.width - this.padding - this.blockSize) return
this.$refs.sliderBtn.$el.style.left = d + "px"
this.$refs.sliderBtn.$el.style.transition = "inherit"
this.$refs.puzzleLost.style.left = d + "px"
this.$refs.puzzleLost.style.transition = "inherit"
this.$refs.puzzleShadow.style.left = d + "px"
this.$refs.puzzleShadow.style.transition = "inherit"
},
//
moveEnd(e) {
let moveEnd_X = e.pageX - this.moveStart
let ver_Num = this.randomX - 10
let Min_left = ver_Num - this.deviation
let Max_left = ver_Num + this.deviation
if (this.moveStart !== null) {
if (Max_left > moveEnd_X && moveEnd_X > Min_left) {
this.displayTips = true
this.verification = true
setTimeout(() => {
this.displayTips = false
this.initCanvas()
this.$emit('success')
}, 500)
}
else {
this.displayTips = true
this.verification = false
setTimeout(() => {
this.displayTips = false
this.initCanvas()
this.$emit('fail')
}, 800)
}
}
setTimeout(() => {
this.$refs.sliderBtn.$el.style.left = '0'
this.$refs.sliderBtn.$el.style.transition = "left 0.5s"
this.$refs.puzzleLost.style.left = '0'
this.$refs.puzzleLost.style.transition = "left 0.5s"
this.$refs.puzzleShadow.style.left = '0'
this.$refs.puzzleShadow.style.transition = "left 0.5s"
}, 400)
this.moveStart = null
this.removeMouseMoveListener()
},
//
addMouseMoveListener() {
document.addEventListener("mousemove", this.moving)
document.addEventListener("mouseup", this.moveEnd)
},
removeMouseMoveListener() {
document.removeEventListener("mousemove", this.moving)
document.removeEventListener("mouseup", this.moveEnd)
}
},
created() {
//
let imgRandomIndex = Math.round(Math.random() * (this.puzzleImgList.length - 1))
this.imgRandom = this.puzzleImgList[imgRandomIndex]
},
mounted() {
this.$nextTick(() => this.initCanvas())
},
}
</script>
<style lang="scss">
.puzzle-container {
position: relative;
display: inline-block;
padding: 0 15px 28px;
border: 1px solid #ddd;
background: #ffffff;
border-radius: 10px;
.puzzle-header {
display: flex;
align-items: center;
justify-content: space-between;
margin: 5px 0;
.puzzle-header-title {
font-size: 13px;
color: #909399
}
}
.puzzle-view {
position: relative;
overflow: hidden;
.puzzle-lost-box {
position: absolute;
left: 0;
top: 0;
}
canvas {
position: absolute;
left: 0;
top: 0;
z-index: 22;
}
.verify-result {
position: absolute;
left: 0;
bottom: -22px;
background: rgba(255, 255, 255, 1);
height: 22px;
line-height: 22px;
font-size: 12px;
width: 100%;
transition: bottom 0.4s;
.success {
color: #67C23A
}
.error {
color: #F56C6C
}
i {
margin: 0 10px;
font-weight: bold;
}
&.hide {
bottom: 0;
}
}
}
.slider-container {
position: relative;
margin: 10px auto 0;
min-height: 15px;
.slider-bar {
height: 10px;
border: 1px solid #c3c3c3;
border-radius: 5px;
background: #e4e4e4;
box-shadow: 0 1px 1px rgba(12, 10, 10, 0.2) inset;
position: relative;
top: 7px;
}
.slider-btn {
position: absolute;
width: 44px;
height: 44px;
left: 0;
top: -10px;
z-index: 12;
transition: inherit;
}
}
}
</style>

@ -0,0 +1,519 @@
<template>
<transition :name="transition" @after-leave="handleAfterLeave">
<div
v-show="visible"
class="puzzle-container"
:class="{'puzzle-shake':stat==='fail'}"
:style="`left: ${positionLeft}px;top: ${positionTop}px;`"
>
<div class="puzzle-view" :style="`width:${width}px;height:${height}px;`">
<div v-show="!image.loading" style="width: 100%;height: 100%">
<img :src="image.src"/>
<canvas ref="puzzleBox" :height="height" :width="width"/>
<div class="puzzle-lost-box" :style="`left:${leftNum}px`">
<canvas ref="puzzleShadow" :style="sliderStyle" :height="height" :width="width"/>
<canvas ref="puzzleLost" :style="sliderStyle" :height="height" :width="width"/>
</div>
</div>
<transition name="fade">
<div v-show="image.loading" class="puzzle-image-loading">
<div class="puzzle-image-loading__icon"/>
<div class="puzzle-image-loading__text">加载中...</div>
</div>
</transition>
<div :class="resultTipClass">
{{stat==='success'?'验证通过':'请正确拼合图像'}}
</div>
</div>
<div class="slider-container">
<div class="slider-track">
<div class="slider-tip" :style="stat==='ready'?'opacity: 1':''">拖动滑块完成拼图</div>
</div>
<div class="slider-btn" :style="sliderBtnStyle" @mousedown.prevent.stop="moveStart"/>
</div>
<div class="puzzle-footer">
<div class="puzzle-small">
<a class="puzzle-text-container puzzle-close" @click="close">
<div class="puzzle-text-tip">关闭验证</div>
</a>
<a class="puzzle-text-container puzzle-refresh" @click="refresh">
<div class="puzzle-text-tip">刷新验证</div>
</a>
</div>
</div>
</div>
</transition>
</template>
<script>
/*
* https://github.com/Kevin-269581661/vue-puzzle-verification
* 网上搬的例子实际开发建议用极验
* */
export default {
name: "PuzzleVerify",
props: {
//transition.scss
transition: {
type: String,
default: 'scale'
},
//
width: {
type: Number,
default: 260
},
height: {
type: Number,
default: 160
},
//
blockSize: {
type: Number,
default: 40
},
//
deviation: {
type: Number,
default: 4
},
//
padding: {
type: Number,
default: 80
}
},
data() {
return {
visible: false,
stat: 'ready',//ready,move,success,fail,error
positionLeft: 0,
positionTop: 0,
resolve: () => ({}),
reject: () => ({}),
moveStartAtX: null,
randomX: null,
randomY: null,
leftNum: 0,
image: {
src: '',
loading: true
},
sliderStyle: {
left: '0',
transition: 'inherit'
}
}
},
computed: {
resultTipClass() {
const showResultTip = ['success', 'fail'].includes(this.stat)
return ['verify-result', `verify-${this.stat}`, showResultTip ? 'show-result' : '']
},
sliderBtnStyle() {
return {
...this.sliderStyle,
'background-position': this.stat === 'move' ? '0 31.0992%' : '0 11.79625%'
}
}
},
methods: {
close() {
this.visible = false
this.clear()
this.reject()
},
refresh() {
this.initImage().then(() => this.initCanvas())
},
initCanvas() {
this.clear()
let MinX = this.padding + this.blockSize
let MaxX = this.width - this.padding - 2 * this.blockSize - this.blockSize / 6
let MaxY = this.padding
let MinY = this.height - this.padding - this.blockSize - this.blockSize / 6
this.randomX = Math.round(Math.random() * MaxX + MinX)
this.randomY = Math.round(Math.random() * MaxY + MinY)
let X = this.randomX
let Y = this.randomY
this.leftNum = -X + 10
let d = this.blockSize / 3
let c = this.$refs.puzzleBox
let c_l = this.$refs.puzzleLost
let c_s = this.$refs.puzzleShadow
let ctx = c.getContext("2d")
let ctx_l = c_l.getContext("2d")
let ctx_s = c_s.getContext("2d")
ctx.globalCompositeOperation = "xor"
ctx.shadowBlur = 10
ctx.shadowColor = "#fff"
ctx.shadowOffsetX = 3
ctx.shadowOffsetY = 3
ctx.fillStyle = "rgba(0,0,0,0.7)"
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = "rgba(0,0,0,0)"
ctx.moveTo(X, Y)
ctx.lineTo(X + d, Y)
ctx.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx.lineTo(X + 3 * d, Y)
ctx.lineTo(X + 3 * d, Y + d)
ctx.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx.lineTo(X + 3 * d, Y + 3 * d)
ctx.lineTo(X, Y + 3 * d)
ctx.closePath()
ctx.stroke()
ctx.fill()
this.drawImage(img => ctx_l.drawImage(img, 0, 0, this.width, this.height))
ctx_l.beginPath()
ctx_l.strokeStyle = "rgba(0,0,0,0)"
ctx_l.moveTo(X, Y)
ctx_l.lineTo(X + d, Y)
ctx_l.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx_l.lineTo(X + 3 * d, Y)
ctx_l.lineTo(X + 3 * d, Y + d)
ctx_l.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx_l.lineTo(X + 3 * d, Y + 3 * d)
ctx_l.lineTo(X, Y + 3 * d)
ctx_l.closePath()
ctx_l.stroke()
ctx_l.shadowBlur = 10
ctx_l.shadowColor = "black"
ctx_l.clip()
ctx_s.beginPath()
ctx_s.lineWidth = 1
ctx_s.strokeStyle = "rgba(0,0,0,0)"
ctx_s.moveTo(X, Y)
ctx_s.lineTo(X + d, Y)
ctx_s.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y)
ctx_s.lineTo(X + 3 * d, Y)
ctx_s.lineTo(X + 3 * d, Y + d)
ctx_s.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d)
ctx_s.lineTo(X + 3 * d, Y + 3 * d)
ctx_s.lineTo(X, Y + 3 * d)
ctx_s.closePath()
ctx_s.stroke()
ctx_s.shadowBlur = 20
ctx_s.shadowColor = "black"
ctx_s.fill()
},
initImage() {
const imageListTemp = [
"https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=4018557288,1217151095&fm=26&gp=0.jpg",
"https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3252521864,872614242&fm=26&gp=0.jpg",
"https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2534506313,1688529724&fm=26&gp=0.jpg"
]
this.image.loading = true
let imgRandomIndex = Math.round(Math.random() * (imageListTemp.length - 1))
this.image.src = imageListTemp[imgRandomIndex]
return new Promise(resolve => resolve())
},
drawImage(fun) {
let img = new Image()
img.src = this.image.src
img.onload = () => {
fun(img)
this.image.loading = false
}
},
clear() {
this.stat = 'ready'
let c = this.$refs.puzzleBox
let c_l = this.$refs.puzzleLost
let c_s = this.$refs.puzzleShadow
c.setAttribute("height", c.getAttribute("height"))
c_l.setAttribute("height", c.getAttribute("height"))
c_s.setAttribute("height", c.getAttribute("height"))
},
moveStart(e) {
this.moveStartAtX = e.pageX
this.addListener()
},
moving(e) {
this.stat = 'move'
const distance = e.pageX - this.moveStartAtX
if (this.moveStartAtX === null
|| distance < 0
|| distance > this.width - this.blockSize) {
return
}
this.sliderStyle.left = distance + "px"
this.sliderStyle.transition = "inherit"
},
moveEnd(e) {
const distance = e.pageX - this.moveStartAtX
const ver_Num = this.randomX - 10
const minLeft = ver_Num - this.deviation
const maxLeft = ver_Num + this.deviation
if (this.moveStartAtX !== null) {
const success = maxLeft > distance && distance > minLeft
this.stat = success ? 'success' : 'fail'
setTimeout(() => {
this.stat = 'ready'
if (!success) return this.initCanvas()
this.resolve()
this.close()
}, success ? 500 : 1000)
}
setTimeout(() => {
this.sliderStyle.left = '0'
this.sliderStyle.transition = "left 0.5s"
}, 1000)
this.moveStartAtX = null
this.removeListener()
},
addListener() {
if (this.image.loading) return
document.addEventListener("mousemove", this.moving)
document.addEventListener("mouseup", this.moveEnd)
},
removeListener() {
document.removeEventListener("mousemove", this.moving)
document.removeEventListener("mouseup", this.moveEnd)
},
handleAfterLeave() {
this.$destroy(true)
this.$el.parentNode.removeChild(this.$el)
}
},
mounted() {
this.initImage().then(() => this.initCanvas())
},
}
</script>
<style lang="scss">
.puzzle-container {
position: absolute;
box-shadow: 0 0 10px #cccccc;
border: 1px solid #cccccc;
background: #ffffff;
.puzzle-text-container {
&:hover {
overflow: visible !important;
}
.puzzle-text-tip {
position: absolute;
top: -32px;
left: 10px;
border-radius: 2px;
padding: 0 4px;
height: 22px;
min-width: 50px;
line-height: 22px;
background-color: #5F5F5F;
white-space: nowrap;
font-size: 12px;
text-align: center;
color: white;
&::before {
display: block;
position: absolute;
bottom: -6px;
left: 0;
content: '';
border-style: solid;
border-width: 4px 6px;
border-color: #5F5F5F transparent transparent #5F5F5F;
width: 0;
height: 0;
}
}
}
.puzzle-view {
position: relative;
overflow: hidden;
margin: 10px 10px 0;
.puzzle-image-loading {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #e5e5e5;
padding-top: 10%;
&__icon {
background-size: 764.70588%;
background-position: 0 70.70218%;
margin: 11% auto 10px;
width: 34px;
height: 26px;
overflow: hidden;
background-repeat: no-repeat;
background-image: url(~@/assets/images/sprite.1.2.4.png);
}
&__text {
text-align: center;
font-size: 14px;
margin-bottom: 1%;
color: #b2b2b2;
}
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.puzzle-lost-box {
position: absolute;
left: 0;
top: 0;
}
canvas {
position: absolute;
left: 0;
top: 0;
z-index: 22;
}
.verify-result {
position: absolute;
z-index: 999;
color: white;
height: 24px;
line-height: 24px;
left: 0;
bottom: -25px;
font-size: 14px;
width: 100%;
transition: bottom 0.3s;
text-indent: 16px;
&.verify-success {
background-color: $--color-success
}
&.verify-fail {
background-color: $--color-danger
}
&.show-result {
bottom: 0;
}
}
}
.slider-container {
position: relative;
margin: 5.39% 3.24%;
width: 93.52%;
padding: 0 0 13.67% 0;
background-color: white;
background-size: 100%;
background-position: 0 0;
background-repeat: no-repeat;
background-image: url(~@/assets/images/sprite.1.2.4.png);
.slider-track {
position: absolute;
top: 50%;
margin: -19px 0 0 0;
padding: 0 0 0 25%;
.slider-tip {
position: relative;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity .5s;
line-height: 38px;
font-size: 14px;
text-align: center;
white-space: nowrap;
color: #88949d;
}
}
.slider-btn {
position: absolute;
margin: -4.62% 0 0 -2.31%;
width: 25.38%;
padding: 0 0 25.38% 0;
cursor: pointer;
background-size: 393.93939%;
background-repeat: no-repeat;
background-image: url(~@/assets/images/sprite.1.2.4.png);
}
}
.puzzle-footer {
position: relative;
border-top: 1px solid #EEEEEE;
width: 100%;
margin: 0;
padding: 0 0 17.27% 0;
.puzzle-small {
position: absolute;
left: 5.1%;
top: 50%;
margin-top: -4.13%;
padding: 0 0 8.27% 0;
width: 40.45%;
height: 0;
.puzzle-close, .puzzle-refresh {
margin-left: 8.9%;
width: 17.8%;
cursor: pointer;
text-decoration: none;
vertical-align: top;
position: relative;
display: inline-block;
height: 0;
padding-bottom: 17.8%;
background-repeat: no-repeat;
background-image: url(~@/assets/images/sprite.1.2.4.png);
overflow: hidden;
background-size: 1300%;
}
.puzzle-close {
margin-left: 0;
background-position: 0 44.86874%;
}
.puzzle-refresh {
background-position: 0 81.38425%;
}
}
}
}
.puzzle-shake {
animation: shake 0.2s linear 3 both;
}
@keyframes shake {
25% {
transform: translate3d(-6px, 0, 0)
}
75% {
transform: translate3d(6px, 0, 0)
}
100% {
transform: translate3d(0, 0, 0)
}
}
</style>

@ -13,6 +13,7 @@ import BottomTip from '@/components/BottomTip'
import Guide from '@/components/Guide' import Guide from '@/components/Guide'
import ImageViewer from '@/components/ImageViewer' import ImageViewer from '@/components/ImageViewer'
import Message from '@/components/Message' import Message from '@/components/Message'
import PuzzleVerify from '@/components/PuzzleVerify'
Vue.use(Element) Vue.use(Element)
@ -29,6 +30,7 @@ Vue.prototype.$bottomTip = BottomTip
Vue.prototype.$guide = Guide Vue.prototype.$guide = Guide
Vue.prototype.$image = ImageViewer Vue.prototype.$image = ImageViewer
Vue.prototype.$message = Message Vue.prototype.$message = Message
Vue.prototype.$puzzleVerify = PuzzleVerify
Vue.config.productionTip = false Vue.config.productionTip = false

@ -59,12 +59,6 @@ const router = {
component: () => import('@/views/example/components/picturePreview'), component: () => import('@/views/example/components/picturePreview'),
meta: {title: '图片预览'} meta: {title: '图片预览'}
}, },
{
path: 'verify',
name: 'verify',
component: () => import('@/views/example/components/verify'),
meta: {title: '滑块验证'}
},
{ {
path: 'skeleton', path: 'skeleton',
name: 'skeleton', name: 'skeleton',

@ -2,7 +2,7 @@
<div class="error-container"> <div class="error-container">
<div> <div>
<div class="pic"> <div class="pic">
<img alt="403" height="428" src="@/assets/403_images/403.gif" width="313"> <img alt="403" height="428" src="@/assets/images/403.gif" width="313">
</div> </div>
<div class="bullshit"> <div class="bullshit">
<div class="bullshit__oops">Oops!</div> <div class="bullshit__oops">Oops!</div>

@ -2,10 +2,10 @@
<div class="error-container"> <div class="error-container">
<div> <div>
<div class="pic"> <div class="pic">
<img alt="404" class="pic__parent" src="@/assets/404_images/404.png"> <img alt="404" class="pic__parent" src="@/assets/images/404.png">
<img alt="404" class="pic__child left" src="@/assets/404_images/404_cloud.png"> <img alt="404" class="pic__child left" src="@/assets/images/404_cloud.png">
<img alt="404" class="pic__child mid" src="@/assets/404_images/404_cloud.png"> <img alt="404" class="pic__child mid" src="@/assets/images/404_cloud.png">
<img alt="404" class="pic__child right" src="@/assets/404_images/404_cloud.png"> <img alt="404" class="pic__child right" src="@/assets/images/404_cloud.png">
</div> </div>
<div class="bullshit"> <div class="bullshit">
<div class="bullshit__oops">Oops!</div> <div class="bullshit__oops">Oops!</div>

@ -96,12 +96,16 @@
this.passwordType = this.passwordType === 'password' ? '' : 'password' this.passwordType = this.passwordType === 'password' ? '' : 'password'
this.$nextTick(() => this.$refs.password.focus()) this.$nextTick(() => this.$refs.password.focus())
}, },
login() { login({pageX, pageY}) {
if (this.loading) return if (this.loading) return
this.$refs.form.validate(valid => { this.$refs.form.validate(valid => {
if (!valid) return if (!valid) return
this.loading = true this.loading = true
this.$store.dispatch('user/login', {...this.form, password: md5(this.form.password)}) this.$puzzleVerify({left: pageX, top: pageY})
.then(() => this.$store.dispatch('user/login', {
...this.form,
password: md5(this.form.password)
}))
.then(() => this.success()) .then(() => this.success())
.catch(() => this.loading = false) .catch(() => this.loading = false)
}) })

@ -1,20 +0,0 @@
<template>
<div>
<el-row class="tip-row">
<a href="https://github.com/Kevin-269581661/vue-puzzle-verification" target="_blank">网上搬的例子</a>
</el-row>
<puzzle-verification :puzzleImgList="puzzleImgList"/>
</div>
</template>
<script>
import PuzzleVerification from '@/components/PuzzleVerify'
export default {
components: {PuzzleVerification},
data() {
return {
puzzleImgList: ['http://q4r31dqam.bkt.clouddn.com/image/56.jpg']
}
}
}
</script>
Loading…
Cancel
Save