
不知各位朋友现在在web端进行登录的时候有没有注意一个变化,以前登录的时候是直接账号密码通过就可以直接登录,再后来图形验证码,数字结果运算验证,到现在的拼图验证。这一系列的转变都是为了防止机器操作,但对于我们来说,有亿点麻烦,但也没办法呀。
今天我们也一起来做一个制造亿点麻烦的人,实现一个拼图验证。
这个实现原理并不复杂,我们只需要一张图作为我们的拼接素材,我们再单独弄一个盒子,然后移动它,到我们的指定位置,到达指定范围内即验证通过,反之验证未通过。
既然原理我们知道了,那我们就开干吧。
本篇文章以 css 为主, javascript为辅实现。
我们要实现这个功能,我们需要先搭建出来一个框架。
// css
<style>
.check{
width: 400px;
height: 300px;
background-repeat: no-repeat;
background-size: 100% 100%;
background-image: url(https://img0.baidu.com/it/u=2028084904,3939052004&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500);
}
</style>
// html
<div class="check"></div>
我们画出来后,它就长下面图这样。

我们需要添加一个被校验的区域及校验区域,用来做我们的校验,像下图这两个东西。

这里我们使用伪类来实现这两个区域。
校验区域
.check::before{
content: '';
width: 50px;
height: 50px;
background: rgba(0, 0, 0, 0.5);
border: 1px solid #fff;
position: absolute;
top: 100px;
left: 280px;
}
这样一个校验区域就做好了。

被校验区域
这里我们需要使用到background-position根据我们的校验区域大小进行切出我们的被校验区域。
background-image和background-repeat我们直接继承,background-position设置为校验区域的坐标位置(也就是距离top和left的距离),我们将background-size图片大小设为原盒子的大小。这样我们就得到了校验区域的那一片区域,也就是我们的被校验区域了。
.check-child{
content: '';
width: 50px;
height: 50px;
border: 1px solid #fff;
background-image: inherit;
background-repeat: inherit;
background-size: 400px 300px;
background-position: -280px -100px;
position: absolute;
top: 100px;
left: 10px;
}
// html
<!-- 被校验区域 -->
<div class="check-child"></div>

这里我们两个区域都添加完了,我们需要添加一个拖动条。
我们先添加一个拖动区域。
// css
.drag{
width: 400px;
height: 50px;
background-color: #e3e3e3;
margin-top: 10px;
position: relative;
}
// html
<div class="drag"></div>

现在拖动区域有了,我们需要在拖动区域内添加一个可拖动的盒子,及操作说明,不然看起来交互效果不友好。
我们添加一个可以拖动的盒子。
// css
.drag-child{
width: 50px;
height: 50px;
background-color: aquamarine;
z-index: 10;
position: absolute;
top: 0;
left: 0;
}
// html
<!-- 可拖动的盒子 -->
<div class="drag-child"></div>

为了我们友好的交互,我们在拖动区域内给他添加操作说明。
// css
.drag-tips{
display: flex;
align-items: center;
justify-content: end;
width: 95%;
height: 100%;
margin: 0 auto;
font-size: 12px;
color: #8a8a8a;
}
// html
<!-- 可拖动的盒子 -->
<div class="drag-tips">
<span>按住左边按钮向右拖动完成上方图像验证</span>
</div>

这一步我们需要让我们的拖动盒子动起来,让他可以在拖动区域内随意的左右拖动。
// 获取元素实例
const drag = document.querySelector('.drag-child')
// 声明鼠标按下事件
const dragMouseDown = event => {
// 添加鼠标移动事件
document.addEventListener('mousemove', dragMouseMove)
}
// 监听鼠标移动事件
const dragMouseMove = event => {
// 获取当前 x 轴坐标
const { offsetX } = event
if(offsetX < 0 || offsetX > 350){
return
}
// 修改可移动盒子的 x 轴坐标
drag.style.transform = `translateX(${offsetX}px)`
}
// 结束鼠标监听事件
const dragMouseUP = event => {
// 移除鼠标移动事件
document.removeEventListener('mousemove', dragMouseMove)
}
// 添加鼠标按下事件
document.addEventListener('mousedown', dragMouseDown)
// 添加鼠标弹起事件
document.addEventListener('mouseup', dragMouseUP)
现在我们的盒子就可以正常的拖动了,但现在它还有几个问题,我们后面来解决。
拖动区域内拖动会闪烁;我们先让被校验区域跟着我们的拖动动起来。
// 图形校验
const check = document.querySelector('.check-child')
// 修改被校验区域坐标
check.style.left = `${offsetX}px`
这样我们的被校验区域就能够跟着动了,我们声明一个方法用来表示,通过校验的回调。
// 通过校验回调
const success = () => {
console.log('通过校验');
}
// 监听鼠标移动事件
const dragMouseMove = event => {
// 获取当前 x 轴坐标
const { offsetX } = event
if(offsetX < 0 || offsetX > 350){
return
}
// 修改可移动盒子的 x 轴坐标
drag.style.transform = `translateX(${offsetX}px)`
// 修改被校验区域坐标
check.style.transform = `translateX(${offsetX}px)`
if(offsetX >= 278 && offsetX <= 285){
// 执行回调
success()
}
}

这里我们在鼠标移出监听的时候添加一个动画,当当前未通过校验的时候我们给他还原到初始位置。
@keyframes move {
to {
transform: translateX(0);
}
}
// 结束鼠标监听事件
const dragMouseUP = event => {
// 移除鼠标移动事件
document.removeEventListener('mousemove', dragMouseMove)
// 获取当前 x 轴坐标
const { offsetX } = event
if(offsetX < 278 || offsetX > 285){
// 修改可移动盒子的 x 轴坐标
drag.style.animation = 'move 0.5s ease-in-out'
// 修改被校验区域坐标
check.style.animation = 'move 0.5s ease-in-out'
// 动画结束监听回调
const animationEnd = ()=>{
// 修改可移动盒子的 x 轴坐标
drag.style.transform = `translateX(${0}px)`
// 修改被校验区域坐标
check.style.transform = `translateX(${0}px)`
// 清除动画属性
drag.style.animation = ''
check.style.animation = ''
// 移出动画结束监听
document.removeEventListener("animationend", animationEnd)
}
// 添加动画结束监听
document.addEventListener("animationend", animationEnd)
}
}
当我们未通过校验,且放开鼠标的时候,它就会自动回到初始位置。
我们在提示文字的样式中添加user-select: none;,禁用掉文字选择。
/* 提示文字说明 */
.drag-tips{
display: flex;
align-items: center;
justify-content: end;
width: 95%;
height: 100%;
margin: 0 auto;
font-size: 12px;
color: #8a8a8a;
user-select: none;
z-index: 1;
position: absolute;
top: 0;
left: 0;
}
拖动区域内拖动会闪烁我们将我们刚刚使用的offsetX改为pageX。这里需要注意一下边距偏移量的问题哦。
// 监听鼠标移动事件
const dragMouseMove = event => {
console.log(event);
// 获取当前 x 轴坐标
const { pageX } = event
if(pageX < 0 || pageX > 350){
return
}
// 修改可移动盒子的 x 轴坐标
drag.style.transform = `translateX(${pageX}px)`
// 修改被校验区域坐标
check.style.transform = `translateX(${pageX}px)`
if(pageX >= 278 && pageX <= 285){
// 执行回调
success()
}
}
// 结束鼠标监听事件
const dragMouseUP = event => {
// 移除鼠标移动事件
document.removeEventListener('mousemove', dragMouseMove)
// 获取当前 x 轴坐标
const { pageX } = event
if(pageX < 278 || pageX > 285){
// 修改可移动盒子的 x 轴坐标
drag.style.animation = 'move 0.5s ease-in-out'
// 修改被校验区域坐标
check.style.animation = 'move 0.5s ease-in-out'
// 动画结束监听回调
const animationEnd = ()=>{
// 修改可移动盒子的 x 轴坐标
drag.style.transform = `translateX(${0}px)`
// 修改被校验区域坐标
check.style.transform = `translateX(${0}px)`
// 清除动画属性
drag.style.animation = ''
check.style.animation = ''
// 移出动画结束监听
document.removeEventListener("animationend", animationEnd)
}
// 添加动画结束监听
document.addEventListener("animationend", animationEnd)
}
}
我们看一下效果图。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>drag</title>
<style>
*{
margin: 0;
padding: 0;
}
body{
padding: 20px;
}
/* 图形拼图验证码 */
.check{
width: 400px;
height: 300px;
background-repeat: no-repeat;
background-size: 100% 100%;
background-image: url(https://img0.baidu.com/it/u=2028084904,3939052004&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500);
position: relative;
}
.check::before{
content: '';
width: 50px;
height: 50px;
background: rgba(0, 0, 0, 0.5);
border: 1px solid #fff;
position: absolute;
top: 100px;
left: 280px;
}
.check-child{
content: '';
width: 50px;
height: 50px;
border: 1px solid #fff;
background-image: inherit;
background-repeat: inherit;
background-size: 400px 300px;
background-position: -280px -100px;
position: absolute;
top: 100px;
left: 0;
}
/* 拖动条 */
.drag{
width: 400px;
height: 50px;
background-color: #e3e3e3;
margin-top: 10px;
position: relative;
}
/* 可拖动的盒子 */
.drag-child{
width: 50px;
height: 50px;
background-color: aquamarine;
z-index: 10;
position: absolute;
top: 0;
left: 0;
}
/* 提示文字说明 */
.drag-tips{
display: flex;
align-items: center;
justify-content: end;
width: 95%;
height: 100%;
margin: 0 auto;
font-size: 12px;
color: #8a8a8a;
user-select: none;
z-index: 1;
position: absolute;
top: 0;
left: 0;
}
@keyframes move {
to {
transform: translateX(0);
}
}
</style>
</head>
<body>
<!-- 图形校验区域 -->
<div class="check">
<!-- 被校验区域 -->
<div class="check-child"></div>
</div>
<!-- 拖动条 -->
<div class="drag">
<!-- 操作说明 -->
<div class="drag-tips">
<span>按住左边按钮向右拖动完成上方图像验证</span>
</div>
<!-- 可拖动的盒子 -->
<div class="drag-child"></div>
</div>
</body>
<script>
// 获取元素实例
const drag = document.querySelector('.drag-child')
// 图形被校验区域
const check = document.querySelector('.check-child')
// 通过校验回调
const success = () => {
console.log('通过校验');
}
// 声明鼠标按下事件
const dragMouseDown = event => {
// 添加鼠标移动事件
document.addEventListener('mousemove', dragMouseMove)
}
// 监听鼠标移动事件
const dragMouseMove = event => {
// 获取当前 x 轴坐标
const { pageX } = event
if(pageX < 0 || pageX > 350){
return
}
// 修改可移动盒子的 x 轴坐标
drag.style.transform = `translateX(${pageX}px)`
// 修改被校验区域坐标
check.style.transform = `translateX(${pageX}px)`
if(pageX >= 278 && pageX <= 285){
// 执行回调
success()
}
}
// 结束鼠标监听事件
const dragMouseUP = event => {
// 移除鼠标移动事件
document.removeEventListener('mousemove', dragMouseMove)
// 获取当前 x 轴坐标
const { pageX } = event
if(pageX < 278 || pageX > 285){
// 修改可移动盒子的 x 轴坐标
drag.style.animation = 'move 0.5s ease-in-out'
// 修改被校验区域坐标
check.style.animation = 'move 0.5s ease-in-out'
// 动画结束监听回调
const animationEnd = ()=>{
// 修改可移动盒子的 x 轴坐标
drag.style.transform = `translateX(${0}px)`
// 修改被校验区域坐标
check.style.transform = `translateX(${0}px)`
// 清除动画属性
drag.style.animation = ''
check.style.animation = ''
// 移出动画结束监听
document.removeEventListener("animationend", animationEnd)
}
// 添加动画结束监听
document.addEventListener("animationend", animationEnd)
}
}
// 添加鼠标按下事件
document.addEventListener('mousedown', dragMouseDown)
// 添加鼠标弹起事件
document.addEventListener('mouseup', dragMouseUP)
</script>
</html>
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:
当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested
我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下