开发环境:C#,VS2019,.NET Core 3.1,ASP.NET Core
1、建立一个验证码控制器
新建两个方法Create和Check,Create用于创建验证码(返回1张图片和令牌),Check用于验证(验证图片旋转角度)它是否有效。
声明一个静态类变量存放列表,列表中存放包含令牌和验证码的对象。
/// <summary>/// 返回一张图片和令牌./// </summary>/// <returns></returns>public string Create(){try{// 记录验证码到缓存中VCodeCircleModel model = new VCodeCircleModel();model.id = Guid.NewGuid().ToString(); // 生成令牌var vcode = VCodeCircleModel.GetVCode(); // 生成验证码model.code = vcode;_list.Add(model);// 返回图片var images = VCodeCircleModel.GetImage(Convert.ToInt32(vcode));VCodeCircleController_Create_Receive result = new VCodeCircleController_Create_Receive();result.code = "0";result.data.id = model.id;result.data.img = VCodeCircleModel.BitmapToBase64Str(images);var json = JsonConvert.SerializeObject(result);return json;}catch (Exception ex){_logger.LogWarning(exception: ex, message: ex.Message);VCodeCircleController_Create_Receive result = new VCodeCircleController_Create_Receive();result.code = "999999";result.msg = "系统异常";var json = JsonConvert.SerializeObject(result);return json;}}public string Check(string id, string code){try{// 旋转图片的误差在±5var temp = Convert.ToInt32(code) - 5;var index = _list.FindIndex(m =>{if (m.id.Equals(id)){for (int i = 0; i < 10; i++){if (m.code.Equals(temp.ToString())){return true;}temp++;}}return false;});ReceiveObject result = new ReceiveObject();if (index >= 0){_list.RemoveAt(index);result.code = "0";result.msg = "验证成功";var json = JsonConvert.SerializeObject(result);return json;}else{result.code = "1";result.msg = "验证失败";var json = JsonConvert.SerializeObject(result);return json;}}catch (Exception ex){_logger.LogError(exception: ex, message: ex.Message);ReceiveObject result = new ReceiveObject();result.code = "999999";result.msg = "系统异常";var json = JsonConvert.SerializeObject(result);return json;}}
public class VCodeCircleModel{/// <summary>/// 令牌./// </summary>public string id { get; set; }/// <summary>///验证码./// </summary>public string code { get; set; }/// <summary>/// 获取随机验证码./// </summary>/// <returns></returns>public static string GetVCode(){// 这里的随机码是旋转图片的角度,至少旋转60度,最多旋转300度Random random = new Random();return random.Next(60, 300).ToString();}/// <summary>/// 随机获取一张图片./// </summary>/// <returns></returns>public static Bitmap GetImage(int angle){// 从文件加载原图Random random = new Random();var image_index = random.Next(0, 2);Image originImage;switch (image_index){case 0:originImage = Image.FromFile(string.Format(@"{0}\Images\{1}", PathHelper.Path, "circleImg1.png"));break;case 1:default:originImage = Image.FromFile(string.Format(@"{0}\Images\{1}", PathHelper.Path, "circleImg2.png"));break;}originImage = Rotate(originImage as Bitmap, angle);return (Bitmap)originImage;}/// <summary>/// 图片旋转/// </summary>/// <param name="ImageOriginal">原图.</param>/// <param name="AngleValue">旋转角度.</param>/// <returns></returns>public static Bitmap Rotate(Bitmap ImageOriginal, float AngleValue){AngleValue = AngleValue % 360;double radian = AngleValue * Math.PI / 180.0;double cos = Math.Cos(radian);double sin = Math.Sin(radian);int w = ImageOriginal.Width;int h = ImageOriginal.Height;int W = (int)(Math.Max(Math.Abs(w * cos - h * sin), Math.Abs(w * cos + h * sin)));int H = (int)(Math.Max(Math.Abs(w * sin - h * cos), Math.Abs(w * sin + h * cos)));Bitmap ImageBaseOriginal = new Bitmap(W, H, PixelFormat.Format32bppArgb);System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(ImageBaseOriginal);g.InterpolationMode = InterpolationMode.NearestNeighbor;g.SmoothingMode = SmoothingMode.HighQuality;Point Offset = new Point((W - w) / 2, (H - h) / 2);Rectangle rect = new Rectangle(Offset.X, Offset.Y, w, h);Point center = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2);g.Clear(Color.White);g.TranslateTransform(center.X, center.Y);g.RotateTransform(360 - AngleValue);g.TranslateTransform(-center.X, -center.Y);g.DrawImage(ImageOriginal, rect);g.ResetTransform();g.Save();g.Dispose();return ImageBaseOriginal;}/// <summary>/// 将图片对象转成Base64的字符串./// </summary>/// <param name="bitmap"></param>/// <returns></returns>public static string BitmapToBase64Str(Bitmap bitmap){using (MemoryStream memoryStream = new MemoryStream()){bitmap.Save(memoryStream, ImageFormat.Jpeg);byte[] bytes = memoryStream.ToArray();return Convert.ToBase64String(memoryStream.ToArray());}}}
2、新建一个视图文件,引入jquery,css文件,js方法中添加三个鼠标事件 - 鼠标按下,鼠标移动和鼠标松开。页面首次加载时调用控制器的Create方法获取图片和令牌,在鼠标松开时调用Check方法验证旋转的角度是否有效。
本来想实现鼠标在图片上拖动后让图片的效果,但发现难度有点大,就换成了拖动滑块后让图片旋转的形式。
<link href="~/css/circle_slide.css" rel="stylesheet" type="text/css" /><!-- 展示验证码 -->
<div class="container"><div class="main"><div id="content" class="content"><img id="backImage" src="" alt=""></div></div><div id="slider"><div id="sliderBlock"></div></div>
</div><script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/MyCircleSlide.js"></script>
* {margin: 0;padding: 0;
}.main {position: relative;margin-left: 20px;margin-top: 20px;width: 200px;background-color: white;
}.content {width: 100%;
}.content img {width: 100%;height: auto;}#slider {width: 85%;height: 40px;background-color: aliceblue;position: relative;
}#sliderBlock {position: absolute;left: 5px;height: 30px;width: 45px;top: 5px;background-color: white;border-radius: 5px;box-shadow: 0 0 10px 2px lightgray;
}
$(function () {var _imageBase64; // 大图var _id;// 鼠标左键是否按下var isMouseDown;// 鼠标按下x值var mouseDownStartX;// 鼠标移动距离var mouseMoveLength;init();document.onmousedown = function (event) {var obj = getElementPosition(document.getElementById('sliderBlock'))if (event.clientX > obj.left &&event.clientX < (obj.left + obj.width) &&event.clientY > obj.top &&event.clientY < (obj.top + obj.height)) {// 鼠标点击事件发生在滑动条的范围内this.isMouseDown = truethis.mouseDownStartX = event.clientXconsole.log("鼠标点击事件发生在滑动条的范围内");}}document.onmousemove = function (event) {if (this.isMouseDown) {this.mouseMoveLength = event.clientX - this.mouseDownStartX; // 计算滑块拖动的距离if (this.mouseMoveLength > 0 &&this.mouseMoveLength < 360) {// 滑块拖动的最小距离是5px,最大距离是大图的宽度 - 小图的宽度document.getElementById('sliderBlock').style.left = 5 + this.mouseMoveLength + 'px'$("#backImage").css("transform", "rotate(" + (this.mouseMoveLength) + "deg)");}}}document.onmouseup = function (event) {// 鼠标松开后停止拖动if (this.isMouseDown) {this.isMouseDown = false// console.log("小图移动了:" + this.mouseMoveLength);// check(x.replace("px", ""));check(this.mouseMoveLength);}}// dom在浏览器的位置function getElementPosition(element) {let top = element.offsetToplet left = element.offsetLeftlet width = element.offsetWidthlet height = element.offsetHeightvar currentParent = element.offsetParent;while (currentParent !== null) {top += currentParent.offsetTopleft += currentParent.offsetLeftcurrentParent = currentParent.offsetParent}return { top, left, width, height }}function check(x) {$.get("Check?code=" + x + "&id=" + _id, function (data) {var obj = JSON.parse(data);if (obj.code == "0") {alert("验证成功");}else {alert("验证失败");}});}// 设置当前图片function setCurrentImageBase64(imageBase64) {_imageBase64 = imageBase64document.getElementById('backImage').src = _imageBase64}function init() {$.get("Create", function (data) {// 获取两张图片和令牌var obj = JSON.parse(data);_id = obj.data.id;setCurrentImageBase64('data:image/webp;base64,' + obj.data.img);});}
});
效果图: