Trying to pass in an array to justGage - javascript

I'm using JustGage Javascript Plugin to create a dynamic gauge. I want to pass in an array to this gauge and have it display those values. So far, I'm only able to get it to display random integers using getRandomInt().
Here's the relevant js part of the code:
<script src="justgage-1.2.2/justgage.js"></script>
<script src="justgage-1.2.2/raphael-2.1.4.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var g1 = new JustGage({
id: 'g1',
value: getRandomInt(0, 100),
min: 0,
max: 100,
pointer: true,
gaugeWidthScale: 0.6,
customSectors: [{
color: '#ff0000',
lo: 50,
hi: 100
}, {
color: '#00ff00',
lo: 0,
hi: 50
}],
counter: true
});
setInterval(function() {
g1.refresh(getRandomInt(0, 100));
}, 2000)
});
</script>
I'm wondering why something like
var num= [1,2,3,4,5,6,7,8,9];
var i = 0;
setInterval(function() {
document.innerHTML = num[i++];
if (i == num.length) i = 0;
}, 1000);
Can't be used instead of the getRandomInt() function.
Since this can't be seen on jfiddle or codepen, here's the plugin's site that shows some demo charts: http://justgage.com/ Instead of having to hit a refresh button, it refreshes on it's own.
Thought it would be more productive to ask here while I still try to work things out too.

Man that plugin has lousy docs...
Seems like you're not far off with what you want to do - in your setInterval you're going to want to do something like this, with the key part being the g1.refresh(val) with the new value of your gauge.
var i = 0,
num = [1,2,3,4,5,6,7,8,9];
setInterval(function() {
if (i >= num.length) i = 0;
g1.refresh(num[i++]);
}, 2000);

Related

Slow rendering of image + slider with Plotly JS

I'm trying to plot an image/heatmap with a slider that will change the opacity (of the heatmap), and a second slider that will modify a custom parameter on each "onchange" event.
Once this image/heatmap is rendered, no computation should be done, and moving the sliders should be instantaneous. But from what I have tried, moving one slider is very slow (1 second lag between each position), and uses max CPU %.
I'm looking for a JS-only solution (no Python for this part).
How to make a faster slider rendering with Plotly JS?
var z = [], steps = [], i;
for (i = 0; i < 500; i++)
z.push(Array.from({length: 600}, () => Math.floor(Math.random() * 100)));
for (i = 0; i < 100; i++)
steps.push({ label: i, method: 'restyle', args: ['line.color', 'red']});
var data = [{z: z, colorscale: 'YlGnBu', type: 'heatmap'}];
var layout = {title: '', sliders: [{
pad: {t: 5},
len: 1,
x: 0,
currentvalue: {
xanchor: 'right',
prefix: 'i: ',
font: {
color: '#888',
size: 20
}
},
steps: steps
}]};
Plotly.newPlot('myDiv', data, layout);
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<div id="myDiv"></div>
This is because you are using method: 'restyle' with the wrong args, ie. ['line.color', 'red'] the syntax is not correct and there is no line so I guess Plotly (without knowing what to restyle exactly) just redraws the whole plot whenever the slider moves, which is slow.
Basically, you can use the same slider configuration in javascript and in python for the same task (in the end the same Plotly.js slider component will be used).
For example, one can set the opacity of an image according to the slider's position, but for the changes to be applied instantly one needs to set the proper method and args in the slider' steps configuration, excactly as explained in this post :
steps.push({
label: i,
execute: true,
method: 'restyle',
args: [{opacity: i/100}]
});
Here is a full example with two sliders : one that changes the opacity of the heatmap and another one that doesn't touch the plot but only triggers a specific handler :
const z = [];
for (let i=0; i<500; i++) {
z.push(Array.from({length: 600}, () => Math.floor(Math.random() * 100)));
}
const data = [{z: z, colorscale: 'YlGnBu', type: 'heatmap'}];
// Steps for the heatmap opacity slider
const opacity_steps = [];
for (let i = 0; i <= 100; i++) {
opacity_steps.push({
label: i + '%',
execute: true,
method: 'restyle',
args: [{opacity: i/100}]
});
}
// Steps for the custom slider
const custom_steps = [];
for (let i = 50; i <= 200; i++) {
custom_steps.push({
label: i,
execute: false,
method: 'skip',
});
}
const layout = {
title: '',
sliders: [{
name: 'opacity_slider',
steps: opacity_steps,
active: 100,
pad: {t: 30},
currentvalue: {prefix: 'opacity: '}
}, {
name: 'custom_slider',
steps: custom_steps,
pad: {t: 120},
currentvalue: {prefix: 'i: '}
}]
};
Plotly.newPlot('graph', data, layout);
// Retrieve the graph div
const gd = document.getElementById('graph');
// Attach 'plotly_sliderchange' event listener to it (note that we can't specify
// which slider the handler is taking care of using a secondary selector)
gd.on('plotly_sliderchange', function(event) {
// ... so we have to distinguish between the two sliders here.
if (event.slider.name != 'custom_slider')
return;
const slider = event.slider; // the slider emitting the event (object)
const step = event.step; // the currently active step (object)
const prev = event.previousActive; // index of the previously active step
const value = step.value; // captain obvious was here
const index = step._index; // index of the current step
// ...
});
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<div id="graph"></div>

how can i decrease delay in this.time.addEvent in phaser.js, based on score?

Someone already asked that question, but they didn't seem to get the right answer.
This is my code:
const eggGenLoop = this.time.addEvent({
delay: 1500 - (gameState.score * 100),
callback: eggGen,
callbackScope: this,
loop: true,
});
so the delay doesn't change, clearly phaser just takes the initial gameState.score which is 0. two months ago i tried to make the same game, using plain JavaScript and setTimeout() / setInterval(), but i stumbled at the same obstacle, i hoped with phaser.js it would be easier.
As you mentioned, using this.time.addEvent and setting delay won't work, the reason is because the TimerEvent is created once, on
the call this.time.addEvent(...).
I see two simple options (personally I would choose option 2):
Option 1)
You could instead use setTimeout and call it recursively, and update the delay on each call.
function setTimoutWithVariableDelta( score ){
setTimeout(
_ => {
// ... do some stuff
setTimoutWithVariableDelta( gameState.score );
},
1500 - (score * 100));
}
Depending on your code, this option might not work well (and btw.: setTimeout, doesn't stop when the browser is in the background).
Option 2)
Or you could use the Scene update function.
I put the logic for this type of dynamic delay into the function callOnMuliplierInterval, it should be straight forward. Just using the time parameter of the update function to keep track of the delay. (here the link to the documentation)
Here is a possible example:
(Sorry it got abit long, I got carried away)
Just click the buttons to change the score multiplier.
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 203,
scene: { create, update },
banner: false
};
let interval = 2000;
let lastRun = -1;
let statusText;
let score = 1;
let currentDelta = 1;
let callsCount = 0;
function create () {
statusText = this.add.text(220, 20, `Score multiplier: ${score}`)
.setFontFamily('Arial')
.setFontStyle('bold')
.setFontSize('20px');
createButton(this, 40, 20, 0xff0000, '#ffffff', .33);
createButton(this, 40, 80, 0x0000ff, '#ffffff', .66);
createButton(this, 40, 140, 0x008800, '#ffffff', 1);
}
// demo helper function for creating buttons
function createButton(scene, x, y, color, textColor, multiplier){
let button = scene.add.rectangle(x, y, 140, 40, color)
.setOrigin(0);
let buttonText = scene.add.text(0, 0, `Score: ${multiplier}`)
.setFontFamily('Arial')
.setFontStyle('bold')
.setFontSize('20px');
Phaser.Display.Align.In.Center(buttonText, button);
button
.setInteractive()
.on('pointerdown', _ => score = multiplier);
}
// this function contains the important logic
function callOnMuliplierInterval(time){
let newMessage = `Score multiplier: ${score}\n`;
newMessage += `ElapsedTime: ${(time / 1000).toFixed(0)}s\n\n`;
newMessage += `CurrentDelay: ${currentDelta}\n`;
newMessage += `NextDelay: ${interval * score}\n\n`;
newMessage += `TimesCalled: ${callsCount}`;
// Execute on: first Run or when the delay has passed
if( lastRun == -1 || lastRun + currentDelta <= time ){
lastRun = time;
currentDelta = interval * score;
// HERE YOU WOULD DO THE ACTION
callsCount++;
}
// Just update the message
statusText.setText(newMessage);
}
function update(time, delta){
callOnMuliplierInterval(time);
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

Trouble displaying code on screen

This might sound like a simple thing for you programmers out there but i can't seem to figure it out. I'm making a program that converts Decimal Numbers into Roman Numerals. I've got the core code working but i can't seem to think how i can display the results on the screen. I would like it so that when the user types a number into a text box the result appears in another text box at the press of a button. Thanks for your time & help.
function convertToRoman(num) {
var romans = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1,
};
var result = '';
for (var key in romans) {
if (num >= romans[key]) {
result += key.repeat(Math.trunc(num / romans[key]));
num -= romans[key] * Math.trunc(num / romans[key]);
}
}
return result;
}
If you add a div element and give it an id like "output", you can do something like this.
document.getElementById("output").innerHTML = result;
You can use the trusty old alert function. Just do alert(convertToRoman(10)). Put this in a <script> tag or inside body onload attribute
For fancier more professional debugging, you can use console.log. Originating with an old Firefox addon called Firebug, it is now supported by all browsers. Simply type console.log(convertToRoman(10)); The result will be printed out to console, to see it, right click your browser, and open inspect document and move to Console tab, or alternatively, Ctrl+Shift+I in Chrome
You are missing the part where you attach the result to the html. See working code below as an example.
function convertToRoman(num) {
var romans = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1,
};
var result = '';
for (var key in romans) {
if (num >= romans[key]) {
result += key.repeat(Math.trunc(num / romans[key]));
num -= romans[key] * Math.trunc(num / romans[key]);
}
}
document.getElementById('output').innerHTML = result;
}
convertToRoman(12345);
<div> my result <span id="output"></span></div>

How to access the value of a custom attribute in data from highcharts

I have this in my code
series: [{
name: 'Velocidad',
turboThreshold: 5000,
data: (function() {
// generate an array of random data
var data = [],
i;
for (i = 0; i < valor; i++) {
data.push({
x: dataUtc[i],
y: dataVel[i],
id: i
});
}
return data;
})(),
tooltip: {
valueDecimals: 0,
valueSuffix: 'Kms/H'
}
}]
When I try to get the maximum value I only get the x or y value.
I need to get the id value though.
What can I do?
I tried the following:
var chart = $('#containerVelocidad').highcharts();
min = chart.xAxis[0].min;
max = chart.xAxis[0].max;
Unfortunately that didn't help.
First of all, use yAxis.dataMax not yAxis.max. The second one is returning actual extremes, when sometimes you may have different extremes, than just from one series (zooming, setting extremes, multiple series etc.).
Demo: http://jsfiddle.net/ez4hjazk/1/
And code:
var max = chart.yAxis[0].dataMax,
maxIndex = chart.series[0].yData.indexOf(max), //get index from yData, read more about .indexOf() !
data = chart.series[0].data;
// make sure point is within actual extremes
if(maxIndex > -1) {
console.log("max = " + max, 'id = ' + data[maxIndex].id);
}
Do you need to get the maximum/minimum of id?
If so, try chart.series[0].data[i].id. Here's an example: http://jsfiddle.net/ez4hjazk/

Long array of small data points is rendered as a jagged line

The Goal
I'm attempting to render a long series of data (around 200 ticks, from small float values like 1.3223) into a line chart.
The Issue
When I use a series of data that changes only a small amount (around 0.0001 every tick), the chart is rendered as very jagged (scissor like). I would like to somehow fix it to have a "saner" radius between each point on the graph.
A Good Example
On the other hand, when rendering higher values (around 1382.21) with bigger difference between ticks (from 0.01 to 0.05 +/-) the graph is rendered more smooth and aesthetically pleasing.
Edit: As user Arie Shaw pointed out, the actual low or high values don't make a difference and it remains an issue of representing small "monotonous" changes is a less jagged form.
The Code
var initChart = function(data, container) {
new Highcharts.Chart({
chart: {
type: "area",
renderTo: container,
zoomType: 'x'
},
title: {
text: ''
},
xAxis: {
labels: {
enabled: false
}
},
yAxis: {
title: {
text: ''
}
},
legend: {
enabled: false
},
color: '#A3D8FF',
plotOptions: {
area: {
fillColor: '#C6E5F4',
lineWidth: 1,
marker: {
enabled: false
},
shadow: false,
states: {
hover: {
lineWidth: 1
}
},
threshold: null
}
},
exporting: {
enabled: false
},
series: [{
name: "TEST",
data: data
}]
});
};
Both graphs, and sample data sets are presented in the following fiddle:
http://jsfiddle.net/YKbxy/2/
The problem you're experiencing is unavoidable: If you have a lot of small variations over time, the graph is going to appear jagged provided that you show each data point.
The key point is that last phrase.
One way to 'smooth out' the rough parts would be to average the data. For example:
var myData = []; //... Some array of data; assuming just numbers
var averageData = function (data, factor) {
var i, j, results = [], sum = 0, length = data.length, avgWindow;
if (!factor || factor <= 0) {
factor = 1;
}
// Create a sliding window of averages
for(i = 0; i < length; i+= factor) {
// Slice from i to factor
avgWindow = data.slice(i, i+factor);
for (j = 0; j < avgWindow.length; j++) {
sum += avgWindow[j];
}
results.push(sum / avgWindow.length)
sum = 0;
}
return results;
};
var var initChart = function(data, container) {
new Highcharts.Chart({
series: [{
name: "TEST",
data: averageData(myData, 2)
}]
});
});
This method also has the advantage that you could (potentially) reuse the function to compare the averaged data to the regular data, or even toggle between how much to average the data.
You can always use areaspline instead of area, see: http://jsfiddle.net/YKbxy/3/
why dont you treat you .00001 data as 1, so times 10000, and then write it in your legend like that.
You should even do that as a test, since if the chart looks fine then, it means there is a problem in the dataset numbers when you return it to normal, since high charts takes the difference between high and low...
Either you must approximate your data by only using a few decimal places or you must average out the values using something like:
var data = new Array(200);
var smallArray = new Array(5);
var averagedData = new Array(20);
for (var index=0; index<=averagedData.length; index++){
for(var i = 0; i<=smallArray.length; i++){
smallArray[i] = data[i + index * 5];
}
averagedData[index] = (smallArray[1] + smallArray[2] + smallArray[3] + smallArray[4] + smallArray[5])/smallArray.length;
}
Then you will only need to plot 20 averaged points on an array of 200 data points. You can change the values for what you need.
In the end the issue is in the frequency of the points or their plotting on yAxis.
When I provide more realistic positioning (e.g timestamp) it will look good.
Meaning that jaggedness is a result of the small changes over constant yAxis progression, which is most similar to nt3rp's answer

Categories

Resources