How to animate strokeDashOffset in Adobe XD? - javascript

In Adobe XD, I have a dashed Path (or Line) that I need to animate. In other software, I've always done this by animating the path's dash offset (its "phase"). In Adobe XD, this property exists but is not exposed through the user interface.
I've done some digging and found in the plugin API that Line and Path derive from GraphicNode, and that they both have a strokeDashOffset property.
I've made a quick plugin that prints and sets these values, using something along the lines of:
function animateLineDashCommand(selection) {
let linesAndPaths = selection.items.filter(el => el instanceof Path || el instanceof Line);
linesAndPaths.forEach(el => {
el.strokeDashOffset = 10; // Or whatever test value I'm using.
});
}
This works just fine, and the path definitely changes its phase. However, if I place the same line on two different Artboards with different phases, then wire them together using Auto Animate, the property is ignored. I'm assuming that behind the scenes, this property isn't interpolated.
Does anybody know of a way to have this property animate and/or an alternative solution to path offset animation in Adobe XD? To my knowledge, animated SVGs or GIFs are not currently possible.
Thanks.
Update 1:
I have tried a manual frame-by-frase approach inside of a Component, where each state is essentially a frame with a different dash phase. The idea was to make the Component a contained timeline so that the animation could be reused in different Artboards. However, Components do not support the Time trigger, which means it can only be forwarded by an interaction (clicking, dragging, etc). This approach will not work.
Update 2:
Since Artboards do have access to the Time trigger, I can duplicate the Artboards as many times as I need frames, and have each containing path have its dash phase set accordingly. This works but is quite painful as it can require a large quantity of duplicated Artboards, which I want to avoid. There is also a performance penalty for animating between dense Artboards quickly.
I have made a quick GUI plugin to make this easier and uploaded it to GitHub. I'll keep this question open in case a better solution is found.

Related

How could I improve performance when repeatedly updating an SVG DOM in React JS?

Last year I tried to learn a bit React JS for making the project you can find here. I apologize for my rather vague / imprecise description below, but I'm by no means versed in this.
Basically, there is a single <svg> tag, which will contain a number of paths etc. as created by the user. The problem I have is that things become very slow the more paths are present. To my current understanding, this is due to the fact that the entire SVG DOM gets updated repeatedly upon user interactions that involve dragging the mouse or using the mouse wheel.
This holds true, particularly, for two user interactions:
a) Panning - all paths are being moved at the same time; I think one might circumvent this issue by taking a snapshot image first and moving that around instead. However, that's not a solution for the other user interaction, which is:
b) Expanding/collapsing paths - here, all paths are being modified in terms of coordinates of some of their points. That is, every path must be modified in a different way, but all of them must be modified at once, and this must happen repeatedly because it's a user interaction controlled with the mouse wheel where changes happen gradually and the user requires immediate visual feedback on these changes as they happen.
Particularly for b), I see no alternative that would involve a single transformation or something.
After extensive research last year, I came to the conclusion that choosing SVG to display and modify a lot of things dynamically on screen was a wrong decision in the first place, but I realized too late, so I gave up and have never touched it since. I'm pretty certain that there isn't any way to deal with the low performance that builds upon what I already have; I have no intention to start this project from scratch with a completely different approach. Also, the reason why I chose SVG was that it's easy to manipulate.
In summary, I'd basically like to get confirmation that there is no feasible way to rescue this project.

Moving object performance - ThreeJs

Context :
I'm working on a pretty simple THREE.JS project, and it is, I believe, optimized in a pretty good way.
I'm using a WebGLRenderer to display lot's of Bode plot extracted from an audio signal every 50ms. This is pretty cool, but obviously, the more Bode I display, the more laggy it is. In addition, Bodes are moving at constant speed, letting new ones some space to be displayed.
I'm now at a point where I implemented every "basic" optimization I found on Internet, and I managed to get a 30 fps constantly at about 10.000.000 lines displayed, with such a bad computer (nVidia GT 210 and Core i3 2100...).
Note also i'm not using any lights,reflections... Only basic lines =)
As it is a working project, i'm not allowed to show some screenshots/code, sorry ...
Current implementation :
I'm using an array to store all my Bodes, which are each displayed via a THREE.Line.
FYI, actually 2000 THREE.Line are used.
When a Bode has been displayed and moved for 40s, it is then deleted and the THREE.Line is re-used with another one. Note that to move these, I'm modifying THREE.Line.position property.
Note also that I already disabled my scene and object matrix autoUpdate, as I'm doing it manually. (Thx for pointing that Volune).
My Question :
Do the THREE.Line.position modification induces some heavy
calculations the renderer has already done ? Or is three.js aware that my object did not change and
avoid these ?
In other words, I'd like to know if rendering/updating the same object which was just translated is heavier in the rendering process than just leaving it alone, without updating his matrix etc...
Is there any sort of low-level optimization, either in ThreeJS about rendering the same objects many times ? Is this optimization cancelled when I move my object ?
If so, I've in mind an other way to do this : using only two big Mesh, which are folowing each other, but this induces merging/deleting parts of their geometries each frames... Might it be better ?
Thanks in advance.
I found in the sources (here and here) that the meshes matrices are updated each frame no matter the position changed or not.
This means that the position modification does not induce heavy calculation itself. This also means that a lot of matrices are updated and a lot of uniforms are sent to the GC each frame.
I would suggest trying your idea with one or two big meshes. This should reduce javascript computations internal to THREE.js, and the only big communications with the GC will be relative to the big buffers.
Also note that there exists a WebGL function bufferSubData (MSDN documentation) to update parts of a buffer, but it seems not yet usable in THREE.js

Reflow/Layout performance for large application

I am using GWT to build a HTML application where the performance is correct in general.
Sometimes, it can load many objects in the DOM and the application becomes slow. I used Chrome Developer Tools Profiler to see where that time was spent (under Chrome once the app is compiled ie no GWT overhead) and it is clear that the methods getAbsoluteLeft()/getBoundingClientRect() consume the major part of this time.
Here is the implementation used under Chrome (com.google.gwt.dom.client.DOMImplStandardBase) :
private static native ClientRect getBoundingClientRect(Element element) /*-{
return element.getBoundingClientRect && element.getBoundingClientRect();
}-*/;
#Override
public int getAbsoluteLeft(Element elem) {
ClientRect rect = getBoundingClientRect(elem);
return rect != null ? rect.getLeft()
+ elem.getOwnerDocument().getBody().getScrollLeft()
: getAbsoluteLeftUsingOffsets(elem);
}
This makes sense to me, as the more elements in the DOM, the more time it may take to calculate absolute positions. But it is frustrating because sometimes you know just a subpart of your application has changed whereas those methods will still take time to calculate absolute positioning, probably because it unnecessarily recheck a whole bunch of DOM elements. My question is not necessarily GWT oriented as it is a browser/javascript related problem :
Is there any known solution to improve GWT getAbsoluteLeft/javascript getBoundingClientRect problem for large DOM elements application ?
I did not find any clues on the internet, but I thought about solution like :
(reducing number of calls for those methods :-) ...
isolate part of the DOM through iframe, in order to reduce the number of elements the browser has to evaluate to get an absolute position (although it would make difficult components to communicate ...)
in the same idea, there might be some css property (overflow, position ?) or some html element (like iframe) which tell the browser to skip a whole part of the dom or simply help the browser to get absolute position faster
EDIT :
Using Chrome TimeLine debugger, and doing a specific action while there are a lot of elements in the DOM, I have the average performance :
Recalculate style : nearly zero
Paint : nearly 1 ms
Layout : nearly 900ms
Layout takes 900ms through the getBoundingClientRect method. This page list all the methods triggering layout in WebKit, including getBoundingClientRect ...
As I have many elements in the dom that are not impacted by my action, I assume layout is doing recalculation in the whole DOM whereas paint is able through css property/DOM tree to narrow its scope (I can see it through MozAfterPaintEvent in firebug for example).
Except grouping and calling less the methods that trigger layout, any clues on how to reduce the time for layout ?
Some related articles :
Minimizing browser reflow
I finally solve my problem : getBoundingClientRect was triggering a whole layout event in the application, which was taking many times through heavy CSS rules.
In fact, layout time is not directly proportional to the number of elements in the DOM. You could draw hundred thousands of them with light style and layout will take only 2ms.
In my case, I had two CSS selectors and a background image which were matching hundred thousands of DOM elements, and that was consuming a huge amount of time during layout. By simply removing those CSS rules, I reduce the layout time from 900ms to 2ms.
The most basic answer to your question is to use lazy evaluation, also called delayed evaluation. The principle is that you only evaluate a new position when something it depends upon has changed. It generally requires a fair amount of code to set up but is much cleaner to use once that's done. You'd make one assignment to something (such as a window size) and then all the new values propagate automatically, and only the values that need to propagate.

How to properly use HTML5's canvas within JavaScript classes?

First off, I use the term "classes" to mean functions with prototypes that might be in a separate file from the main initializing file for what I'm working on.
Now for my question/issue:
I'm working on building something in JavaScript/HTML5, and trying to program it "properly" (ie. using prototypes with formats that are, I hope, standard). In my main JavaScript file, I have methods that have create use an instance (basically the root instance of my OOP/prototype based script) that sets up the canvas.
I also have another file that is loaded which contains a 'class' for creating clickable buttons. As of right now, I'm just trying to get the buttons to draw on the canvas, however, I can't access the instance of the canvas because I believe the instance is out of scope (which is fine, I don't want everything I do to contain a long dot-notation for referencing instances). However, this is giving me trouble when trying to draw a rectangle on the canvas, considering the 'class' the button is in, isn't able to reference the context outside of it.
I found a way around this by using something along the lines of:
function CreateButton(btn_x, btn_y, btn_width, btn_height, btn_txt) {
// ... (check and define parameters)
CreateButton.prototype.custom_canvas = document.getElementById('root_canvas');
CreateButton.prototype.ctxt = this.custom_canvas.getContext('2d');
CreateButton.prototype.ctxt.fillStyle = '#666666';
CreateButton.prototype.ctxt.fillRect(this.x, this.y, this.width, this.height);
}
Basically, it's writing on top of the canvas with a canvas of the same name? I'd assume that I can still manipulate the regular canvas afterwards and it would just act as if it was a single canvas element. I worried that redrawing on the canvas might use up a lot of memory when many things are added, however, no matter the method, writing on top of the canvas can't be avoided (even when in the same scope).
Any suggestions, or help? Or is my method of using the same canvas within a different class acceptable?
Thanks for any feedback.
[UPDATE]
Hmm, maybe I should try passing the context as a parameter and just using that.
...Or should I just make the canvas a global object? Any suggestions?
I guess you could try to implement some sort of "WidgetManager" that retains reference to canvas and your widgets. It will use a callback mechanism to render widgets. Each widget (ie. in this case Button) will have certain kind of rendering states (pressed, released) and some kind of internal state. Other widgets might have more complicated states.
Note that "WidgetManager" should probably keep track of widget "z" and user presses (which widget was hit). Based on this it should be able to trigger handlers bound to widgets. In a way you have to reinvent what basic UI libs do already. :)
I think you are better off by working out your design this way before moving into the implementation phase. A lot depends on what you really want to with it. :)
Note that you can simplify rendering and checks a lot by using multiple canvasii instead of just one. In this case you'll have to deal with z-index and absolute positioning but at least you get to piggyback on some of the existing stuff without having to implement it yourself.

Drawing Application Zoom Concept

I'm building an application on top of canvas, it consists of a simple DOM that gets redrawn on every mouse move (yes, it is necessary), for performance issues not every part part gets redrawn only what is needed.
The app is working well but I'd like to add the zoom feature, the way I see it, it can be done in three different ways:
1 - Every DOM element gets recalculated (position and size) every time a user zooms in or out - it might have issues with precision and its not a very good abstraction
2 - The canvas has a resolution property (i.e. when the user zooms out resolution might change from 1 to .75) - there will be a need to make the calculations on every redraw
3 - Use the built in translate() and scale() methods - possibly the most elegant and fastest solution, however it is not intuitive at all, it might be difficult to understand how it is being done latter on by me or someone else (these methods work on the full canvas, first you would translate and scale on the canvas and afterwards everything you draw gets 'magicly' translated and scaled)
Which one is best or are there other possibilities I'm not thinking of?
I would use the builtin translate()/scale() methods. If you're worried about the performance and quality of any of these methods, you should try to do it in a way that you can swap it out for another of the options to compare, if the results end up giving you any concern.

Categories

Resources