Vue 使用接口返回的背景图片和拼图图片进行滑动拼图验证

一、背景

前两天发了一篇 vue-monoplasty-slide-verify 滑动验证码插件使用及踩坑_vue-monoplasty-slide-verify 引用后不显示-CSDN博客

这两天项目又需要通过接口校验,接口返回了背景图片和拼图图片,于是在网上找了一篇帖子,vue + 图片滑动验证_基于vue图片滑动验证-CSDN博客

写的不错,代码拿过来就能用;但是感觉源码和vue-monoplasty-slide-verify的比较像,vue-monoplasty-slide-verify的icon图片拿过来直接就能在参考帖子里用。

二、获取验证码接口返回数据示例

三、校验接口返回数据示例

 

四、封装与后端配合的滑动组件

//在参考帖基础上修改的
<template>
    <div>
        <el-dialog :visible.sync="showSlideVerify" title="向右滑动通过验证" :width=" w +48 + 'px'" :close-on-click-modal="false" :close-on-press-escape="false">
            <div class="slide-verify" :style="{width: w + 'px'}" id="slideVerify" onselectstart="return false;">
                <!-- 图片加载遮蔽罩 -->
                <div :class="{'slider-verify-loading': loadBlock}"></div>
                <canvas :width="w" :height="h" ref="canvas"></canvas>
                <div v-if="showRefresh" @click="refresh" class="slide-verify-refresh-icon"></div>
                <canvas :width="block_width" :height="h" 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}">
                            <div class="slide-verify-slider-mask-item-icon"></div>
                        </div>
                    </div>
                    <span class="slide-verify-slider-text">{{sliderText}}</span>
                </div>
            </div>
        </el-dialog>
    </div>
  </template>
<script>
import { getCanvasAndPuzzle, checkPuzzle } from '@/api/modules/setting'

export default {
    name: 'SlideVerify',
    props: {
        sliderText: {
            type: String,
            default: '向右滑动'
        },
        showRefresh: {
            type: Boolean,
            default: true
        }
    },
    data () {
        return {
            showSlideVerify: false,
            containerActive: false, // container active class
            containerSuccess: false, // container success class
            containerFail: false, // container fail class

            nonceStr: '',

            canvasCtx: null,
            blockCtx: null,
            block: null,
            block_src: undefined,
            block_y: undefined,
            block_width: undefined,
            block_height: undefined,
            block_radius: undefined,

            w: 0,
            h:0,
            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
        }
    },
    methods: {
        show () {
            Object.assign(this.$data, {
                success: false,
                containerActive: false,
                containerSuccess: false,
                containerFail: false,

                nonceStr: '',

                w: 0,
                h: 0,
                img: undefined,
                
                loadBlock: true,
                block_src: undefined,
                block_y: undefined,
                block_width: undefined,
                block_height: undefined,
                block_radius: undefined,

                sliderLeft: 0,
                sliderMaskWidth: 0
            });
            this.getCanvasAndPuzzle();
        },
        getCanvasAndPuzzle () { //从接口获取滑动组件背景图片、图片宽高与拼图图片、拼图宽高、block_radius、垂直方向上的位置
            getCanvasAndPuzzle().then(res => {
                Object.assign(this.$data, {
                    nonceStr: res.data.nonceStr, //根据接口的校验逻辑,校验时需传递给后台
                    w: res.data.canvasWidth,
                    h: res.data.canvasHeight,
                    img: res.data.canvasSrc,
                    block_src: res.data.blockSrc,
                    block_y: res.data.blockY,
                    block_width: res.data.blockWidth,
                    block_height: res.data.blockHeight,
                    block_radius: res.data.blockRadius,
                    showSlideVerify: true
                });
                this.$nextTick(() => {
                    this.init();
                });
            }).catch(error => {
                this.$message.error(error);
            });
        },
        init () {
            this.initDom();
            this.initImg();
            this.bindEvents();
        },
        initDom () {
            this.block = this.$refs.block;
            this.canvasCtx = this.$refs.canvas.getContext('2d');
            this.blockCtx = this.block.getContext('2d');
        },
        initImg () {
            let _this = this, image = new Image(), blockImage = new Image();
            image.src = this.img;
            blockImage.src = this.block_src;
            image.onload = () => {
                _this.canvasCtx.drawImage(image, 0, 0, _this.w, _this.h);
            };
            blockImage.onload = () => {
                _this.loadBlock = false;
                // 第二个、三个参数分别是被拖动的拼图在水平、垂直方向的偏移量
                _this.blockCtx.drawImage(blockImage, 0, _this.block_y, _this.block_width, _this.block_height);

            };
        },
        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;
                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;
                this.timestamp = + new Date() - this.timestamp;

                this.verify();
            });
        },
        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;
            this.verify();
        },
        verify () {
//将拼图水平方向拖动量传递给接口,接口来校验是否拼好返回校验结果,我也不是很理解这个操作,但没办法,就让这样做,只能自己网上找例子,然后改出来了
            checkPuzzle({
                nonceStr: this.nonceStr,
                value: parseInt(this.block.style.left) //被拖动拼图在水平方向上的移动量
            }).then(res => {
                if(res.code===-1 || !res.data) {//校验通过,res.data的值为true,否则为false
                    this.containerFail = true;
                    this.$message.error(res.message);
                    this.refresh();
                } else {
                    this.$emit('verify-success');
                    Object.assign(this.$data, {
                        containerSuccess: true,
                        success: true,
                        showSlideVerify: false
                    });
                }
            }).catch(error => {
                this.$message.error(error);
            });
        },
        refresh () {
            let { w, h, block_width } = this;
            this.canvasCtx.clearRect(0, 0, w, h);
            this.blockCtx.clearRect(0, 0, block_width, h);
            this.block.style.left = 0;
            Object.assign(this.$data, {
                success: false,
                containerActive: false,
                containerSuccess: false,
                containerFail: false,

                nonceStr: '',
                img: undefined,
                loadBlock: true,
                sliderLeft: 0,
                sliderMaskWidth: 0
            });
            this.getCanvasAndPuzzle();
        }
    }
}
</script>
<style scoped>
.slide-verify {
    position: relative;
}
  
/* 图片加载样式 */
.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: 0;
    top: 0;
    width: 34px;
    height: 34px;
    cursor: pointer;
    background: url('./imgs/icon.png') 0 -437px;
    background-size: 34px 471px;
}

.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-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: #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: 15px;
    left: 13px;
    width: 14px;
    height: 12px;
    background: url('./imgs/icon.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>

五、 接口

意义不太大,要根据项目实际情况来的

 六、CSS 代码里用到的图片资源

https://download.csdn.net/download/hrcsdn13/89709217

七、CSS与组件文件位置

八、效果图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/872692.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

四、搭建网站服务器超详细步骤——解决宝塔界面无法登录问题

前言 本篇博客是搭建网站服务器的第四期&#xff0c;也到了中间的一节 先分享一下我在搭建网站时的个人感受&#xff0c;我在这个环节卡住了很久 后来突然醒悟了&#xff0c;然后成功进入了宝塔界面 现在就来分享一下&#xff0c;我所遇到的问题 小伙伴们坐好了 …

PostgreSQL技术内幕6:PostgreSQL索引技术

文章目录 0. 简介1.PG索引类型介绍2. PG创建索引说明及索引属性查看2.1 创建说明2.2 查看方式2.2.1 查看PG默认支持的索引及对应的Handler类型2.2.2 查看B树索引属性 3. 索引选择3.1 查看索引情况 4.PG中B-Tree索引原理4.1 页存储结构 5.索引代码分析5.1 不同索引结构解析5.1.1…

day15-Linux的优化_linux15个优化

① UID 当前用户uid信息 [rootoldboy59 ~]# id uid0(root) gid0(root) groups0(root) \\UID 当前用户uid信息※② PATH 存放的是命令的位置/路径 [rootoldboy59 ~]# echo $PATH \\用$符号识别环境变量 /usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bi…

【技巧】Excel检查单元格的值是否在另一列中

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 用到的excel函数 IF(ISNUMBER(MATCH(H2, I2:I10, 0)), H2, "") 注意改上面的“H2、I2、I10”&#xff01; 函数效果 函数解释 检查单元格 …

探索MongoDB的Python之钥:pymongo的魔力

文章目录 探索MongoDB的Python之钥&#xff1a;pymongo的魔力背景&#xff1a;为什么选择pymongo&#xff1f;简介&#xff1a;pymongo是什么&#xff1f;安装&#xff1a;如何将pymongo纳入你的项目&#xff1f;基础用法&#xff1a;五个核心函数介绍1. 连接到MongoDB2. 选择数…

[Deepin] 简单使用 RustDesk 实现远程访问Deepin

本教程假设你学会了看官方文档&#xff0c;且拥有基本的IT常识 本教程仅提供可用的方法&#xff0c;并讲述局限性和更优但更复杂的方法&#xff0c;不是一个手把手教程 目标&#xff1a;实现远程访问Deepin 依托 樱花frpRustDesk的“允许通过ip访问” 概述 在RustDesk打开…

【C++】—— string 模拟实现

【C】—— string模拟实现 0 前言1 string的底层结构2 默认成员函数的实现2.1 构造函数2.1.1 无参构造2.1.2 带参构造2.1.2 合并 2.2 析构函数2.3 拷贝构造函数2.3.1 传统写法2.3.2 现代写法 2.3 赋值重载2.3.1 传统写法2.3.2 现代写法2.3.3 传统写法与现代写法的优劣 3 size、…

Bootstrap 字体图标无法显示问题,<i>标签字体图标无法显示问题

bootstrap fileInput 以及 Bootstrap 字体图标无法显示问题。 今天在用 bootstrap fileInput 插件的时候发现图标无法显示&#xff0c;如下&#xff1a; 查看DOM&#xff0c;发现那些图标是<i>标签做的&#xff1a; 网上的方案 方案1 网上很多人说是我们打乱了boots…

每日OJ_牛客_走迷宫(简单bfs)

目录 牛客_走迷宫&#xff08;简单bfs&#xff09; 解析代码&#xff1a; 牛客_走迷宫&#xff08;简单bfs&#xff09; 走迷宫__牛客网 解析代码&#xff1a; 采用一个二维数组&#xff0c;不断的接受迷宫地图(因为有多个地图)&#xff0c;获取到迷宫地图后&#xff0c;采…

信号有效带宽

根据傅里叶变换可以知道信号带宽是无穷大的&#xff0c;这对实际应用是帮助不大的&#xff0c;所以有了有效带宽的概念&#xff0c;可能大家知道常用的经验公式&#xff1a;O.35/Tr或者0.5/Tr等&#xff0c;那这个公式是怎么来的呢&#xff1f;有效带宽又是什么含义呢&#xff…

Go Web 编程 PDF

&#x1f4da; Go Web开发必读:《Building Web Applications with Go》PDF资源分享 &#x1f50d; 找寻良久,终于寻得这本珍贵资源!现在我免费分享给大家 你是否正在学习Go语言开发Web应用?是否想要提升Go并发编程能力?这本书绝对不容错过! &#x1f4d6; 关于这本书 《B…

短信PHP接口平台可以为企业带来哪些优势

短信验证码在我们的日常生活中可以说是无处不在&#xff0c;并且短信验证码目前在市场中已经得到了广泛的使用&#xff0c;这种验证方法可以保证注册人事实名认证&#xff0c;并且可以防止恶意注册&#xff0c;不过也有人觉得短信验证码有一些累赘&#xff0c;那么短信验证码真…

OpenCV中的颜色映射函数applyColorMap的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 OpenCV 中应用类似于 GNU Octave 或 MATLAB 中的颜色映射&#xff0c;虽然 OpenCV 中的颜色映射类型与 GNU Octave 或 MATLAB 中的颜色映射类型名…

深度学习——基于MTCNN算法实现人脸侦测

这里写目录标题 先看效果 MTCNN主体思想级联网络图像金字塔IOU算法iou 公式 nms 算法数据生成celeba 数据代码训练代码侦测代码总结 先看效果 MTCNN 从2016年&#xff0c;MTCNN算法出来之后&#xff0c;属实在工业上火了一把&#xff0c;最近尝试着把论文代码复现了一下。 主…

mybatis特殊符号处理,mybatis一级二级缓存,java反射机制

mybatis特殊符号处理 在 mybatis 中的 xml 文件中&#xff0c;存在一些特殊的符号&#xff0c;比如&#xff1a;<、>、"、&、<>等&#xff0c;正常书写 mybatis 会报错&#xff0c;需要对这些符号进行转义。具体转义如下所示&#xff1a; 特殊字符 转义字…

Python爬虫使用实例-漫kzhan

环境配置 pip install shutil parsel pillow pypdf1/ 单个章节 singleChapter 需要获取参数&#xff1a;chapter_id与comic_id&#xff0c;可能要sign和uid 获取请求地址 urlhttps://comic.mkzhan.com/chapter/content/v1/ # 请求地址获取请求参数 data{chapter_id:499…

Linux下安装MySQL8.0

一、安装 1.下载安装包 先创建一个mysql目录&#xff0c;在将压缩包下载到此 # 下载tar包 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz等待下载成功 2.解压mysql8.0安装包 tar xvJf mysql-8.0.20-linux-glibc2.12-x86…

java编辑器——IntelliJ IDEA

java编辑器有两种选择——IntelliJ IDEA和VsCode。其中IntelliJ IDEA现在是企业用的比较多的&#xff0c;是专门为java设计的&#xff0c;而VsCode则是通过插件来实现Java编辑的。 1.IntelliJ IDEA 官网下载链接&#xff1a;https://www.jetbrains.com/idea/ 注意选择社区版…

STM32F103调试DMA+PWM 实现占空比逐渐增加的软启效果

实现效果&#xff1a;DMAPWM 实现PWM输出时&#xff0c;从低电平到输出占空比逐渐增加再到保持高电平的效果&#xff0c;达到控制 MOS 功率开关软启的效果。 1.配置时钟 2.TIM 的 PWM 功能配置 选择、配置 TIM 注意&#xff1a;选择 TIM 支持 DMA 控制输出 PWM 功能的通道&a…

【Python报错已解决】`AttributeError: move_to requires a WebElement`

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引言&#xff1a;一、问题描述&#xff1a;1.1 报错示例&#xff1a;1.2 报错分析&#xff1a;1.3 解决思路&#xff…