As we wrote the SVG widgets for JavaScript

Disclaimer.

I must confess that initially, I had a task – to write on habré about our product. I had originally planned to restrict the usual PR essays describing the core features and functions our component, not concentrating attention on the details. Of course, such a story would fully disclose all of the chips of our product. On the other hand, he certainly would have caused you yawn for about the third paragraph.

Therefore, I will try to make this article was useful not only the author but also the readers. I will describe not so much what we did, how we did it. We begin, naturally, with the task that was standing before us.


Task

Here are widgets, you can do
Imagine that you – web-programmer who implements a complex SCADA system, dashboard (sorry, but a clear translation of the word in Russian I have not met), the interactive control system of metrics, or simply you need to insert into your website the watch with a clever design. In this case you need to add all sorts of scale, whirlings arrow (in English this is called Gauge), clock and other "gadgets", perhaps even interactive.

At first glance, this problem is solved quite simply. For example, there is a free component of Google Gauge and many different pieces that fall at the request in the same Google. On the other hand, in most of these libraries a set of options tend to be limited. As soon as you need to do something – starts to work, the principle of "easier to write".

Therefore, our task was to create a system that would not only customize the existing "krutilki", but to create them yourself from scratch. The system was allowed to assemble a widget of the graphical primitives (geometric shapes, arrows, special elements such as "tubes", "springs"), etc. in addition, we wanted to develop a scripting language with which the programmer can associate values of individual elements of the widget (e.g., a number in the text field and the position of arrows in scale).

So, a team consisting of two programmers, QA (who joined later) product Manager (all of which constantly interfered with his work, forcing all to change, "because the user is inconvenient or difficult"), began developing immediately after the holidays.

The case was facilitated by the fact that at the beginning of the project, we had the same product for Windows Forms. We successfully dragged out the object model of the widget and the widget editor for Windows.

We faced several technical problems that had to solve:
the

    cross-Platform. Widget was supposed to work on pure HTML+JavaScript, regardless of platform or technology.

    Standards. the Widget should support CSS for customization options.

    Performance. During the change values arrow should move smoothly, not to slow and not to load the processor on 100%.

    Interaction. the User should be able to set the value by dragging the arrow (in this widget, again, should not be slow).

    Easy. the Programmer should be easy and comfortable: the embedding of a widget in a Web page should occur with the use of several obvious lines of code. looks like JavaScript code for working hours

    Framework

    Before you begin, we needed to choose tools. I would venture to call holivar and the indignant cries that we "do not know how to cook JS", but still say. To write "pure" JavaScript is a task requiring excessive perseverance, care and too much "memory" of the programmer. The experience of custom development can convincingly say that the qualitative development of RIA applications using pure JavaScript takes a half to two times longer than creating the same system using Flash or Silverlight (may he rest in peace).

    In our case it was compounded by the fact that we had professional Silverlight team, which had to retrain (alas, the strategists of Microsoft sometimes can annoy their own developers). Accordingly, in order to reach the same level in working with "pure" JS, it would take several years and a couple of "fused" products.

    However, we found a very good out of the situation, which, incidentally, will actively recommend to all .NET developers who write RIAS using JavaScript. This output was a framework called Script#, which is being developed by Microsoft employees. It allows you to write C# code and translate it into JavaScript. And, I will say frankly: as JS-code, the output is very decent. Of course, the functionality there is limited, but saves the possibility to implement separate methods entirely on JS, after which they are translated unchanged to the output file. In General, if you write in C#, I recommend to try.

    Architecture


    SVG vs. Canvas
    a Widget to display the weatherit was Immediately clear that the development should be conducted using HTML5. The first question which we, of course, asked, had the choice between SVG and Canvas.

    On the one hand, SVG is perfectly suited to work with widgets collected from individual objects and primitives in the editor. On the other hand, we feared for SVG support in different browsers. I did not want to be in a situation when the component only works correctly in the latest version of Google Chrome. The more important point was the support for mobile devices: iOS, Android, and other of the zoo.

    If you look at the Canvas, a definite plus was hardware support in many browsers, but the obvious drawback is the need for a complex redrawing of objects when animating (for example, the motion of the arrow will have to redraw the part of the objects located under it).

    After some reflection we realized that the entire rendering system to be written in abstractions, do not depend on the specifics of SVG or Canvas (hi Cap). After this is implemented with a separate set of classes for rendering and redraw the objects using specific technology. In the first version we decided to limit SVG, since we are strongly attracted by the opportunity to twist the arrow attribute of the angle and not redraw the image on every turn. In addition, we wanted to use the standard SVG animation (alas, it had a big problem) and CSS to control the styles of the widget.

    Thus, while all the widgets are drawn in pure SVG.

    Object model
    looks like the structure of the widgetgiven the fact that we already had a system ready for Windows Forms, from which at least the editor we were going to actively use, it was decided to base the same object model and a little to adapt it for the Web.
    Given the fact that the development was carried out on Script#, porting the model has been reduced to banal "copy-paste". All attempts to write one code base, unfortunately, was not successful. There were too many differences in the behavior, rendering and other stuff. Because of this there was a bunch of conditional Directive and branches, which are only difficult to code and bring more harm than good. In the result, it was decided that the DRY principle is not offended. As time has shown we did the right thing – in the code of the web part is almost gone pieces, identical to the Windows part.

    However, to be objective, there are successful examples splitting code between C# and Script#.

    Serialization
    These balls are on the strings also movestorage WinForms widgets, which served as the starting point of our work, we have used our own binary format. Files in this format were read and stored by the widget editor. On the other hand, really like to see in the Web version of the widget asked a native JavaScript method. Obviously, we chose JSON.

    It was originally planned that JSON with the description of the widget will be created by the user (i.e. programmer that uses our product), but then this idea moved a little to the side. JSON object description is created when exporting the widget in the editor. In the first version we went the path of least resistance and screw standard JSON Serializer that comes with .NET Framework 3.5. Without going into implementation details, I will say that the object model of the widget in the standard way is serialized to JSON and the result is given to the programmer. The programmer when adding a widget to the page passes this JSON as a constructor parameter (in JavaScript). When you create the widget, the JSON is parsed by the engine, and it built a set of SVG primitives.

    One of the main drawbacks, which are already planned for the near future – an abundance of unnecessary information in the resulting JSON description. This is due to the fact that we use the standard mechanisms for serializing and get a copy of the object "as is", including a bunch of irrelevant properties (have, alas, to serialize all public fields). This JSON description looks monstrous and takes up a lot of space. We even have specially made JSON-inspector that programmers can see how their widget works, downloading the JSON description.

    By the way, we've added the ability to change any settings JSON at the time you create the widget, so it's very easy, having a common description, to vary the details: for example, to change the time scale to make the arrow invisible, etc.

    Implementation of


    Animation
    We took the types and description of animations from jQueryit is Clear that the arrow in the scale should not (in most cases) to move from one value to another "quantum leap", and should gradually pass all intermediate values and, a little behind the desired value, elegant back. Besides, it is important that the widget does not eat 146% CPU, performing it with smooth movement.

    Actually, originally we were trying to be honest to use CSS animations... Alas, we suffered a crushing defeat, losing the two weeks. This is probably the most striking moment where the full force of manifested features web development posed by a zoo of browsers who do not want to work with one standard.
    As a result, once it became clear that quod non licet licet Chrome Internet Explorer, we wrote JavaScript custom animation with preference and courtesans. It is strongly untied our hands. For example, we were able to implement almost all the variants of acceleration-deceleration and other beauty.
    The main fear that everything will slow down, was in vain, and as soon as we stopped to redraw the entire widget on a small position change arrows, all became simple to fly. Conclusion – normally optimized redrawing "by hand" SetInterval is not inferior in performance CSS animations, but it works the same in all browsers.

    CSS
    Hands all widgets configured using CSS
    We had a task – to give the programmer the ability to configure the colors of individual elements of the widget in CSS. In General, no problems there. Have the SVG element, he's got class, there are CSS settings for that class. The only thing that was disappointing is the inability to normally ask in a CSS gradient to the SVG. Specification to use a gradient fill to SVG objects, it must first declaratively described in the same SVG.

    So, I had to write a separate method in JavaScript which would allow to specify a gradient fill for the objects. Of course, this still allowed the programmer that fill ask, but to make all the "designer" settings in CSS, like we originally wanted, unfortunately, failed.

    Another very unpleasant aspect was the lack of a conical gradient fill to SVG. This is an extremely convenient way to fill for tools that use scales with colors, gradually changing, for example, from red to green. Accordingly, it has been used in a variety of widgets for WinForms. We had to approximate the conical gradient is linear, which turns out not always successfully.

    Interactivity
    This car panel - one widget, collected from several devicesWe basically wrote the widgets so that the user could, pulling the arrow or the slider, set new value. Of course, the widget when it calls the callback and notifies JavaScript.

    Handling mousedown and mouseup were made for div'and in which you have inserted the widget. When you click the mouse, we determined which element was clicked and thus build further logic depending on it.

    It is quite complicated task was to detect when the user clicks on an area with relatively complicated boundary. In "normal" languages this is usually done using the HitTest method, but in JavaScript for SVG, unfortunately, no.

    Because the border region is typically set is not a polygon, but by a bézier curve, use simple algorithms-type of summation of angles did not work. For testing clicks on most of the sites are quite suitable Bounding Box, but there are exceptions in the form of large and thin Crescent-shaped elements of the type of a Crescent.

    In the end, it was decided to write HitText for each component separately, remembering analytic geometry and approximarely object more simple primitives.

    Another small problem arose when we began to redraw the widget in response to user actions. For example, the user "grabs" the arrow and begins to twist it with great speed. Naturally, many things need to be redrawn. The positive aspect of using SVG vector had the opportunity to redraw (and in most cases – just turn up) only floating objects and not touch the rest, while using a Canvas we would have to redraw also the object under the arrow. On the other hand, sometimes I had to redraw quite a lot.

    Of course, initially all incredibly retarded, but after a couple of hours of optimization became easy to fly. And flew until, until we allowed users to add widget in complex scripts, which forced me to redraw not only the moving arrow, but also a bunch of other components related to it scripts.
    By the way, interesting note: a local version of JavaScript on the page loaded from the file system runs much slower than on the same page in the same browser, but downloaded from the server. Who knows why, please share in the comments.

    In order to implement support for gestures for touch-enabled devices, you need to handle completely different event, such as touchstart, touchend, touchmove, etc. Therefore, this support had to write separately.

    Scripts and the internal logic
    Linear widgets. The height of the triangle to the right varies with the value of the scaleSharpShooter Gauges was famous for the fact that he kept internal scripts. These scripts were a key moment in the development of the widget, as determined by the binding position of graphic elements scales and rulers.

    Additionally, the script allowed us to relate the dimensions of the elements to format the output text, and so on. The presence of scripts was possible to create a widget of any complexity, describing the interaction between the individual objects and primitives inside it, not to impose this task on the shoulders of the programmer that embeds a widget into your product (encapsulation, yeah).

    Initially we had the idea to completely convert the internal scripting in JavaScript syntax. But, thinking of our old users who will not understand if their WinForms widgets suddenly stop working in the new version of the system, we decided to leave the internal scripting as it is and translate the scripts in JS when you export. However, the amount of this heroic initiatives has exceeded all conceivable terms and considerably delayed the release.

    Despite the undeniable power of scripting, there are outright cons.

    First, the problem is the implicit conversion of objects (e.g. date) to the line. The fact that in JavaScript .NET date is converted to string differently, which adds a fair amount of incompatibility between the versions for Windows and HTML5.

    Second, initially there was a sharp deterioration in the performance of the widget if you have a complex script links. However, we do not lose heart and started the optimization. After about a week of complex scripts almost no effect on the animation, and the slowdown was noticeable only during very fast drag and drop in IE. In chrome everything worked perfectly. On this decided to stay.

    smaller
    Widget with a stretched picture-\"skin\"after Reading this article, you guys made some comments about other issues that were worth mentioning, so in this section I will describe the most typical ones.

    the inability to calculate the size of the text. In SVG, as in many other graphical systems, there is no way to determine in advance what size is the text after rendering. As the version for WinForms slightly more than fully relied on this feature GDI, we had to invent clever scheme for its implementation. Now all the text is drawn twice. Once in order to get the dimensions, and again with normal positioning.

    Problem with RGBA on iOS devices. Closer to the middle of the development, testing widgets on different device models, we noticed that on the iPhone 4 and iPad our fill was completely black. Of course, the widgets because of this looked very Gothic and funny, but still completely wrong. After a little research, we realized that the RGBA format, which was used to determine colors, is not supported in Safari under iOS4+ (surprisingly, the iPhone 3G, everything worked). As a result, we have divided the RGBA value into two separate (color and opacity) everywhere we used it. Helped.

    Conclusions

    I have said that I wanted to avoid bias the article in the “marketing bullshit” and make it more or less useful from a technical point of view. So here are a few insights that may help when working with SVG in JavaScript.

    Conclusion 1. Most modern browsers support the normal work with SVG and visually give the same result.
    2. Framework Script# copes with its task, allowing you to use C# for RIA development with JavaScript and HTML5.
    3. Conclusion Standard CSS animation is too crude and browsere-dependent to use it for work. "Manual" animation, built on the redraw parts of SVG works everywhere the same way and with minimum optimization, it is not slow (even in IE).
    Conclusion 4. From CSS you can normally configure the style SVG elements. In order to configure the gradient fill, it first must be declared in the same SVG, so one CSS is not enough. Conical fill, alas, is not supported.
    observation 5. iOS device (except the oldest) don't support color in the format RGBA. Therefore, when you specify the styles you need to set RGB and Alpha separately.
    Output. We made a cool product that we like :).
    And, of course, to see what we got you can here.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Briefly on how to make your Qt geoservice plugin

Database replication PostgreSQL-based SymmetricDS

Yandex.Widget + adjustIFrameHeight + MooTools