Best way to animate a constant (unknown) data flow with Vue.js? - javascript

I'm currently running into a problem trying to get a smooth animation.
I'm using vue + electron, with the main processes sending data to the renderer process at about 16-33ms (30-60fps). When I receive the data in my component, I update the data property and it is bound to the style property of the element. This does work, but there's quite a bit of jitter. I'm curious if there's a better way to handle this. Is there something similar to requestAnimationFrame()? Thank you.
Simplified example:
<template>
<div>
<img :style={'transform': `translate(${x}%, ${y}%)} src=""></img>
</div>
</template>
<script>
data: function () {
return {
x: 50,
y: 50
}
},
mounted () {
// this is coming every ~16-33ms
this.$electron.ipcRenderer.on('data', (e, data) => {
this.x = data.x
this.y = data.y
})
}
</script>

You created a multi layered issue there.
Electron IPC's are slow, the reason for that is that they serialize/de-serialize JSON objects and not buffer, also the main and render process have to sync. A simple solution to this specific issue is to write an preload script and bring your logic from the main into the render thread. No need for IPC, no serialization, direct access to NodeJS and any native node module.
For constant animations on values CSS animations are often lacking on low-end PC's they tend to interrupt animations, so it's advisable to use a framework for tweening/animation an example of this is anime.js or this self writting vue example from the vue docs https://v2.vuejs.org/v2/guide/transitioning-state.html#Dynamic-State-Transitions
I hope this brings you on the right track ;)

Related

How do I run Prismjs in a Svelte component?

I'm simply trying to implement Prismjs into my Svelte project.
I'm loading the CSS in my <head> and I'm importing the JS in my Svelte component.
I'm running into a similar issue described here: https://medium.com/get-it-working/get-prismjs-working-in-react-a6d989e59290
Basically, it seems I need to re-run the script after the component has rendered. I'm trying to accomplish this in the same way as it's described in React in the article by running Prism.highlightAll() inside of the onMount lifecycle.
I was hoping this approach would work, but I'm not getting anywhere. I still have to refresh the page to see the styles take effect. What am I missing here?
tldr; this wasn't a Svelte issue. It was an issue with my serializer for Sanity's blocks-to-html utility.
Hi #RichHarris! Upon putting together a repo for reproduction, I found prism worked as expected. I was then able to narrow down my issue.
I should have mentioned initially, I'm using Sapper and pulling in content from Sanity. I made a modification to the serializer for Sanity's block-content-to-html utility that is converting my portable text to markup. The <pre> tag is getting data-language='HTML' from Sanity, and the Prism CSS is expecting class="language-HTML. I modified my serializer to:
code: ({node}) =>
h(
"pre",
{
"data-language": node.language, "class": "language-" + node.language
},
h("code", {}, node.code)
)
Which was working after refreshing. The class attribute was getting added, and the CSS applied. I'm just sort of thumbing through the code as I don't really know how blocksToHtml works, but upon changing the object property to className, everything is working as expected:
code: ({node}) =>
h(
"pre",
{
"data-language": node.language, className: "language-" + node.language
},
h("code", {}, node.code)
)
Thanks for taking a look, Rich! I'm really enjoying Svelte!

Using Redux with vanilla JS how can I reach it without passing it to every component?

I'm using Redux in a vanilla JS project. I have a bunch of small modular UI files and controllers and such. In those UI files I might have code like:
const ExampleForm = function (StoreInstance) {
return $('<form />', {
submit: () => {
StoreInstance.dispatch({
type: 'EXAMPLE_DISPATCH',
post: {
message: $TextareaComponent.val()
}
})
return false
}
})
}
The issue is I have a lot of simple view files like this and many of them are nested and I'm finding it to be ugly and error prone to have the store passed as a param to everything.
For example, I trimmed it for brevity but the form component has form element components such as a textarea. Currently I see two options of managing the Store:
Setting it to window when creating it in my entry file (index.js) and then just accessing Store globally. This seems the nicest, although not "best practice" and makes unit testing and server side rendering a bit harder.
Passing it to every component tediously. This is my example above. This I'd consider as "best practice" but it's pretty annoying to do for every file you make almost.
I'm wondering if there's any alternatives or tricks to passing the store instance. I'm leaning towards just making it global.
You could use the constructor pattern and create every view as new ConnectedView(). The ConnectedView would have a memoized instance of the store (this.store within the view), so it doesn't need to be global.

Activate a LoadingMask for a View from a Store method in ExtJs without coupling

First of all I know how to set a LoadingMask for a component but have a problem with the uncoupling of the system I am making so I am just looking for a hint/idea.
I am using a MVVC architecture and in the View I have a Container with several Components in it, one of which is a Grid.Panel. The grid is bound to a store and has an event that when fired calls a method of the store. The following code happens in the ViewController:
functionForEvent() {
var store = getStoreForThisGrid();
store.update(Ext.getBody());
}
What happens now is the update() method makes a request to a server, that updates the store itself and the view component, and I need the loading mask during that time. How I handle the situation right now is I pass Ext.getBody() (or a DOM Element representation of a specific component) to the method and it deals with that reference. This function part of the store that is attached to the Grid and resides in the Store:
update : function (el) {
el.mask();
makeRequest();
el.unmask();
}
What I am looking for is another way (Pattern maybe if such exists for JavaScript) to access the View component from the Store instead of passing it around because that does not seem like a good practice and couples the system.
Since I come from a Java background I would have used the Observer pattern but cannot find how to apply this in JS.

Use of Meteor-ui-progress-circle (accessing to Template variables created in the HTML)

It may be a very dumb question... I am using Meteor-ui-progress-circle and I want redrawing the template when the percentage (wich is store in a reactive collection Progress) is changed (currently, when I click on a "play" button).
I think I have to use Blaze.render but I don't really understand how it work.
Here a part of my main template (in Jade) :
div.panel-body
div.col-md-9.col-sm-8
p Lorem ipsum...
div.col-md-3.col-sm-4#progress-circle
+progressCircle progress="0" radius="100" class="green"
And my JavaScript :
Template.controlBar.events(
{
"click .play-button": function ()
{
var tmp = Progress.findOne({});
if (!tmp)
{
Meteor.call('createProgress');
tmp = Progress.findOne({});
}
var val = tmp.progressValue;
val += 10;
if (val > 100)
return;
Meteor.call('updateProgess', tmp._id, val);
Template.progressCircle.progress = tmp.progressValue;
Blaze.render(Template.progressCircle, $("#progress-circle")[0]);
},
Doing this... I have several template that are displaying each time I click on the play button. I don't understand how to specify that I don't want a new template but just re-render the one I already have.
Not sure I quite understand your question, but I'll try to help by giving my best understanding of templating and how I have come to use them. If someone sees any incorrect information here, please speak up so I can get a better understanding myself and correct this answer.
First, the Template.XXX.events handlers. In your event handler, you are using a function with no arguments. You can actually accept 2 arguments for these event handler functions: the event and the template. So, you can do something like thus:
Template.controlBar.events({
'click .play_button': function(event, tmpl) {
tmpl.$('div#progress-circle').doSomething();
}
});
Notice the tmpl.$() call? That says to use jQuery to find the specified selector, but ONLY in the current template. This is a wonderful way to use classes to generalize your components, but then be able to filter the selection to only those within the same template...
...Which brings me to my next bit of advice: Use child templates excessively. Any component that I can identify as an "autonomous component" on my page I will consider as a separate template. For instance, I was recently working on a custom reporting page that had a table and some D3 graphs representing some real-time data. In this report page, I had one main template defined for the "page", then each of the D3 graphs where defined as a separate template, and the table was another separate template. This allows several advantages:
Compartmentalization of the "components" of the page, allowing code reuse (I can now put the same graph on ANY page, since it's now an autonomous "component"
The advantage of using the Template.XXX.events trick above to "narrow" the scope of my element searches to elements within that template
Prevents total page refreshes as Meteor is smart enough to only refresh templates that need to be refreshed, which also speeds the responsiveness of the page itself
As a result, I try to apply my Templates liberally. In your case, it would sound to me that if I were to have multiply progress bars on the page that I might turn those into separate templates. I might even do it if I had a single progress bar if it made sense to separate it out for ease of data handling.
Finally, inter-communications between Templates. This can be tricky at times, but the best, most efficient way to do this I have found is through Session variables. The pattern I typically use is to have my data for my template be returned by a Template .helper, which does something like this:
Template.controlBar.helpers({
progressData: function() {
if (!Session.equals('playId', null)) {
return Progress.findOne({_play_id: Session.get('playId')});
}
}
});
Because Helpers are reactive, and Sessions is reactive, the template is re-rendered anytime the 'playId' is altered in the Session. The corresponding Session variable can be set from anywhere in the client code. Again, this tends to work best when you narrow the scope of your templates to the individual components. It is important to note here that the Session object in Meteor is NOT the same as "sessions" in other languages like Java and such, which typically use cookies and a session token/id. Meteor sessions work considerably different, and do not survive page reloads or closing of browsers.

Meteor: How to Perform Calculations Client-Side on Mongo Data

In my Meteor app, I've successfully published data server-side and subscribed to that data client-side. Now, rather than pushing raw data straight to the client's screen, I want to store it as a javascript object, perform some calculations on it (number crunching), and render the result on the client's screen (within an HTML5 canvas element). Every time Mongo is updated, the javascript code should re-run (i.e. the js object is re-set, calculations are performed again from that object, and the new results are rendered on the canvas).
I can grab hold of Mongo data using the Template.example.helpers block and show that directly in the client as follows:
Meteor.subscribe('collection','query');
Template.example.helpers({
sampleData: function(){
return Collection.findOne({query:`query`});
}
});
<template name="example">
<div>
{{sampleData.last}}
</div>
<canvas id="test-canvas"></canvas>
</template>
But what I'm trying to do is grab hold of this data before pushing to the client's screen, within the Template.example.rendered block:
Meteor.subscribe('collection','query');
Template.example.rendered = function(){
// define HTML5 canvas and context variables
var canvas = $("#test-canvas")[0];
var context = canvas.getContext("2d");
// store Mongo data as Javascript variable
// loop over this variable and perform calculations
// draw results to the canvas
}
Am I approaching this the right way? If so, how can I achieve it? Thanks!
It would be probably easier to store the data and perform the calculations in a seperate object and render the template from the object and not straight from the mongo, you can easily keep your templates reactive using "Deps" and it will make you code much more maintainable.
I was able to figure out my own answer to the question posed above. My template was loading before the data was retrieved by the client, therefore I kept getting cannot read property <blank> of undefined in the browser's javascript console and code would interrupt. You need to use the iron-router package in order to
1) set the "data context" of the template you are working on (package up an object containing data sources you need for that specific template), and
2) force the template not to render until the data has been retrieved. Once the data is retrieved, iron-router loads the template and you now have full javascript control of the data object you created above.
Steps at a high-level:
1) Install iron-router
2) Define a route for the template
3) Use the waitOn method to tell iron-router which data source you are subscribing to (which data it is waiting on)
4) Define a "loading" template (aka splash screen) to show while data is being retrieved before template load. As per https://github.com/EventedMind/iron-router/issues/554, you can achieve this by inserting this block of code in your iron-router router.js file:
Router.onBeforeAction(function(pause) {
if (!this.ready()) {
this.render('loading'); // wait
pause(); // ready
}
});
Just make sure you create a loading template to go along with this.
5) Use the data method to set the data context for your template (create the data object to be used in the template)
Steps at a detailed-level:
http://www.manuel-schoebel.com/blog/iron-router-tutorial
Your approach is good.
The key here is to observe collection and update canvas when items in collections are:
added(document) or addedAt(document, atIndex, before)
removed(oldDocument) or removedAt(oldDocument, atIndex)
changed(newDocument, oldDocument) or changedAt(newDocument, oldDocument, atIndex)
movedTo(document, fromIndex, toIndex, before)
There is also more performant way to observe changes in collection.
Code similar to below should help you:
if (Meteor.isClient) {
Template.example.rendered = function () {
var canvas = $("#test-canvas")[0];
var context = canvas.getContext("2d");
Collection.find().observe({
added: function (doc) { // draw sth on canvas },
changed: function (doc) { // draw sth on canvas },
movedTo: function (doc) { // draw sth on canvas },
removed: function (doc) { // remove sth from canvas }
});
};
}
Example: https://github.com/Slava/d3-meteor-basic

Categories

Resources