SkiaSharp 透明度

如你所见,SKPaint 类包含一个 SKColor 类型的 Color 属性。 SKColor 包含一个 alpha 通道,因此使用 SKColor 值着色的任何内容都可以部分透明。

SkiaSharp 中的基本动画一文中演示了一些透明度。 本文更深入地探讨了透明度,包括在单个场景中组合多个对象,这种技术有时称为“混合”。 SkiaSharp 着色器部分中的文章讨论了更高级的混合技术。

首次使用四参数 SKColor 构造函数创建颜色时,可以设置透明度级别:

SKColor (byte red, byte green, byte blue, byte alpha);

alpha 值为 0 是完全透明,alpha 值为 0xFF 是完全不透明。 这两个极端之间的值会创建部分透明的颜色。

此外,SKColor 还定义了一种方便的 WithAlpha 方法,该方法会基于现有颜色创建新颜色,但具有指定的 alpha 级别:

SKColor halfTransparentBlue = SKColors.Blue.WithAlpha(0x80);

示例的“编写更多代码”页中演示了部分透明文本的使用。 此页面通过在 SKColor 值中整合透明度来淡入和淡出两个文本字符串:

public class CodeMoreCodePage : ContentPage
{
    SKCanvasView canvasView;
    bool isAnimating;
    Stopwatch stopwatch = new Stopwatch();
    double transparency;

    public CodeMoreCodePage ()
    {
        Title = "Code More Code";

        canvasView = new SKCanvasView();
        canvasView.PaintSurface += OnCanvasViewPaintSurface;
        Content = canvasView;
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();

        isAnimating = true;
        stopwatch.Start();
        Device.StartTimer(TimeSpan.FromMilliseconds(16), OnTimerTick);
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        stopwatch.Stop();
        isAnimating = false;
    }

    bool OnTimerTick()
    {
        const int duration = 5;     // seconds
        double progress = stopwatch.Elapsed.TotalSeconds % duration / duration;
        transparency = 0.5 * (1 + Math.Sin(progress * 2 * Math.PI));
        canvasView.InvalidateSurface();

        return isAnimating;
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        const string TEXT1 = "CODE";
        const string TEXT2 = "MORE";

        using (SKPaint paint = new SKPaint())
        {
            // Set text width to fit in width of canvas
            paint.TextSize = 100;
            float textWidth = paint.MeasureText(TEXT1);
            paint.TextSize *= 0.9f * info.Width / textWidth;

            // Center first text string
            SKRect textBounds = new SKRect();
            paint.MeasureText(TEXT1, ref textBounds);

            float xText = info.Width / 2 - textBounds.MidX;
            float yText = info.Height / 2 - textBounds.MidY;

            paint.Color = SKColors.Blue.WithAlpha((byte)(0xFF * (1 - transparency)));
            canvas.DrawText(TEXT1, xText, yText, paint);

            // Center second text string
            textBounds = new SKRect();
            paint.MeasureText(TEXT2, ref textBounds);

            xText = info.Width / 2 - textBounds.MidX;
            yText = info.Height / 2 - textBounds.MidY;

            paint.Color = SKColors.Blue.WithAlpha((byte)(0xFF * transparency));
            canvas.DrawText(TEXT2, xText, yText, paint);
        }
    }
}

transparency 字段的动画效果以正弦节奏从 0 到 1,再从 1 回到 0。 第一个文本字符串显示通过从 1 减去 transparency 值来计算的 alpha 值:

paint.Color = SKColors.Blue.WithAlpha((byte)(0xFF * (1 - transparency)));

WithAlpha 方法在现有颜色上设置 alpha 分量,在这里是 SKColors.Blue。 第二个文本字符串使用从 transparency 值本身计算的 alpha 值:

paint.Color = SKColors.Blue.WithAlpha((byte)(0xFF * transparency));

动画在两个单词之间交替,敦促用户“编写更多代码”(或可能请求“更多代码”):

编写更多代码

在上一篇有关 SkiaSharp 中的位图基础知识的文章中,你了解了如何使用其中一种 SKCanvasDrawBitmap 方法来显示位图。 所有 DrawBitmap 方法都包含一个 SKPaint 对象作为最后一个参数。 默认情况下,此参数设置为 null,可以忽略它。

或者,可以设置此 SKPaint 对象的 Color 属性以显示具有某种透明度的位图。 在 SKPaintColor 属性中设置透明度级别可让你淡入和淡出位图,或将一个位图分解为另一个位图。

位图透明度在“位图分解”页中有演示。 XAML 文件会实例化一个 SKCanvasView 和一个 Slider

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
             x:Class="SkiaSharpFormsDemos.Effects.BitmapDissolvePage"
             Title="Bitmap Dissolve">
    <StackLayout>
        <skia:SKCanvasView x:Name="canvasView"
                           VerticalOptions="FillAndExpand"
                           PaintSurface="OnCanvasViewPaintSurface" />

        <Slider x:Name="progressSlider"
                Margin="10"
                ValueChanged="OnSliderValueChanged" />
    </StackLayout>
</ContentPage>

代码隐藏文件会加载两个位图资源。 这些位图的大小不同,但它们的纵横比相同:

public partial class BitmapDissolvePage : ContentPage
{
    SKBitmap bitmap1;
    SKBitmap bitmap2;

    public BitmapDissolvePage()
    {
        InitializeComponent();

        // Load two bitmaps
        Assembly assembly = GetType().GetTypeInfo().Assembly;

        using (Stream stream = assembly.GetManifestResourceStream(
                                "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg"))
        {
            bitmap1 = SKBitmap.Decode(stream);
        }
        using (Stream stream = assembly.GetManifestResourceStream(
                                "SkiaSharpFormsDemos.Media.FacePalm.jpg"))
        {
            bitmap2 = SKBitmap.Decode(stream);
        }
    }

    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        canvasView.InvalidateSurface();
    }

    void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
    {
        SKImageInfo info = args.Info;
        SKSurface surface = args.Surface;
        SKCanvas canvas = surface.Canvas;

        canvas.Clear();

        // Find rectangle to fit bitmap
        float scale = Math.Min((float)info.Width / bitmap1.Width,
                                (float)info.Height / bitmap1.Height);
        SKRect rect = SKRect.Create(scale * bitmap1.Width,
                                    scale * bitmap1.Height);
        float x = (info.Width - rect.Width) / 2;
        float y = (info.Height - rect.Height) / 2;
        rect.Offset(x, y);

        // Get progress value from Slider
        float progress = (float)progressSlider.Value;

        // Display two bitmaps with transparency
        using (SKPaint paint = new SKPaint())
        {
            paint.Color = paint.Color.WithAlpha((byte)(0xFF * (1 - progress)));
            canvas.DrawBitmap(bitmap1, rect, paint);

            paint.Color = paint.Color.WithAlpha((byte)(0xFF * progress));
            canvas.DrawBitmap(bitmap2, rect, paint);
        }
    }
}

SKPaint 对象的 Color 属性设置为两个位图的两个互补 alpha 级别。 与位图一起使用 SKPaint 时,Color 值的其余部分是多少并不重要。 唯一重要的是 alpha 通道。 此处的代码只是对 Color 属性的默认值调用 WithAlpha 方法。

移动 Slider 会在一个位图和另一个位图之间分解:

位图分解

在过去几篇文章中,你已了解如何使用 SkiaSharp 绘制文本、圆圈、椭圆、圆角矩形和位图。 下一步是 SkiaSharp 线条和路径,你将在其中了解如何在图形路径中绘制连接的线条。