Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Best way to embed steps and calories into bar graph

ANSWERED

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>
Best Answer
0 Votes
1 BEST ANSWER

Accepted Solutions

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.

View best answer in original post

Best Answer
14 REPLIES 14

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.

Best Answer

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);

 

Best Answer
0 Votes

Sorry, looks like I missed a bit:

 

const GRANULARITY = "minutes"; // or seconds
Best Answer
0 Votes

Great thanks Jon! It worked like a charm.

Best Answer
0 Votes

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);

  

Best Answer
0 Votes

Hi Jon,

 

I tried running the code posted above and am not seeing any graph/updating data on the screen, am I missing something?

Best Answer
0 Votes

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.

 

Best Answer
0 Votes

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;
}
Best Answer
0 Votes

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.

Best Answer
0 Votes

 

Move 

let activityData = {...}

inside function tickHandler(evt) {...}

 

function tickHandler(evt) {
  let activityData = {
    activeMinutes: getActiveMinutes(),
    calories: getCalories(),
    distance: getDistance(),
    elevationGain: getElevationGain(),
    steps: getSteps()
  }

  activityCallback(activityData);
}

Best Answer
0 Votes

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. 

Best Answer
0 Votes
Juste move bar graph (Calculator x and/or y positions)

Envoyé de mon iPhone
Best Answer
0 Votes

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" />
Best Answer
0 Votes

// 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" />

Best Answer
0 Votes