03-27-2018 17:19 - edited 03-27-2018 17:20
03-27-2018 17:19 - edited 03-27-2018 17:20
Hi all,
Hope all is well. What's the most efficient way to write javascript that embeds the step count and calorie count into the rect elements below? I am trying to create a vertical bar graph for each.
Also, is it best practice to include all the javascript in the index.js file or have a .js file for each metric? Thanks in advance!
<svg class="background"> <rect id="mySteps" x="312" y="0" width="18" height="100%" fill="white" /> <rect id="myCalories" x="330" y="0" width="18" height="100%" fill="#1abc9c" /> </svg>
Answered! Go to the Best Answer.
03-28-2018 07:25 - edited 03-28-2018 07:27
03-28-2018 07:25 - edited 03-28-2018 07:27
I've been working on a simple abstraction layer for some of these things.
Create `simple-activity.js` in your /app folder, with these contents:
/* simple-activity.js A simple way of returning activity data in the correct format based on user preferences. Callback should be used to update your UI. */ import clock from "clock"; import { today, goals } from "user-activity"; import { units } from "user-settings"; let activityCallback; export function initialize(granularity, callback) { clock.granularity = granularity; clock.addEventListener("tick", tickHandler); activityCallback = callback; } let activityData = { activeMinutes: getActiveMinutes(), calories: getCalories(), distance: getDistance(), elevationGain: getElevationGain(), steps: getSteps() } function tickHandler(evt) { activityCallback(activityData); } function getActiveMinutes() { let val = (today.adjusted.activeMinutes || 0); return { raw: val, pretty: (val < 60 ? "" : Math.floor(val/60) + "h,") + ("0" + (val%60)).slice("-2") + "m" } } function getCalories() { let val = (today.adjusted.calories || 0); return { raw: val, pretty: val > 999 ? Math.floor(val/1000) + "," + ("00"+(val%1000)).slice(-3) : val } } function getDistance() { let val = (today.adjusted.distance || 0) / 1000; let u = "km"; if(units.distance === "us") { val *= 0.621371; u = "mi"; } return { raw: val, pretty: `${val.toFixed(2)}${u}` } } function getElevationGain() { let val = today.adjusted.elevationGain || 0; return { raw: val, pretty: `+${val}` } } function getSteps() { let val = (today.adjusted.steps || 0); return { raw: val, pretty: val > 999 ? Math.floor(val/1000) + "," + ("00"+(val%1000)).slice(-3) : val } }
Then add this in your `index.js`.
import document from "document";
import * as simpleActivity from "./simple-activity";
let mySteps = document.getElementById("mySteps");
let myCalories = document.getElementById("myCalories");
function activityCallback(data) {
// work out the percentage width here
mySteps.width = 0 // data.steps;
myCalories.width = 0; //data.calories; }
simpleActivity.initialize(GRANULARITY, activityCallback);
I hope that helps.
03-28-2018 07:25 - edited 03-28-2018 07:27
03-28-2018 07:25 - edited 03-28-2018 07:27
I've been working on a simple abstraction layer for some of these things.
Create `simple-activity.js` in your /app folder, with these contents:
/* simple-activity.js A simple way of returning activity data in the correct format based on user preferences. Callback should be used to update your UI. */ import clock from "clock"; import { today, goals } from "user-activity"; import { units } from "user-settings"; let activityCallback; export function initialize(granularity, callback) { clock.granularity = granularity; clock.addEventListener("tick", tickHandler); activityCallback = callback; } let activityData = { activeMinutes: getActiveMinutes(), calories: getCalories(), distance: getDistance(), elevationGain: getElevationGain(), steps: getSteps() } function tickHandler(evt) { activityCallback(activityData); } function getActiveMinutes() { let val = (today.adjusted.activeMinutes || 0); return { raw: val, pretty: (val < 60 ? "" : Math.floor(val/60) + "h,") + ("0" + (val%60)).slice("-2") + "m" } } function getCalories() { let val = (today.adjusted.calories || 0); return { raw: val, pretty: val > 999 ? Math.floor(val/1000) + "," + ("00"+(val%1000)).slice(-3) : val } } function getDistance() { let val = (today.adjusted.distance || 0) / 1000; let u = "km"; if(units.distance === "us") { val *= 0.621371; u = "mi"; } return { raw: val, pretty: `${val.toFixed(2)}${u}` } } function getElevationGain() { let val = today.adjusted.elevationGain || 0; return { raw: val, pretty: `+${val}` } } function getSteps() { let val = (today.adjusted.steps || 0); return { raw: val, pretty: val > 999 ? Math.floor(val/1000) + "," + ("00"+(val%1000)).slice(-3) : val } }
Then add this in your `index.js`.
import document from "document";
import * as simpleActivity from "./simple-activity";
let mySteps = document.getElementById("mySteps");
let myCalories = document.getElementById("myCalories");
function activityCallback(data) {
// work out the percentage width here
mySteps.width = 0 // data.steps;
myCalories.width = 0; //data.calories; }
simpleActivity.initialize(GRANULARITY, activityCallback);
I hope that helps.
03-29-2018 06:58
03-29-2018 06:58
Thanks Jon! Looks really robust. Just a quick question - in the index.js portion, do I simply just pass parameters into this function? If yes, what does that look like exactly for step count? Sorry for my lack of understanding, I'm still pretty new to JS. Thanks again!
simpleActivity.initialize(GRANULARITY, activityCallback);
03-29-2018 12:04
03-29-2018 12:04
Sorry, looks like I missed a bit:
const GRANULARITY = "minutes"; // or seconds
03-31-2018 09:19
03-31-2018 09:19
Great thanks Jon! It worked like a charm.
04-03-2018 14:46
04-03-2018 14:46
Hi Jon,
Quick question-so the granularity is set to seconds. However, it appears that the callback is not retrieving new values for the "data" object that is created below. I can see the data object populating in the console every second but the step values are not updating every second. It appears that the data only refreshes when I switch to the Today app and then switch back to the clock face.
Is there any other adjustments that need to be made to the simple-activity.js? Thanks Jon!
function activityCallback(data) {
// work out the percentage height here
// barCalories.height = (data.calories.raw / data.goalCalories.raw)*348; // data.calories;
barSteps.height = (data.steps.raw / data.goalSteps.raw)*348; //data.steps;
barActiveMinutes.height = (data.activeMinutes.raw / data.goalActiveMinutes.raw)*348; //data.activeminutes;
// statCalories.text = data.calories.pretty;
statSteps.text = data.steps.pretty;
statActiveMinutes.text = data.activeMinutes.pretty;
// Begin monitoring the sensor
hrm.start();
console.log("Calories: " + JSON.stringify(data.calories.pretty));
console.log("Steps: " + JSON.stringify(data.steps.pretty));
console.log("Active Minutes: " + JSON.stringify(data.activeMinutes.pretty));
console.log("HR: " + MYHR.text);
};
simpleActivity.initialize(GRANULARITY, activityCallback);
06-07-2018 21:28
06-07-2018 21:28
Hi Jon,
I tried running the code posted above and am not seeing any graph/updating data on the screen, am I missing something?
06-08-2018 01:29 - edited 06-08-2018 01:34
06-08-2018 01:29 - edited 06-08-2018 01:34
It looks like the 'activityData' is only initialised once (upon start), so the actual values contained are a 'snapshot' and not continually updated.
To get the values to update, activityData would need to be updated every clock tick.
I can't try this now (am at work), but in the tickHandler function, I think you'd need something like the following before the callback was called:
activityData.activeMinutes = getActiveMinutes(); activityData.calories = getCalories(); activityData.distance = getDistance(); activityData.elevationGain = getElevationGain(); activityData.steps = getSteps();
You could also wrap that code in another function and call that in the tickHandler to make it a bit more tidy.
06-10-2018 18:59
06-10-2018 18:59
Hi Mark,
Thanks for the reply. I tried this and was still not successful.
Does it have something to do with the comment in the previous code. I never set the width anywhere except in the index.gui.
function activityCallback(data) { // work out the percentage width here mySteps.width = 0 // data.steps; myCalories.width = 0; //data.calories; }
06-14-2018 22:00
06-14-2018 22:00
Hi Ramirez,
I'm wondering if you ever got this working, I have not been able to get Jon's code working? I believe it's an issue with setting the width.
06-26-2018 06:01
06-26-2018 06:01
Move
let activityData = {...}
inside function tickHandler(evt) {...}
function tickHandler(evt) {
let activityData = {
activeMinutes: getActiveMinutes(),
calories: getCalories(),
distance: getDistance(),
elevationGain: getElevationGain(),
steps: getSteps()
}
activityCallback(activityData);
}
07-01-2018 13:34
07-01-2018 13:34
Is there a simple way to switch the direction that the graph fills? I want it to fill to the left, but currently it is filling to the right.
07-01-2018 14:38
07-01-2018 14:38
07-01-2018 19:54
07-01-2018 19:54
What do you mean by X/Y positions. My index.js looks like this, which works but the bar chart fills from the 100 pixels down to 298 pixel. I'm hoping to switch this by filling the chart from the bottom up. I tried using negative numbers but that is not working.
myActiveMinutes.height = (data.activeMinutes.raw / 60)*196;
Here is my index.gui
<rect id= "myActiveMinutes" x="102" y="102" width="46" height="16" fill="teal" />
07-02-2018 05:23
07-02-2018 05:23
// In app/index.js
let rStep = document.getElementById("rStep");
rStep.height = ((goals.steps-today.local.steps)*96) / goals.steps;
rStep.height = rStep.height > 0 ? rStep.height :0;
rStep.y = 103 - rStep.height;
// In ressources/index.gui
<rect id="rStep" x="7" y="7" width="12" height="96" fill="black" />