从最初的滑动验证码,到实现旋转验证码!不光实践了SkiaSharp的使用,也学到了很多东西。在网上看到一个拼接验证码功能,手痒了起来,结合前面实现的两种验证码,我们来学习一下如何实现拼接验证码功能!
目录
- 效果预览
- 实现原理
- 代码部分
- 获取背景图
- 计算间隔
- 定义随机值
- 将背景图切成上下两部分
- 将上半部分图片切割成左右两部分
- 拼接图片
- 总结
效果预览
实现原理
其实拼接验证码实现起来比滑动验证码与选择验证码要简单的多,因为拼接验证码不涉及凹槽与滑块模板。只需要一张背景图片就可以了!
获取图片:首先获取背景图片。将背景图片转换成SKBitmap
。
获取一个随机值:根据图片的宽和高,设定一个固定的X坐标间距spacingX
与一个Y坐标间距spacingY
,再定义一个X坐标的随机值randomX
与Y坐标随机值randomY
横着切图:根据得到的随机值randomY
,横着把背景图切成2块,使其成为上下两部分。
竖着切图:根据得到的随机值randomX
,将上半部分竖着切成两块!
这样,我们就得到了4张图片。
将上半部分左右两张图。位置调换,组成一张新图
将组合好的新图,与下半部分组合成一张图。
这样,我们就完成了拼接验证码的图片。将他传到前端。
前端根据服务端传递的数据,将滑块定位到上半部分。然后控制上半部分图片的移动来达到拼接验证码的效果!
代码部分
获取背景图片部分,前面讲过了。
可参考:NET 6 实现滑动验证码(六)、验证码背景图、滑块图与凹槽图的生成
获取背景图
//拼接验证码没有模板,只需要背景图
//获取背景图
var background = await _resourceManager.RandomBackground();
将背景图转为SKBitmap
using var backgroundImage = SKBitmap.Decode(background);
计算间隔
//X坐标间距
int spacingX = backgroundImage.Width / 8;
//Y坐标间距
int spacingY = backgroundImage.Height / 4;
定义间隔主要是为了生成X轴和Y轴的随机值。
定义随机值
int randomX = _random.Next(spacingX, backgroundImage.Width - backgroundImage.Width /5);
int randomY = _random.Next(spacingY, backgroundImage.Height - spacingY);
定义X轴随机值randomX
与Y轴随机值randomY
。
**randomY **:randomY主要作用是随机将背景图片切成上下两部分使用的。
**randomX **主要作用是随机将背景图切成左右两部分使用的。
将背景图切成上下两部分
首先定义一个方法,这个方法的作用是根据传入的内容,将指定图片切成两部分。
public static List<byte[]> SplitImage(int pos,bool direction,SKBitmap img)
{
int StartImageWidth,
StartImageHeight,
EndImageWidth,
EndImageHeight;
int EndScanX, EndScanY;
int ImageWidth = img.Width;
int ImageHeight = img.Height;
StartImageWidth = direction ? ImageWidth : pos;
StartImageHeight = direction ? ImageHeight - pos : ImageHeight;
EndImageWidth = direction ? ImageWidth : ImageWidth - StartImageWidth;
EndImageHeight = direction ? pos : ImageHeight;
EndScanX = direction ? 0 : pos;
EndScanY = direction ? StartImageHeight : 0;
using var StartImage = new SKBitmap(StartImageWidth, StartImageHeight);
using var StartCanvas = new SKCanvas(StartImage);
SKRect StartSourceRect = new SKRect(0, 0, StartImageWidth, StartImageHeight);
SKRect StartDestRect = new SKRect(0, 0, StartImageWidth, StartImageHeight);
StartCanvas.DrawBitmap(img, StartSourceRect, StartDestRect);
using var EndImage = new SKBitmap(EndImageWidth, EndImageHeight);
using var EndCanvas = new SKCanvas(EndImage);
SKRect EndSourceRect = new SKRect(EndScanX, EndScanY, EndScanX + EndImageWidth, EndScanY + EndImageHeight);
SKRect EndDestRect = new SKRect(0, 0, EndImageWidth, EndImageHeight);
EndCanvas.DrawBitmap(img, EndSourceRect, EndDestRect);
List<byte[]> result = new List<byte[]>();
using var StartImg = SKImage.FromBitmap(StartImage);
using var StartData = StartImg.Encode(SKEncodedImageFormat.Png, 100);
using var EndImg = SKImage.FromBitmap(EndImage);
using var EndData = EndImg.Encode(SKEncodedImageFormat.Png, 100);
result.Add(StartData.ToArray());
result.Add(EndData.ToArray());
return result;
}
SplitImage方法有3个参数:
pos:分割点,指在图上从哪开始切。
direction:切割方向。true为水平方向,false为垂直方向!
img:待切割的图片。
int StartImageWidth,
StartImageHeight,
EndImageWidth,
EndImageHeight;
int EndScanX, EndScanY;
int ImageWidth = img.Width;
int ImageHeight = img.Height;
因为要将图片切成2张图片,所以StartImageWidth
与StartImageHeight
表示第一张图片的宽和高。EndImageWidth
与EndImageHeight
表示第二张图片的宽和高。
EndScanX
表示X坐标的切割点,EndScanY
表示Y坐标的切割点。
接下来分别定义两个SKCanvas
,StartCanvas
与EndCanvas
。表示第一张图的画布与第二张图的画布。定义好之后,再定义两个SKRect
。SKRect
表示矩形,他有4个参数。分别是left
、top
、right
和bottom
。刚看到这4个参数有点蒙,这是啥意思?随即反应过来了。其中lert
和top
确定左上角的坐标。right
和bottom
确定右下角的坐标。再使用DrawBitmap
方法,得到两张图片。
需要注意的是,返回的是一个List<byte[]>。里面保存了两张图片的byte[]
数据。为什么不直接返回List呢?因为在代码中使用using var
。如果返回List的话。出了这个方法,两张图的SKBitmap就被释放了。那么再使用这两张图的SKBitmap,就会异常了。如果是使用var
,并不好找一个合适的时机将他们释放掉。所以就返回了一个List<byte[]>。这样都不耽误。
有了这个方法,我们就可以把图片按照要求,切割成2张需要的图片了
将图片切割成上下两部分:
List<byte[]> bgImageList = CaptchaImageUtils.SplitImage(randomY, true, backgroundImage);
using var bgImage0 = SKBitmap.Decode(bgImageList[0]);
using var bgImage1 = SKBitmap.Decode(bgImageList[1]);
将上半部分图片切割成左右两部分
List<byte[]> bgImageTopList = CaptchaImageUtils.SplitImage(randomX, false, bgImage0);
using var bgImageTop0 = SKBitmap.Decode(bgImageTopList[0]);
using var bgImageTop1 = SKBitmap.Decode(bgImageTopList[1]);
使用SplitImage
方法,将刚刚切好的上下2部分的图中的上半部分,切成左右2部分。这时,我们就有4张图片了!
拼接图片
我们计划是,将bgImageTop0
与bgImageTop1
拼接一下,但是拼接的顺序是bgImageTop1+bgImageTop0。这样就得到了一张这样的图,我们叫他SliderImage
然后再将SliderImage
与bgImage1
拼接,就完成了。
先来看一下拼接的方法,给这个拼接方法起个名字,叫ConcatImage
吧。具体代码为:
public static byte[] ConcatImage(bool direction,int width,int height, params SKBitmap[] imgArr)
{
int pos = 0;
using var NewImage = new SKBitmap(width, height);
using var NewImageCanvas = new SKCanvas(NewImage);
foreach(var img in imgArr)
{
float x = direction ? pos : 0;
float y = direction ? 0 : pos;
NewImageCanvas.DrawBitmap(img, x,y);
pos += direction ? img.Width : img.Height;
}
using var NewImg = SKImage.FromBitmap(NewImage);
using var NewData = NewImg.Encode(SKEncodedImageFormat.Png, 100);
return NewData.ToArray();
}
ConcatImage
方法有4个参数,
direction:bool
类型,表示拼接方向,true为水平方向拼接,false为垂直方向拼接。
width:int
类型,拼接图片的宽度
height:int
类型,拼接图片的高度
imgArr:params
类型,要拼接图片的数组
分别定义SKBitmap
与SKCanvas
。然后循环数组。将图片拼接起来,最后返回一个byte[]
。
拼接完成后,将图片返回给前端。
return new ConcatImageCaptchaInfo
{
BackgroundImage = bgImage.ToBase64String(SKEncodedImageFormat.Png),
SliderImage = null,
BackgroundImageWidth = bgImage.Width,
BackgroundImageHeight = bgImage.Height,
SliderImageWidth = 0,
SliderImageHeight = 0,
RandomX = randomX,
RandomY = randomY,
Tolerant = 0.05F,
CaptchaType = CaptchaTypeConstant.CONCAT
};
ToBase64String
为自己写的扩展方法。可以参考.Net 6实现旋转验证码,这篇文章写了这个方法了。
前端代码跟之前的滑动验证码基本一样。稍微改下就可以了!
总结
还有其他种类的验证码,比如点选验证码、图文验证码等。会慢慢完善。
点击下方公众号卡片,关注我!一起学习,一起进步!