目录
前言
随着验证码的发展,通过滑动滑块进行验证在许多网页有着应用
不难看出其由图片及滑块组成,滑块从图片中截取。将其简化,就是一个大的图片和一个小的图片,通过移动小的图片以达到重合的目的。当然这是简陋版。
图片与滑块的生成和参数传递
图片和滑块的生成同样使用PIL库中的类实现,在生成图片时需要在随机的位置留下空白作为缺失的部分。为了更加简单和直观,滑块的边框采用方形,图片与滑块则只有颜色的区别。当然,最为重要的是缺失部分参数的保存与传递,其参数不仅用于前端页面滑块位置的渲染,也用于滑块最终重合与否。具体实现如下:
def slider(request):
# 由于需要y对滑块位置进行确定,所以在此处将y赋值
y = random.randint(15, 230)
request.session['y'] = y
# 只有完成滑动后才会进行获取并判断
if request.method == 'POST':
x = request.session.get('x') + 150
final_x = int(request.POST.get('final_x'))
# 平行滑动,只判断x轴,设置合理范围
if final_x in range(x-3, x+4):
return JsonResponse({'info': 1})
else:
return JsonResponse({'info': 0})
return render(request, 'slider.html', {'y': y})
# 完整的图片生成
def complete_puzzle(request):
# 初始化图片
img_obj = Image.new('RGB', size=(500, 300), color='black')
# 画笔初始化
img_draw = ImageDraw.Draw(im=img_obj)
# 划定图片残缺的范围,后续代码需要该参数以确保 残缺部分初始位置的正确,验证重合时的范围控制合理
x = random.randint(20, 390)
y = request.session.get('y')
# 将数据存储在session中
request.session.update({'x': x})
# 正方形,规定左上及右下
img_draw.rectangle(xy=[(x, y), (x+45, y+45)], fill='hotpink', width=2)
# 临时存储
io_obj = BytesIO()
img_obj.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue())
# 滑动图片生成
def incomplete_puzzle(request):
img_obj = Image.new('RGB', size=(45, 45), color='skyblue')
io_obj = BytesIO()
img_obj.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue())
前端展示
前端页面主要是获取y值,保证滑块与空缺位置平行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>滑块验证</title>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
<style>
#container {
height: 300px;
width: 800px;
margin: auto;
position: relative;
}
.movable_image {
position: absolute;
transition: transform 0.1s ease;
}
</style>
</head>
<body>
<h1 style="text-align: center">请滑动滑块使图片完整</h1>
<div style="text-align: center;height: 300px;">
<div class="container">
<div class="row">
<div class="col-md-12">
<div id="container">
<span style="display: inline-block;margin-right: 0">
<img src="/picture/complete_puzzle/">
</span>
<span style="top: {{ y }}px; left: 0;position: absolute">
<img src="/picture/incomplete_puzzle/" class="movable_image">
</span>
</div>
</div>
</div>
<div class="row" style="width: 300px; margin: auto">
<div class="col-md-12" method="post">
<p>使用滑动条控制图片的横移:</p>
<input type="range" min="0" max="600" value="0" class="custom-range" id="slider">
<button type="button" id="id_button" class="btn btn-success">刷新</button>
</div>
</div>
</div>
</div>
<script>
$('#id_button').click(function () {
location.reload();
});
$(document).ready(function() {
$('#slider').on('input', function() {
let value = $(this).val();
$('.movable_image').css('transform', 'translateX(' + value + 'px)');
});
});
$('#slider').one('mouseup', function() {
let value = $(this).val();
$.ajax({
type: 'POST',
data: {
'final_x': value,
},
success: function(args) {
if (args.info === 1) {
alert('成功');
} else{
alert('失败');
}
}
});
// 禁用滑动条
$('#slider').prop('disabled', true);
});
</script>
</body>
</html>
效果如下:
误区
个人在views中写图片生成函数时,容易出现以下情况:
def slider(request):
y = request.session.get('y')
...
return render(request, 'slider.html', {'y': y})
# 完整的图片生成
def complete_puzzle(request):
...
x = random.randint(20, 390)
y = random.randint(15, 230)
# 将数据存储在session中
request.session.update({'x': x, 'y': y})
...
这是非常自然的思路,将x,y的随机整合到图片生成函数中去。但是大家在执行时就会发现滑块与空缺部分不在一条直线上
于是我在函数中输出y值,发现:
也就是说,slider函数中的y值总是上一次 complete_puzzle函数中生成的值
从控制台的输出也可以看出,先执行slider函数(先拿到y值),再执行complete_puzzle函数(设置y值),这就造成了slider函数传给前端的值是上一次的。找到原因后,就对症下药,将y的赋值放到slider函数中,即可解决。
总结
编程需要动手实践,看着简单,但是实操时就可能会出现报错,同时细节也非常重要。
不足请指正