一、前言
最近因業務需要開發了一款地產類微信小程序,開發的過程中需要實現分享給好友和分享到朋友圈的功能。好友分享可以直接調用微信小程序API來實現,調用時需提供一張5:4的圖片,如果不提供則使用默認截圖,為了更好的效果我們一般會選擇自行生成圖片。而分享到朋友圈的功能由於小程序並未開放,則需要我們生成一張長圖供用戶自行發送朋友圈。下面我以生成好友好享圖片為例來講講如何在.Net Core環境中使用SkiaSharp繪制圖片。
(圖1:最終效果圖)
二、SkiaSharp類庫介紹
SkiaSharp是一個基於Google的Skia圖形庫(https://skia.org/)打造的供.Net平台使用的跨平台的2D繪圖API類庫。它提供一個全面的2D繪圖API,能用在移動端、服務端和桌面端呈現圖像。
目前SkiaSharp可供以下平台使用:
- .NET Standard 1.3
- .NET Core
- Tizen
- Xamarin.Android
- Xamarin.iOS
- Xamarin.tvOS
- Xamarin.watchOS
- Xamarin.Mac
- Windows Classic Desktop (Windows.Forms / WPF)
- Windows UWP (Desktop / Mobile / Xbox / HoloLens)
github地址:https://github.com/mono/SkiaSharp
三、准備工作
1、新建一個.Net Core API項目,這里我命名為ShareImage。
2、安裝SkiaSharp,使用NuGet命令或者包管理器安裝:
nuget install SkiaSharp
3、准備底圖
分析最終效果圖(圖1),找出不會變化的部分,讓UI摳下來作為底圖。可以將底圖保存在項目的Images目錄下,我這里存為bg.png。
(圖2:底圖)
4、准備字體
我這里中文部分用了PingFangSC Regular字體,數字部分用了PingFangSC Medium字體。在項目根目錄下新建Fonts目錄,復制這兩種字體,我這里分別命名為PIngfangScRegular.ttf和PingfangScMedium.ttf。由於載入字體是一個耗時的操作,因此我們可以定義一個靜態類TypeFace,預先將字體載入。
using SkiaSharp; namespace ShareImage { public static class Typeface { public static TypefaseWithMuiltWeight PingFang = new TypefaseWithMuiltWeight { Regular = SKTypeface.FromFile("Fonts/PIngfangScRegular.ttf"), Medium = SKTypeface.FromFile("Fonts/PingfangScMedium.ttf") }; } public class TypefaseWithMuiltWeight { public SKTypeface Regular { get; set; } public SKTypeface Medium { get; set; } } }
四、繪圖
1、讀取要寫入的數據,一般這類數據從數據庫讀取,這里我直接定義示例數據
//示例數據 var shop = new ShopInfo { Name = "李學雯", Company = "某某地產", ViewNum = "1024", Nums = new string[] { "128", "64", "96", "48" }, Location = "廣州市-黃埔區", Des = "專業為大宗資產提供中介、法律、會計服務,買賣廠房、倉庫、土地請找我們為您提供更專業更安心的服務" };
2、載入底圖並初始化畫布
//載入底圖 var bmp = SKBitmap.Decode("Images/cardShopBg.png"); //初始化畫布 var canvas = new SKCanvas(bmp);
3、畫頭像
//保存當前畫布狀態,即正常全圖繪制狀態 canvas.Save(); //定義圓形頭像路徑 var avatarPath = new SKPath(); avatarPath.AddCircle(114, 120, 70); //在當前畫布上剪切出頭像路徑,作為當前繪制區域 canvas.ClipPath(avatarPath, SKClipOperation.Intersect, true); //載入頭像圖片,這里用Images/avatar0.jpg文件代替 using (var avatarImage = SKImage.FromEncodedData(SKData.Create("Images/avatar0.jpg"))) { //定義繪制頭像的位置和尺寸 var rect = SKRect.Create(44, 50, 140, 140); //繪制頭像 canvas.DrawImage(avatarImage, rect); } //恢復畫布狀態為全圖繪制狀態 canvas.Restore();
由於頭像是圓形,而我們獲取到的頭像一般是方形,所以我們需要在底圖上定義一個圓形區域作為本次畫圖的區域,在繪制完成后需要恢復為全圖繪制狀態,否則就無法繼續接下來的寫字等操作了。
4、寫單行文本(以姓名為例)
//定義畫筆 using (var paint = new SKPaint()) { //字體 paint.Typeface = Typeface.PingFang.Regular; //字體大小 paint.TextSize = 48; //字體顏色 paint.Color = SKColors.White; //抗鋸齒不能少 paint.IsAntialias = true; //調用畫單行文字擴展方法 canvas.DrawSingleLineText(shop.Name, 225, 62, paint); } //畫單行文字擴展方法 public static void DrawSingleLineText(this SKCanvas canvas,string text,float x,float y,SKPaint paint) { //定義一個矩形,此矩形為計算文字區域的結果 var tRect = new SKRect(); //計算文字占用區域 paint.MeasureText(text, ref tRect); //調用畫布的畫字方法 canvas.DrawText(text, x - tRect.Left, y - tRect.Top, paint); }
寫文字的時候需要注意有一個坐標編移量,可以直接調用canvas.DrawText(text, 0, 0, paint)來觀察一下,寫出文字的起點並非是0,0。所以我們在寫出文字的時候要算出這個偏移量並抵消掉。這個偏移量是如何產生的本人並不知道,如果有熱心網友能幫忙解答一下就好了!
5、畫帶色矩形背景框(見圖1公司名“某某地產”黃色矩形背景)
//定義畫筆 using (var paint = new SKPaint()) { //字體 paint.Typeface = Typeface.PingFang.Regular; //字體大小 paint.TextSize = 26; //抗鋸齒 paint.IsAntialias = true; //顏色 paint.Color = SKColor.Parse("#FFB33E"); //由於這個黃色背景矩形長度是根據文字長度而定的,所以我們需要計算文字的區域大小 var tRect = new SKRect(); //文字長度 var tWidth = paint.MeasureText(shop.Company,ref tRect); //矩形背景區域 var rect = SKRect.Create(224, 134, tWidth + 20, 40); //畫矩形 canvas.DrawRect(rect, paint); }
6、寫多行文字。由於本人並未在SkiaSharp文檔中找到寫多行文字的方法,所以寫一個擴展方法來寫出多行文字:
/// </summary> /// <param name="canvas">畫布</param> /// <param name="text">多行文字</param> /// <param name="x">x坐標</param> /// <param name="y">y坐標</param> /// <param name="paint">畫筆</param> /// <param name="maxWidth">單行文字最大寬度</param> /// <param name="maxLines">最大行數,如果超出,則使用...</param> public static void DrawMuiltLineText(this SKCanvas canvas, string text, float x, float y, SKPaint paint, float maxWidth, int maxLines) { //已寫出行數 int lines = 0; //省略號寬度 var ellipseWidth = paint.MeasureText("..."); //如果文字沒寫完且已寫行數小於最大行數,則繼續寫出 while (!string.IsNullOrEmpty(text) && lines < maxLines) { //單行文字 string lineStr; //單行文字長度 long strLength; //非最后一行 if (lines != maxLines - 1) { //調用BreakText方法,可計算指定寬度能寫出多少文字 strLength = paint.BreakText(text, maxWidth, out float ww, out lineStr); } //最后一行 else { //調用BreakText方法計算單行文字長度,這里需減去省略號寬度 strLength = paint.BreakText(text, maxWidth - ellipseWidth, out float ww, out lineStr); //假如字還沒寫完,需加省略號 if (strLength < text.Length) { lineStr += "..."; } } //文字矩形 var tRect = new SKRect(); //計算文字矩形 paint.MeasureText(lineStr, ref tRect); //計算坐標 var tPoint = new SKPoint { X = x - tRect.Left, //這里注意Y坐標需要加上行高 Y = y + lines * paint.FontSpacing - tRect.Top }; //寫出一行文字 canvas.DrawText(lineStr, tPoint, paint); //已寫出行數加1 lines++; //取剩余文字 text = text.Substring((int)strLength); } }
五、總結
SkiaSharp是一個非常強大且易用的.Net平台繪圖庫,這里以繪制小程序分享圖為例僅列出一些基礎用法。由於本人水平有限,以上代碼如有紕漏、錯誤或有待優化之處,歡迎大家指出!
附SkiaSharp文檔地址:https://docs.microsoft.com/en-us/dotnet/api/SkiaSharp