.Net Core使用SkiaSharp繪制小程序分享圖


一、前言

  最近因業務需要開發了一款地產類微信小程序,開發的過程中需要實現分享給好友和分享到朋友圈的功能。好友分享可以直接調用微信小程序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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM