I am trying to implement some sort of animation while a large graph gets rendered using Flot chart. I have tried numerous solutions all of which have failed. Does anyone know if this is at all possible so the user can see that the graph is being rendered.
I have attempted to use an overlay with a loading gif but the gif does not animate until the graph has stopped rendering.
The code is being asked a lot of but I want to make sure the user can see the progress.
Thanks in advance
Flot renders the whole plot in one pass on the UI thread. There's no way to show progress without modifying the library itself. A perfect solution would take a lot of work, but, depending on your data, a decent approximation might not require much effort.
For example, let's say your plot contains many series, and each one by itself renders pretty quickly, but together they take a long time. In that case, if you open the Flot source and look for the draw function, you'll see that there is a simple loop calling drawSeries for each series:
function draw() {
CODE BLOCK A
for (var i = 0; i < series.length; ++i) {
executeHooks(hooks.drawSeries, [ctx, series[i]]);
drawSeries(series[i]);
}
CODE BLOCK B
}
Replace that with (roughly; haven't tested this) the following:
function draw() {
CODE BLOCK A
var i = 0,
drawNextSeries = function() {
drawSeries(series[i]);
if (++i < series.length) {
setTimeout(drawNextSeries, 0);
} else {
CODE BLOCK B
}
};
setTimeout(drawNextSeries, 0);
}
The idea is to draw each series in a separate call via setTimeout. The zero-millisecond delay queues up the call to run after any pending work, like other JS code, animations, etc. So after each series draws, there's a chance for the UI to update before the next one draws.
Again, this only works if you have many series that each draw fairly quickly. If you have just one big series, you'll still end up doing something similar, but within drawSeries.
Related
I need some help understanding what the best practice is for creating a PIXI.extras.AnimatedSprite from spritesheet(s). I am currently loading 3 medium-sized spritesheets for 1 animation, created by TexturePacker, I collect all the frames and then play. However the first time playing the animation is very unsmooth, and almost jumps immediately to the end, from then on it plays really smooth. I have read a bit and I can see 2 possible causes. 1) The lag might be caused by the time taken to upload the textures to the GPU. There is a PIXI plugin called prepare renderer.plugins.prepare.upload which should enable me to upload them before playing and possibly smoothen out the initial loop. 2) Having an AnimatedSprite build from more than one texture/image is not ideal and could be the cause.
Question 1: Should I use the PIXI Prepare plugin, will this help, and if so how do I actually use it. Documentation for it is incredibly limited.
Question 2: Is having frames across multiple textures a bad idea, could it be the cause & why?
A summarised example of what I am doing:
function loadSpriteSheet(callback){
let loader = new PIXI.loaders.Loader()
loader.add('http://mysite.fake/sprite1.json')
loader.add('http://mysite.fake/sprite2.json')
loader.add('http://mysite.fake/sprite3.json')
loader.once('complete', callback)
loader.load()
}
loadSpriteSheet(function(resource){
// helper function to get all the frames from multiple textures
let frameArray = getFrameFromResource(resource)
let animSprite = new PIXI.extras.AnimatedSprite(frameArray)
stage.addChild(animSprite)
animSprite.play()
})
Question 1
So I have found a solution, possibly not the solution but it works well for me. The prepare plugin was the right solution but never worked. WebGL needs the entire texture(s) uploaded not the frames. The way textures are uploaded to the GPU is via renderer.bindTexture(texture). When the PIXI loader receives a sprite atlas url e.g. my_sprites.json it automatically downloads the image file and names it as mysprites.json_image in the loaders resources. So you need to grab that, make a texture and upload it to the GPU. So here is the updated code:
let loader = new PIXI.loaders.Loader()
loader.add('http://mysite.fake/sprite1.json')
loader.add('http://mysite.fake/sprite2.json')
loader.add('http://mysite.fake/sprite3.json')
loader.once('complete', callback)
loader.load()
function uploadToGPU(resourceName){
resourceName = resourceName + '_image'
let texture = new PIXI.Texture.fromImage(resourceName)
this.renderer.bindTexture(texture)
}
loadSpriteSheet(function(resource){
uploadToGPU('http://mysite.fake/sprite1.json')
uploadToGPU('http://mysite.fake/sprite2.json')
uploadToGPU('http://mysite.fake/sprite3.json')
// helper function to get all the frames from multiple textures
let frameArray = getFrameFromResource(resource)
let animSprite = new PIXI.extras.AnimatedSprite(frameArray)
this.stage.addChild(animSprite)
animSprite.play()
})
Question 2
I never really discovered and answer but the solution to Question 1 has made my animations perfectly smooth so in my case, I see no performance issues.
I'm working on a problem where I want to display data in a dashboard both as a chart (via perak:c3) and in a table (via aslagle:reactive-table). My issue is that the data is pulled from a collection in MongoDB, and it's format is instantly amenable to plotting via c3, but needs to be transformed into a local collection to be used by the reactive-table package, as suggested in this answer to a previous question.
When I change the dataset to be displayed I want the chart to be updated, and the table also. This requires changing the values in the local collection, however, which slows things down and so rather than the chart being smoothly redrawn, there is a freeze on the page, and then the new data is displayed.
I have created a sample project on GitHub here so the problem can be replicated easily. If you run the app and select a dataset in your browser you will see exactly what I mean
To see the reactive behaviour I want to preserve in the chart go to client/templates/dashboard/dashboard.html and simply comment out the table template {{> dashboardTable}}
and now change the dataset to see how the chart is smoothly redrawn. Essentially I am trying to ensure both templates dashboardChart and dashboardTable render independently of one another.
UPDATE
Following Michael Floyd's suggestion of using a timeout helped a bit
Meteor.setTimeout(function(){createLocalCollection(data)},200);
but although the chart gets smoothly drawn, when the table finishes being filled, the chart is drawn again. It looks like it jumps to some intermediate state that I can't understand. Here is a video showing what I mean.
I'm adding this as a separate answer because it's a completely different approach.
Use the onRendered callback in d3 to invoke the local collection update.
Where you have:
chart = c3.generate({
bindto: '#dataset-chart',
in dashboard_chart.js, add:
chart = c3.generate({
onrendered: createLocalCollection(),
bindto: '#dataset-chart',
Of course you need to remove createLocalCollection(data) from your event handler.
To avoid having to pass the data context through the onrendered handler in d3 also update your createLocalCollection function to use the reactive variable datasetID that you defined earlier to establish the current dataset:
var createLocalCollection = function() {
var values = My_First_Collection.find({datasetID: datasetID.get()}).fetch();
var tempDoc = {};
local.remove({});
tempDoc = {};
for (var i in values[0].value) {
tempDoc.value = values[0].value[i];
tempDoc.date = values[0].date[i];
local.insert(tempDoc);
}
};
Using this method you let D3 tell you when the chart rendering is done and then your table can start getting populated. The result is an instantaneous chart update followed by the table updating. No mucking with timeouts either.
Remember that js is single threaded. You have two things to and they are going to happen sequentially. What you can do is defer the code that is updating the local collection using Meteor.setTimeout(). This will allow the chart to update first and then your table can update second. I've seen this before where you run a function that updates the DOM (in your case d3 is updating the svg canvas) but the actual screen update gets stuck behind long running js.
I tried this specifically and chart performance was fine.
Meteor.setTimeout(function(){createLocalCollection(data)},500);
Cutting the interval down to 100 allowed the chart to update but then the menu didn't fade out completely until the local collection finished updating.
One thing that I've used with tables and local collections is to only update the local collection document when the corresponding non-local document is being rendered on screen (assuming there's a 1:1 relationship between the original data and the transformed version). This allows the reactive table to load lazily.
I was toying with socket.io, ThreeJS, Javascript, and NodeJS to create a simple client/server using ThreeJS's graphics. I wasn't sure if all of these frameworks would even work together, but I decided to give it a shot since I've seen similar examples online before even though I cannot find a simple one to dissect or experiment with. It's mainly to experiment with, but I also wanted to make a small little concept-game as proof of what I've learned so far.
I posted my code here: https://gist.github.com/netsider/63c414d83bd806b4e7eb
Sorry if it's a little untidy, but I did my best to make it as readable as possible.
Basically, right now the server-side NodeJS script seems to run fine (Run with "node server-alpha.js"), and the client script (client-alpha.html, which you can just open in a browser) connects to the server, and displays a list of users (who are also connected). However, my intention was for each user to be able to move his/her own cube around, and right now each cube only gets added to the screen (rather than being added, subtracted, and then added again - to give the illusion of movement). If you run both pieces of code and connected one or two users and move the arrow keys a few times for each, you'll see what I'm talking about.
Can anybody help me with this? I tried several different ways to remove the cube (and remembered to call render(), after each)... but everything I tried didn't seem to work. It always resulted in the cubes just being added to the screen, and never subtracted.
I added comments in the code to make things a little easier, as I know this is quite a bit of code to go through (if it's not your own, anyway).
Thanks, any help would be greatly appreciated... as I'm really stuck trying to make the cubes just move.
Also, I'm having trouble adding the Fly-Controls (FlyControls.js - it's commented out ATM), so if someone could tell me where I went wrong I'd appreciate that a lot also.
Ok so you don't want to keep remaking the cubes, all you need to do is change the position.
Also in game development, it is almost a requirement to use object oriented design, a good way to go about this would be to make a player object, so..
CPlayerList = new Array(); // an array of player objects
function ClientPlayer()
{
this.Cube;
this.Name = "unnamed";
this.Id = 0;
this.Create = function(name,pos,id)
{
this.Name = name;
this.Id = id;
var cubeGeometry = new THREE.BoxGeometry(10, 10, 10);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 'red', transparent:false, opacity:1.0});
this.Cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
this.Cube.position.x = pos.x;
this.Cube.position.y = pos.y;
this.Cube.position.z = 20; // don't know why this is 20, remember y axis is up & down in opengl/threejs
scene.add(this.Cube);
}
this.Move = function(vector)
{
this.Cube.position.set(this.Cube.position.x + vector.x, this.Cube.position.y + vector.y, 20);
}
}
So on the server you need a ServerPlayer object which holds similar data, and assign ids on the server before sending them to the clients. So when you send it to the client you want to make a new ClientPlayer, call player.Create() and then push it to the CPlayerList, like so:
function newCPlayer(data)
{
var newPly = new ClientPlayer();
newPly.Create(data.name,data.pos,data.id);
CPlayerList.push(newPly);
}
Then when you call your movePlayer() function, you can simply loop through your players array
function movePlayer(keyStroke, clientID)
{
if (keyStroke == 39)
{
CPlayerList.forEach(function(player,i,a)
{
if(player.Id === clientID)
{
player.Move(new THREE.Vector3(1,0,0));
}
}
}
}
This is just the client code, but this should help you get started, let me know if there's anything you're unclear on.
Also here's an example of a game using a similar design: http://82.199.155.77:3000/ (ctrl+shift+j in chrome to view client sources) and server code: http://pastebin.com/PRPaimG9
I'm working on an implementation of a live-updating line graph using gRaphael which is a graphic extension of the Raphael SVG library.
I can't seem to find any examples of somebody doing this as a near-realtime updating project, which is fine. I'm assuming there's a way to call refresh on the graph with a new data set (without the need to re-initialize a whole new Raphael object each time!), but therein lies the problem:
There doesn't seem to be accurate documentation anywhere. I discovered this StackOverflow question: Graphael line chart which in turn led to this documentation project: https://github.com/kennyshen/g.raphael/tree/master/docs , but the results were cold. Using the examples provided, I ran into some errors:
the syntax r.g.linechart() used in the examples was no longer valid (where r is the Raphael object and I assume g is a gRaphael property within). Somewhere along the way somebody must have switched to properly extending the Raphael object so that r.linechart() worked.
The parameters passed into linechart() were incorrect, resulting in an undefined error again. If I passed in only the #x, #y, width, height, arrayX, arrayY parameters and dropped the chart labels, etc., I could render a plain line. But of course I need to be able to label my axes and provide a legend, etc.
Needless to say, a library without an API document isn't going to do anybody much good, but there are stalwarts out there who are willing to learn based strictly on reading the code itself. I'm not one of those. I would probably do OK with a well-commented example, preferably using live updates.
So I guess the questions are:
Does anybody know better documentation than the one I linked to?
Can someone point me to examples, documentation failing?
Can someone provide a proper itemization of the parameters that linechart() will accept?
Thanks!
For the record, here's how far I am so far:
var r = Raphael('line-chart');
// did NOT work -->
var linechart = r.g.linechart(
10,10,300,220,[1,2,3,4,5],[10,20,15,35,30],
{"colors":["#444"], "symbol":"s", axis:"0 0 1 1"}
);
// worked in a limited way, rendering a plain line with no visible labels/graph -->
var linechart = r.linechart(
10,10,300,220,[1,2,3,4,5],[10,20,15,35,30]
);
I am still trying to learn Raphael myself, but here are the primary resources I have been using: http://g.raphaeljs.com/reference.html and the same sans the "g."
here is a fiddle that pretty much pulls off an updating linechart with knockout/gRaphael, prob not the best solution, but its an idea: http://jsfiddle.net/kcar/mHG2q/
Just a note, I didn't start learning it until I combined reading with trial/error (with a lot of error), so play with the fiddle and see how things change.
but the basic code for it is like:
//constructor
var lines = r.linechart(10, 10, width, height, xVals, yVals, { nostroke: false, axis: "0 0 1 1", symbol: "circle", smooth: true })
.hoverColumn(function () { //this function sets the hover tag effect
this.tags = r.set();
for (var i = 0, ii = this.y.length; i < ii; i++) {
this.tags.push(r.tag(this.x, this.y[i], this.values[i], 160, 10).insertBefore(this).attr([{ fill: "#fff" }, { fill: this.symbols[i].attr("fill") }]));
}
}, function () {
this.tags && this.tags.remove();
});
lines.symbols.attr({ r: 3 }); //this adjusts size of the point symbols
There is a fork in GitHub that is working on the documentation and examples.
You will need to download the code and view it from you computer. It is a work in progress but it's more than you can find in the official g.Raphael page.
I also found this small post with some examples.
For my first foray into html5, I'm trying to make a scoreboard for the game Farkle.
I have figured out how to make a lame looking but functional graph system for up to five players, but I cannot figure out how to have it update without simply drawing over the old bar graphs.
I realize that my code could be much better, but I didn't want to write helper functions until I was sure how it would work.
I'm using buttons to trigger the "score"function for the appropriate player for now,
but I would like to be more automatic.
Here is a "working" version, so that you can see my problem.
http://jsfiddle.net/kaninepete/dnsuj/1/
For graphical stuff, I organize my code into a setup() function, and a draw() function that gets called repeatedly, something like this:
function setup(){
// set up variables you in draw, here
// examples include initial document width and height, and things
// like that (although those in particular would go in draw if you
// want to handle the resize event.
context = document.getElementById("canvas").getContext('2d');
}
function draw(){
// clear the canvas
context.clearRect(0, 0, docWidth, docHeight);
// now redraw your bar graph
// ...
}
setup();
setInterval("draw();", 60);
Do you see where I'm going with this?
the basic of the periodic update:
function update() {
score1();
//you can add here everything you can write here loops, you can write here functions;
move_to(100,100);
alert('hello');
for(i=0;i<100;i++) {
j=2; //this is not needed, just to demonstrate you can do everything here
}
//this is the important, that call himself periodically again;
window.setTimeout(100,update);
}
update();
this code calls an update function wich periodically calls himself. this is the best way to update something like canvas.