.net 8 + SkiaSharp 实现 黑底,浅色字体 6 位字符,扭曲,数字+字母,粘连+干扰线的图形验证码。
先看效果:




实现代码如下:
using SkiaSharp; // SkiaSharp-3.119.0 public class verify_code { private static readonly char[] CharacterTable = { '1','2','3','4','5','6','7','8','9', 'A', 'C','D', 'F', 'H', 'J','K','L', 'N', 'P', 'R','S','T', 'U','V', 'X','Y','Z', }; private static readonly Random Rnd = new Random(); private static readonly double[,] WaveMatrix; private static readonly SKSizeI WaveMatrixSize; private static readonly SKColor[] Colors = { SKColors.Red, SKColors.LimeGreen, SKColors.DeepSkyBlue, SKColors.Yellow, SKColors.LightGray, SKColors.Orange }; static verify_code() { WaveMatrixSize = new SKSizeI(600, 600); WaveMatrix = new double[WaveMatrixSize.Width, WaveMatrixSize.Height]; for (int i = 0; i < WaveMatrixSize.Width; i++) { for (int j = 0; j < WaveMatrixSize.Height; j++) { WaveMatrix[i, j] = (Math.Sin((i + j) * Math.PI / 130.0) + 1.0) / 2.0; } } } public static string NextCode(int length = 6) { return string.Concat(Enumerable.Range(0, length) .Select(_ => CharacterTable[Rnd.Next(CharacterTable.Length)])); } public static SKBitmap NextImage(int length = 6, int width = 190, int height = 80) { string code = NextCode(length); return NextImage(code, width, height); } // ==================== 1. 修改 NextImage 方法(调用单条干扰线) ==================== public static SKBitmap NextImage(string code, int width = 240, int height = 100) { var color = Colors[Rnd.Next(Colors.Length)]; using var font = new SKFont(SKTypeface.FromFamilyName("Arial", SKFontStyle.BoldItalic), 52); using var paint = new SKPaint(font) { Color = color, IsAntialias = true, Style = SKPaintStyle.Fill, }; var textBounds = new SKRect(); paint.MeasureText(code, ref textBounds); float spacing = -9.5f; // 粘连度 using var bitmap = new SKBitmap(width, height); using var canvas = new SKCanvas(bitmap); canvas.Clear(SKColors.Black); // ==================== 关键调整:整体靠左 ==================== float totalTextWidth = textBounds.Width + spacing * (code.Length - 1); float x = (width - totalTextWidth) / 2f - 12; // ← 改为 -12(靠左),之前是 +5 float y = height * 0.68f; foreach (char c in code) { string ch = c.ToString(); canvas.DrawText(ch, x, y, paint); x += paint.MeasureText(ch) + spacing; } DrawNoiseLine(canvas, width, height, color); return ApplyWaveDistortion(bitmap); } // ==================== 2. 新增单条干扰线方法 ==================== private static void DrawNoiseLine(SKCanvas canvas, int width, int height, SKColor color) { using var paint = new SKPaint { Color = color.WithAlpha(200), StrokeWidth = 2.8f, Style = SKPaintStyle.Stroke, IsAntialias = true, }; var path = new SKPath(); path.MoveTo(Rnd.Next(width / 4), Rnd.Next(height)); // 绘制一条平滑的曲线 path.CubicTo( Rnd.Next(width * 2 / 5, width * 3 / 5), Rnd.Next(height / 4, height * 3 / 4), Rnd.Next(width * 2 / 5, width * 3 / 5), Rnd.Next(height / 4, height * 3 / 4), Rnd.Next(width * 3 / 4, width), Rnd.Next(height)); canvas.DrawPath(path, paint); } private static void DrawNoiseLines(SKCanvas canvas, int width, int height, SKColor color) { using var paint = new SKPaint { Color = color.WithAlpha(180), StrokeWidth = 2.5f, Style = SKPaintStyle.Stroke, IsAntialias = true, }; for (int i = 0; i < 4; i++) { var path = new SKPath(); path.MoveTo(Rnd.Next(width), Rnd.Next(height)); path.CubicTo( Rnd.Next(width), Rnd.Next(height), Rnd.Next(width), Rnd.Next(height), Rnd.Next(width), Rnd.Next(height)); canvas.DrawPath(path, paint); } } private static SKBitmap ApplyWaveDistortion(SKBitmap source) { int w = source.Width; int h = source.Height; var result = new SKBitmap(w, h); int xi = Rnd.Next(WaveMatrixSize.Width - w); int yi = Rnd.Next(WaveMatrixSize.Width - w); int xj = Rnd.Next(WaveMatrixSize.Height - h); int yj = Rnd.Next(WaveMatrixSize.Height - h); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int nx = i - (int)(WaveMatrix[xi + i, xj + j] * 12); int ny = j - (int)(WaveMatrix[yi + i, yj + j] * 12); if (nx >= 0 && nx < w && ny >= 0 && ny < h) { result.SetPixel(i, j, source.GetPixel(nx, ny)); } } } return result; } }

浙公网安备 33010602011771号