question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Missing DashOffset in Pen Style

See original GitHub issue

Hi, I’m wondering why the DashOffset is not present in Mapsui.Styles.Pen. I need to create animations which uses XAML StrokeDashOffset or phase in SKPathEffect.CreateDash(dash, phase). Please let me know if you think there’s easier ways to accomplish the following or if you would like a PR 😉

Animation

Mapsui.Styles.Pen

Property
/// <summary>
/// Stroke dash offset (phase)
/// </summary>
public float DashOffset { get; set; }

Mapsui.Rendering.Skia.PenStyleExtension

Optional parameter to specify phase
public static SKPathEffect ToSkia(this PenStyle penStyle, float width, float[] dashArray = null, float phase = 0f)
{
    switch (penStyle)
    {
        case PenStyle.UserDefined:
            // If dashArray is empty or not even, create sold dash
            if (dashArray == null || dashArray.Length == 0 || dashArray.Length % 2 != 0)
                return SKPathEffect.CreateDash(new float[0], 0);
            // Multiply each dash entry with line width
            float[] dash = new float[dashArray.Length];
            for (var i = 0; i < dashArray.Length; i++)
            {
                dash[i] = dashArray[i] * width;
            }
            return SKPathEffect.CreateDash(dash, phase);
        case PenStyle.Dash:
            return SKPathEffect.CreateDash(new [] { width * 4f, width * 3f }, phase);
        case PenStyle.Dot:
            return SKPathEffect.CreateDash(new [] { width * 1f, width * 3f }, phase);
        case PenStyle.DashDot:
            return SKPathEffect.CreateDash(new [] { width * 4f, width * 3f, width * 1f, width * 3f }, phase);
        case PenStyle.DashDotDot:
            return SKPathEffect.CreateDash(new [] { width * 4f, width * 3f, width * 1f, width * 3f, width * 1f, width * 3f }, phase);
        case PenStyle.LongDash:
            return SKPathEffect.CreateDash(new [] { width * 8f, width * 3f }, phase);
        case PenStyle.LongDashDot:
            return SKPathEffect.CreateDash(new [] { width * 8f, width * 3f, width * 1f, width * 3f }, phase);
        case PenStyle.ShortDash:
            return SKPathEffect.CreateDash(new [] { width * 2f, width * 3f }, phase);
        case PenStyle.ShortDashDot:
            return SKPathEffect.CreateDash(new [] { width * 2f, width * 3f, width * 1f, width * 3f }, phase);
        case PenStyle.ShortDashDotDot:
            return SKPathEffect.CreateDash(new [] { width * 2f, width * 3f, width * 1f, width * 3f, width * 1f, width * 3f }, phase);
        case PenStyle.ShortDot:
            return SKPathEffect.CreateDash(new [] { width * 1f, width * 3f }, phase);
        default:
            return SKPathEffect.CreateDash(new float[0], phase);
    }
}
}

Mapsui.Rendering.Skia.LineStringRenderer

Inherits value from VectorStyle
public static void Draw(SKCanvas canvas, IReadOnlyViewport viewport, IStyle style, IFeature feature, IGeometry geometry,
    float opacity)
{
    if (style is LabelStyle labelStyle)
    {
        var worldCenter = geometry.BoundingBox.Centroid;
        var center = viewport.WorldToScreen(worldCenter);
        LabelRenderer.Draw(canvas, labelStyle, feature, center, opacity);
    }
    else
    {
        var lineString = ((LineString) geometry).Vertices;

        float lineWidth = 1;
        var lineColor = new Color();

        var vectorStyle = style as VectorStyle;
        var strokeCap = PenStrokeCap.Butt;
        var strokeJoin = StrokeJoin.Miter;
        var strokeMiterLimit = 4f;
        var strokeStyle = PenStyle.Solid;
        float[] dashArray = null;
        float dashOffset = 0f;

        if (vectorStyle != null)
        {
            lineWidth = (float) vectorStyle.Line.Width;
            lineColor = vectorStyle.Line.Color;
            strokeCap = vectorStyle.Line.PenStrokeCap;
            strokeJoin = vectorStyle.Line.StrokeJoin;
            strokeMiterLimit = vectorStyle.Line.StrokeMiterLimit;
            strokeStyle = vectorStyle.Line.PenStyle;
            dashArray = vectorStyle.Line.DashArray;
            dashOffset = vectorStyle.Line.DashOffset;
        }

        using (var path = lineString.ToSkiaPath(viewport, canvas.LocalClipBounds))
        using (var paint = new SKPaint { IsAntialias = true })
        {
            paint.IsStroke = true;
            paint.StrokeWidth = lineWidth;
            paint.Color = lineColor.ToSkia(opacity);
            paint.StrokeCap = strokeCap.ToSkia();
            paint.StrokeJoin = strokeJoin.ToSkia();
            paint.StrokeMiter = strokeMiterLimit;
            if (strokeStyle != PenStyle.Solid)
                paint.PathEffect = strokeStyle.ToSkia(lineWidth, dashArray, dashOffset);
            else
                paint.PathEffect = null;
            canvas.DrawPath(path, paint);
        }
    }
}

Mapsui.Rendering.Xaml.LineStringRenderer

Inherits value from VectorStyle
public static class LineStringRenderer
{
    public static System.Windows.Shapes.Shape RenderLineString(LineString lineString, IStyle style, IReadOnlyViewport viewport)
    {
        if (!(style is VectorStyle)) throw new ArgumentException("Style is not of type VectorStyle");
        var vectorStyle = style as VectorStyle;

        System.Windows.Shapes.Path path = CreateLineStringPath(vectorStyle);
        path.Data = lineString.ToXaml();
        path.RenderTransform = new MatrixTransform { Matrix = GeometryRenderer.CreateTransformMatrix(viewport) };
        GeometryRenderer.CounterScaleLineWidth(path, viewport.Resolution);
        return path;
    }

    public static System.Windows.Shapes.Path CreateLineStringPath(VectorStyle style)
    {
        var path = new System.Windows.Shapes.Path { Opacity = style.Opacity };
        if (style.Outline != null)
        {
            //todo: render an outline around the line. 
        }
        path.Stroke = new SolidColorBrush(style.Line.Color.ToXaml());
        path.StrokeDashArray = style.Line.PenStyle.ToXaml(style.Line.DashArray);
        path.StrokeDashOffset = style.Line.DashOffset;
        var penStrokeCap = style.Line.PenStrokeCap.ToXaml();
        path.StrokeEndLineCap = penStrokeCap;
        path.StrokeStartLineCap = penStrokeCap;
        path.StrokeLineJoin = style.Line.StrokeJoin.ToXaml();
        path.StrokeMiterLimit = style.Line.StrokeMiterLimit;
        path.Tag = style.Line.Width; // see #linewidthhack
        path.IsHitTestVisible = false;
        return path;
    }
}

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
charlennicommented, Jul 19, 2021

See #1157

0reactions
charlennicommented, Aug 8, 2021

Is this issue is solved? If yes, can you close it?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Stroke animation not happening
I am playing with CSS3 animation and dashoffset. This is the tutorial I have been following: https://jakearchibald.com/2013/animated-line- ...
Read more >
stroke-dashoffset
The stroke-dashoffset property in CSS defines the location along an SVG path where the dash of a stroke will begin. The higher the...
Read more >
DrawSVG progress 50%? - GSAP
Hi, I'm working on a animation for a circle that shows a "progress" between 0% and 100%. What I cannot understand is why...
Read more >
CSS Animation of stroke-dashoffset from 0 to 100% does ...
The attached test case contains an animation that animates the stroke-dashoffset property. The keyframes rule looks like: @keyframes trolley-path-move { to ...
Read more >
2020 SVG Animation | @keyframers 2.29.0 - YouTube
... https://cdpn.io/ pen /BayJqMX Skip around: 2:10 Animation overview 3:22 ... 2020 SVG Animation | Using pathLength=1 with stroke- dashoffset ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found