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

news2024/11/15 13:21:16

一、背景

前两天发了一篇 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.coloradmin.cn/o/2108982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

了解一下HTTP 与 HTTPS 的区别

介绍&#xff1a; HTTP是超文本传输协议。规定了客户端&#xff08;通常是浏览器&#xff09;和服务器之间如何传输超文本&#xff0c;也就是包含链接的文本。通常使用TCP【1】/IP协议来传输数据&#xff0c;默认端口为80。 HTTPS是超文本传输安全协议&#xff0c;具有CA证书。…

羲和能源大数据平台——Python数据绘图方法

1. 写在前面 目前论文对绘图的美观度要求越来越高&#xff0c;在气象领域呈现维度高&#xff0c;时空关联的特性&#xff0c;为了充分展示数据在各个维度的特性&#xff0c;选用合适的绘图方法至关重要&#xff0c;下图给出了如今在科研领域中个常用的各类图像&#xff1a;线型…

远程教育与学习:探索远程控制技术在教育领域的新机遇

什么适合会用到远程控制工具&#xff1f;如果你是运维、是设计或者外勤需要办公的一些资料文件&#xff0c;有远程控制工具工具的话就能轻松解决这些情况。为了保证电脑的安全我建议从官方网站进行下载&#xff0c;比如从向日葵远程控制官网下载就可以得到向日葵的官方正版。这…

Docker Container 常用命令

文章目录 目录 文章目录 1 . 什么是容器&#xff1f; 2 . 容器命令清单 docker create docker run docker ps docker logs docker exec docker kill docker container inspect docker cp docker rm docker export 总结 1 . 什么是容器&#xff1f; 通俗地讲&a…

C++设计模式——Command命令模式

一&#xff0c;命令模式的定义 命令模式是一种行为型设计模式。在实际开发场景中&#xff0c;命令模式将一个请求的处理或者一个具体操作封装为一个对象&#xff0c;从而可以让开发者根据不同的请求参数来生成不同的执行函数。 命令模式的本质是对具体命令的拆解和封装&#…

【Git远程仓库】将本地仓库推送到github(踩坑记录)

上一篇博客已经介绍了git本地仓库的基本操作&#xff0c;接下来记录一下如何将本地仓库上传到远程仓库中 远程仓库&#xff1a;托管在因特网的版本库&#xff0c;保存版本库的历史记录&#xff0c;多人协作 1. 创建远程版本库&#xff0c;得到远程仓库git地址 2. 本地仓库添加…

C#复习之内部类和分布类

知识点一&#xff1a;内部类 知识点二&#xff1a;分布类 知识点三&#xff1a;分部方法

DisplayManagerService启动-Android13

DisplayManagerService启动-Android13 1、DisplayManagerService启动1.1 简要时序图 2、DEFAULT_DISPLAY主屏幕添加3、默认屏幕亮度 1、DisplayManagerService启动 1.1 简要时序图 2、DEFAULT_DISPLAY主屏幕添加 3、默认屏幕亮度

C#复习之继承的基本规则

知识点一&#xff1a;基本概念 知识点二&#xff1a;基本语法 知识点三&#xff1a;实例 知识点四&#xff1a;访问修饰符的影响 知识点五&#xff1a;子类和父类的同名成员 总结&#xff1a;

MIT线性代数

本文链接的原创作者为 浊酒南街https://blog.csdn.net/weixin_43597208 第1讲 MIT_线性代数笔记&#xff1a;第 01 讲 行图像和列图像-CSDN博客 第2讲 MIT_线性代数笔记&#xff1a;第 02 讲 矩阵消元_矩阵first pivot-CSDN博客 第3讲 MIT_线性代数笔记&#xff1a;第 03…

反弹shell介绍和应用

一、什么是反弹shell 1 .含义 反向连接弹shell(即反弹shell为攻击者为服务端,受害者主机为客户端主动连接攻击者的服务端) 2 .目的 对方主机在外网无法访问 对方主机防火墙限制,只能发送请求,不能接收请求 对方IP动态变化 攻击了一台主机需要在自己的机器上…

3D一览通助力成都派铂宇航航天管道设计交付

在航空航天这一高精尖行业中&#xff0c;每一处细节都承载着不可估量的责任与使命。特别是在航天航空管道设计制造的复杂供应链中&#xff0c;任何误差都可能引发连锁反应&#xff0c;影响整体性能乃至安全。当前&#xff0c;航空航天行业面临的一大挑战在于如何有效管理这一复…

【数据结构】LinkedList ------ java实现

知识框架图&#xff1a; LinkedList是一种常用的数据结构。底层是一个双向链表。每个节点包含数据以及指向前一个节点和后一个节点的引用。 一&#xff1a;LinkedList的使用 1.1 LinkedList的构造方法 方法 解释LinkedList() 无参构造public LinkedList(Collection<? exte…

【教程】MySQL数据库学习笔记(六)——数据查询语言DQL(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 第三章 《数据定义语言DDL》 第四章 《数据操…

Day16_0.1基础学习MATLAB学习小技巧总结(16)——元胞数组

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

详细讲解hive on tez中各个参数作用,以及如何优化sql

最近经常有优化sql的任务&#xff0c;但是自己能力有限&#xff0c;只能凭经验去优化&#xff0c;现整理加学习一波&#xff0c;也欢迎各位学习和讨论。 我们经常用hivesql 的模型就是 join.如下。 insert overwrite table a select * from b left join c 这里面发生了什么…

【C++取经之路】map的详细介绍及其使用

目录 关于map 键值对 map的常用操作 关于multimap 关于map template < class Key, // map::key_typeclass T, // map::mapped_typeclass Compare less<Key>, //…

【时间盒子】-【5.绘制闹钟】动态绘制钟表和数字时间

Tips: Preview装饰器&#xff0c;支持组件可预览&#xff1b; Component装饰器&#xff0c;自定义组件&#xff1b; Canvas组件的使用&#xff1b; 使用RenderingContext在Canvas组件上绘制图形&#xff0c;请参考官方文档&#xff1a;https://developer.huawei.com/consume…

Apache ShardingSphere数据分片弹性伸缩加解密中间件

Apache ShardingSphere Apache ShardingSphere 是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。 软件背景 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding…

如何利用python实现碰撞原理

先看图 跑了大概一天 这是结果 具体是通过BIP39规则生成的种子数据 生成完词组后&#xff0c;再根据词组生成姨太地址 # 生成随机助记词 def generate_mnemonic():entropy os.urandom(16) # 随机生成 16 字节熵mnemonic []for i in range(12): # 生成 12 个助记词word_in…