GithubHelp home page GithubHelp logo

kristofferstrube / blazor.svgeditor Goto Github PK

View Code? Open in Web Editor NEW
293.0 9.0 47.0 41.97 MB

A basic SVG editor written in Blazor.

Home Page: https://kristofferstrube.github.io/Blazor.SVGEditor/

License: MIT License

C# 73.19% HTML 25.88% CSS 0.83% JavaScript 0.09%
svg blazor blazor-webassembly webassembly editor path renderer drag-and-drop wysiwyg

blazor.svgeditor's Introduction

KristofferStrube.Blazor.SVGEditor

License: MIT GitHub issues GitHub forks GitHub stars NuGet Downloads (official NuGet)

A basic HTML SVG Editor written in Blazor.

Showcase

Demo

The WASM sample project can be demoed at https://kristofferstrube.github.io/Blazor.SVGEditor/

Getting Started

Prerequisites

You need to install .NET 7.0 or newer to use the library.

Download .NET 7

Installation

You can install the package via NuGet with the Package Manager in your IDE or alternatively using the command line:

dotnet add package KristofferStrube.Blazor.SVGEditor

The package can be used in Blazor WebAssembly and Blazor Server projects. In the samples folder of this repository, you can find two projects that show how to use the SVGEditor component in both Blazor Server and WASM.

Import

You need to reference the package to use it in your pages. This can be done in _Import.razor by adding the following.

@using KristofferStrube.Blazor.SVGEditor

Add to service collection

To use the component in your pages you also need to register som services in your service collection. We have made a single method that allows you to add the needed service which you use like so:

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

// Adding the needed services.
builder.Services.AddSVGEditor();

await builder.Build().RunAsync();

Include needed stylesheets and scripts

The libraries that the component uses also need to have some stylesheets and scripts added to function. For this, you need to insert the following tags in the <head> section of your index.html or Host.cshtml file:

<link href="_content/BlazorColorPicker/colorpicker.css" rel="stylesheet" />
<link href="_content/Blazor.ContextMenu/blazorContextMenu.min.css" rel="stylesheet" />
<link href="_content/KristofferStrube.Blazor.SVGEditor/kristofferStrubeBlazorSVGEditor.css" rel="stylesheet" />

The library uses Scoped CSS, so you must include your project-specific .styles.css CSS file in your project for the scoped styles of the library components. An example is in the test project in this repo:

<link href="KristofferStrube.Blazor.SVGEditor.WasmExample.styles.css" rel="stylesheet" />

At the end of the file, after you have referenced the Blazor Server or Wasm bootstrapper, insert the following:

<script src="_content/Blazor.ContextMenu/blazorContextMenu.min.js"></script>

Adding the component to a site.

Now, you are ready to use the component in your page. A minimal example of this would be the following:

<div style="height:80vh">
    <SVGEditor Input=@Input InputUpdated="(string s) => { Input = s; StateHasChanged(); }" />
</div>

@code {
    private string Input = @"<rect x=""0"" y=""0"" height=""200"" width=""200"" fill=""green"" stroke=""blue"" stroke-width=""2""></rect>";
}

Tag type support and attributes

  • RECT (x, y, width, height, fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
  • CIRCLE (cx, cy, r, fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
  • ELLIPSE (cx, cy, rx, ry, fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
  • POLYGON (points, fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
  • POLYLINE (points, fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
  • LINE (x1, y1, x2, y2, fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
  • TEXT (x, y, style:(font-size,font-weight,font-family), fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
  • PATH (d, fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
    • Movements
    • Lines
    • Vertical Lines
    • Horizontal Lines
    • Close Path
    • Cubic Bézier Curve
      • Shorthand aswell
    • Quadratic Bézier Curve
      • Shorthand aswell
    • Elliptical Arc Curve
      • Needs more work for radi interaction
  • G (fill, stroke, stroke-width, stroke-linecap, stroke-linejoin, stroke-offset)
    • Missing scaling of children
  • ANIMATE
    • Support for showing all Animate tags when Playing
    • Support for editing Fill animation values
    • Support for editing Stroke animation values
    • Support for editing Stroke Offset values
  • LINEARGRADIENT (x1, x2, y1, y2, and stops)

Current goals

  • Add support for touch devices
  • Support errors better to recover instead of crashing when parsing malformed SVGs.

Issues

Feel free to open issues on the repository if you find any errors with the package or have wishes for features.

Related articles:

Diagram of relation between core types in the project.

  graph LR;
      subgraph Shapes
      Circle
      Ellipse
      Line
      Text
      Path
      Polygon
      Polyline
      Rect
      G
      end
      subgraph Path Instructions
      ClosePathInstruction
      CubicBézierCurveInstruction
      EllipticalArcInstruction
      HorizontalLineInstruction
      LineInstruction
      MoveInstruction
      QuadraticBézierCurveInstruction
      ShorthandCubicBézierCurveInstruction
      ShorthandQuadraticBézierCurveInstruction
      VerticalLineInstruction
      end
      ISVGElement[[ISVGElement]]
      Shape[[Shape]]
      BasePathInstruction[[BasePathInstruction]]
      BaseControlPointPathInstruction[[BaseControlPointPathInstruction]]
      IPathInstruction[[IPathInstruction]]
      Shape-.->ISVGElement;
      SVG------ISVGElement
      G---ISVGElement;
      Path-.->Shape;
      Path-----IPathInstruction
      G-.->Shape;
      Circle-.->Shape;
      Ellipse-.->Shape;
      Line-.->Shape;
      Text-.->Shape;
      Polygon-.->Shape;
      Polyline-.->Shape;
      Rect-.->Shape;
      ClosePathInstruction-->BasePathInstruction
      CubicBézierCurveInstruction-->BaseControlPointPathInstruction
      EllipticalArcInstruction-->BasePathInstruction
      HorizontalLineInstruction-->BasePathInstruction
      LineInstruction-->BasePathInstruction
      MoveInstruction-->BasePathInstruction
      QuadraticBézierCurveInstruction-->BaseControlPointPathInstruction
      ShorthandCubicBézierCurveInstruction-->BaseControlPointPathInstruction
      ShorthandQuadraticBézierCurveInstruction-->BaseControlPointPathInstruction
      VerticalLineInstruction-->BasePathInstruction
      BaseControlPointPathInstruction-->BasePathInstruction
      BasePathInstruction-->IPathInstruction

blazor.svgeditor's People

Contributors

kristofferstrube avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blazor.svgeditor's Issues

Feature: Panning and zooming on mobile

We want to be able to support panning and zooming on mobile devices that don't have a mouse.

We currently support panning on devices that have a mouse by listening for the middle-button being pressed. And we support zooming by listening for the scroll-wheen being rolled.

There exist many methods for supporting panning on touch devices. One such approach is to start panning if the user has multiple fingers on the screen. Some only need to have 2 or more fingers on the screen to support a generic free movement of zoom, panning, and coordinate system rotation. But there are other options, so I'm very open to ideas. We haven't implemented rotation of the coordinate system on the desktop yet so I would probably also wait to implement that until I have found an intuitive interaction method for rotating on the desktop.

Support for the filter

I noticed that support for the filter element is not added in the code. Is there a plan to do it in the future?

Feature: Add automatic centering and scaling function after svg file loaded

After SVGEditor.Elements is loaded, is it possible to center all elements to SVgEdit according to the display area, and automatically match the appropriate scaling(maybe a padding value there) according to the display area so that it fills the display area? I saw that SVGEdit has “SVGEditor. Scale” and “SVGEditor.Translate”, but it seems that after loading is completed, the boundarybox of all elements and the boundarybox of SVgEdit need to be calculated in order to calculate the new "Translate" and "Scale". that will be great with this automatic centering and scaling function.

form :
3

to (center and bigger/smaller to fill the display area):
4

Feature: Virtualization

Currently, we recalculate the positions of lines, markers, and the shapes themselves even when they are outside the bounds of the viewport. To minimize this problem we should use the concept called virtualization.

There are many approaches to this, but the most important part is that we should probably only query the selectable points with some margin. The next part is that we currently loop through all shapes in the SVG DOM linearly when we render them. This means that we have to go through all the first hierarchy shapes even the ones we don't render. Instead, we should speak to store the shapes in a tree structure so that we only need to traverse some constant number of paths from the root to render the visible shapes. One such tree is a KD-tree. But before going with this approach I need to make some preliminary analysis of what number of shapes this would be faster for and in which scenarios it applies if any.

Feature: Possible natural mapping for stroke-dasharray

The stroke-dasharray attribute has a lot of possibilities for natural mapping in some control. Right now, we simply take a free-text input that is inserted in the shape.

I imagine som kind of drag and drop of stops on a line similar to how we control the stops of a LinearGradient. But I'm open to ideas.

Self Closing Tags Not Supported

The following SVG lines should both render in the same way. However, the set of the self-closing tags only renders the first object. Presumably it's not correctly identifying the tag is closed.
Does not work correctly:
<rect x="120" y="380" height="125" width="140" fill="green" stroke="blue" stroke-width="2"/> <circle cx="450" cy="300" r="80" stroke="green" stroke-width="1" fill="url('#purple_blue_up_right')"/>
Does work correctly:
<rect x="120" y="380" height="125" width="140" fill="green" stroke="blue" stroke-width="2"></rect> <circle cx="450" cy="300" r="80" stroke="green" stroke-width="1" fill="url('#purple_blue_up_right')"></circle>

Bug: Complete Without Close Instruction gets `ArgumentOutOfRangeException`.

Stack trace:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: ArgumentOutOfRange_IndexMustBeLess Arg_ParamName_Name, index
System.ArgumentOutOfRangeException: ArgumentOutOfRange_IndexMustBeLess Arg_ParamName_Name, index
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder , RenderFragment , Exception& )

Feature: Rotation

It would be nice if we could rotate shapes. We can do this using CSS transforms but we should make sure to scope this feature as it can get rather complex if we have multiple series of rotation combined with scaling and translation.

There are multiple possibilities for scaling. The best one would be to have some natural mapping like a third anchor that can be used explicitly for rotation. Other simpler ideas could be to have discrete options for rotation like "Rotate 90 degrees" or to have some input in the context menu to control the rotation numerically.

Feature: Fit viewport to Shapes

It would be awesome to be able to call some function on the SVGEditor taking a list of shapes that would zoom and translate the viewport of the editor so that the given shapes are centered in the middle of the editor with some padding from the edge of the bounding area.

This could be especially useful in the Blazor.GraphEditor library where nodes' positions are updated continuously.

Bugix : `Elements.IndexOf(element)` value should be checked to avoid crashes

When removing elements and adding new one then removing, it can crash

in SVG.razor.cs

        protected override void OnInitialized()
        {

            ElementSubject
                .Buffer(TimeSpan.FromMilliseconds(33))
                .Where(updates => updates.Count > 0)
                .Subscribe(updates =>
                {
                    updates
                        .Distinct()
                        .ToList()
                        .ForEach(element =>
                        {
                            ElementsAsHtml[Elements.IndexOf(element)] = element.ToHtml();
                        });
                    UpdateInput();
                });
        }

Don't you think Elements.IndexOf(element) value should be checked to secure the code and avoid crashes ?
image

Seperating decimal numbers that have no spaces but are seperated by dots.

Sometimes we get input like this for some instructions in path data:

a.457.318.914.317.61.78

Which should be made into:

a 0.457 0.318 0.914 0.317 0.61 0.78

The idea would be to find all places where there is a . char with either an alphabetic char before or numbers that continue until another . without any spaces.

I tried doing something like this.

string pattern = @"\.(\d+)\.";
string replacement = "$0 0.";
string input = "a.457.318.914.317.61.78";
string result = Regex.Replace(input, pattern, replacement);

but that result is:

a.457. 0.318.914. 0.317.61. 0.78

So I need to match on [a-zA-Z] optionally and I need somehow to match dots twice and fix some extra .´s.

One could also take a look at the implementation details for Path data: https://www.w3.org/TR/SVG/paths.html#PathDataBNF
where they have written a full parse three in BNF that could potentially capture all of this without any edge cases like this.

But for this second approach, we need to redo the full parsing which will potentially make it tougher to read.

Annotations on an image

I have a need to annotate an image and I was thinking that this project would be a great start.

is it possible to add an image and then make the drawings on top of that image?

Bug: Selection is lost when zooming with touch

When you use touch to zoom, all selected shapes are deselected. This was implemented this way as it was not clear the user had touched the back facade as a part of the zoom, which should deselect all shapes.
This has brought pain as users spend a lot of time selecting shapes, which they lose when zooming. To mitigate this, we should only deselect all shapes if a touch interaction starts with pressing the back facade and has not performed any zooming.

Feature: Background graphics

It seems sensible that it should be possible to add some background graphics/shapes to either serve as context for what is being edited or to give an idea of the zoom level/placement of the viewport.

This can be implemented in many ways, so I will start off by describing some simple cases and then later try to come up with an implementation that can support these cases.

  • Grid: It would be nice to have a grid layout on the whole-number coordinates so that the user can relate the size of their shapes to this and align different parts of shapes to each other more easily. A problem with this is that it can be rather heavy to render many shapes in the background that will move a lot when the viewport moves.
  • Static Shape: If the goal for using the editor is to sketch over something, then having a set of shapes that can't be selected, which are placed in the background, would be nice.

For both cases, the background shapes/grid would need to be transformed according to where the viewport is, so we can't simply place some static SVG/HTML in the background.

Before specifying any solution further, I would love to get feedback from the library users to see what concrete cases you have.

SnapToInteger should use midpoint rounding

Currently, when SnapToInteger is activated, we use casts to ints in many places to ensure whole numbers, but it would give a smoother experience if we used Math.Round instead. So, let's update this.

Refactoring with interfaces

Kristoffer,
your work has been really impressive, in this project.

In particular, I found it incredibly useful to use the editor for Bezier curves, in a project I'm working on.

image

My only (small) concern is that I had to create a new SVG element, removing all the things I don't need. I attach the resulting set of projects, where you can see that I had to duplicate a lot of other classes, because creating an SVG2 element, there is a strong dependency on the concrete class in other elements.
I'm wondering if it could be possible to refactor things a little bit to make things cleaner... I'm available to help, if you want...

Blazor.SVGEditor.zip

SVGEditor code in a server-side app : System.InvalidOperationException: The current thread is not associated with the Dispatcher

When using the SVGEditor code in a server-side app and editing the svg as text in textarea

"System.InvalidOperationException: The current thread is not associated with the Dispatcher"
<SVG @ref=SVG1 Input=@input1 InputUpdated="(string s) => { Input1 = s; StateHasChanged(); }" />

After reading this
<SVG @ref=SVG1 Input=@input1 InputUpdated="(string s) => { Input1 = s; InvokeAsync(StateHasChanged); }" />
Solve the issue

Access to internal instructions (and adding new elements by instructions)

Kristoffer,
really a good job, in creating this library. It's very clear and useful out of the box, but I'm wondering if it would be possible to make it even more flexible, giving access to the internal instructions.
I went quickly through the inner code, and I feel that your work on the internal representation is even more interesting than the front end side. For what I see, in this moment I can only work passing and receiving the svg representation, with the cumbersome syntax.
For scientific drawing, for example, it would be much more flexible to be able to pass and receive the list of instructions of a path.
...or maybe I'm missing something, here... :-)
Congratulations again, I would like to have your opinion, when you can, of course.
Thank you.
AB

Color Picker css not load

The BlazorColorPicker.bundle.scp.css file is not load in _context.The ColorPicker style is displayed incorrectly and can not display and hidden.

Disable Panning feature

Hello,

I don't understand the purpose of the Disable Panning feature because I don't see any difference whether it is disabled or not. Could you please clarify?

Thank you.

Link

Could not find 'blazorContextMenu.SetMenuHandlerReference' ('blazorContextMenu' was undefined)

Hi Kristoffer,
I followed the steps for integrating the SVG Editor, but during the runtime I get the error below.
I tried also manually to register the service without success. Could you help?
Best regards,
Nedim.

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Could not find 'blazorContextMenu.SetMenuHandlerReference' ('blazorContextMenu' was undefined).
Error: Could not find 'blazorContextMenu.SetMenuHandlerReference' ('blazorContextMenu' was undefined).
at https://localhost:5002/_framework/blazor.webassembly.js:1:328
at Array.forEach ()
at a.findFunction (https://localhost:5002/_framework/blazor.webassembly.js:1:296)
at _ (https://localhost:5002/_framework/blazor.webassembly.js:1:2442)
at https://localhost:5002/_framework/blazor.webassembly.js:1:3330
at new Promise ()
at Object.beginInvokeJSFromDotNet (https://localhost:5002/_framework/blazor.webassembly.js:1:3311)
at Object.Gt [as invokeJSFromDotNet] (https://localhost:5002/_framework/blazor.webassembly.js:1:62569)
at Object.Ii (https://localhost:5002/_framework/dotnet.7.0.10.vzm6zpl6t5.js:5:71974)
at _mono_wasm_invoke_js_blazor (https://localhost:5002/_framework/dotnet.7.0.10.vzm6zpl6t5.js:14:103886)
Microsoft.JSInterop.JSException: Could not find 'blazorContextMenu.SetMenuHandlerReference' ('blazorContextMenu' was undefined).
Error: Could not find 'blazorContextMenu.SetMenuHandlerReference' ('blazorContextMenu' was undefined).

Bugfix : Deleting multiple selection with Delete button does not work and create console error

Bugfix : in ShapeEditor::KeyUp

                if (eventArgs.Key == "Delete")
                {
                    SVGElement.SVG.Remove(SVGElement.SVG.SelectedElements);
                    SVGElement.SVG.SelectedElements.Clear();
                }

In SVG.razor.cs I commented SelectedElements.Clear(); So this will not work with contextual menu ( I do not use them anymore in my fork).

 public void Remove(ISVGElement SVGElement)
        {
            ElementsAsHtml.RemoveAt(Elements.IndexOf(SVGElement));
            Elements.Remove(SVGElement);
            //SelectedElements.Clear();
            UpdateInput();
            RerenderAll();
        }

        public void Remove(List<ISVGElement> elements)
        {
            foreach (var element in elements)
            {
                this.Remove(element);
            }
        }

Factorization canbe improved to avoid unnecessary code like

            UpdateInput();
            RerenderAll();

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.