Barbell racking calculator with Vue.JS
A little over a year ago I started lifting. I knew calculating what plates to put on the rack should be easy, but after a few hours of exhaustion the head gets a little foggy. Being a programmer, building my own racking calculator was the obvious next step.
With this calculator I wanted the UI to be reactive. Clicking some archaic button like "calculate" would not work for me. Vue.js seemed like an obvious choice.
Project Setup
To get started we will use vue-cli to bootstrap our project. This little tool installs a directory structure for you which includes webpack
, vue
and a starter template to get up and running quickly.
Install vue-cli
If you don't have it already, install the package.
npm install -g vue-cli
Create Project
To create your project cd into your desired directory and run:
npm init webpack barbell-calc
This will through you into a few questions. Below is what we are using for this project:
- Project name
barbell-calc
- Project description
Barbell racking calculator
- Author
You
- Vue build
standalone
- Install vue-router?
No
- Use ESLint to lint your code?
No
- Set up unit tests
No
- Setup e2e tests with Nightwatch?
No
- Should we run
npm install
for you after the project has been created? (recommended)Yes
Getting started
cd barbell-calc
npm run dev
Open your browser to http://localhost:8080
App.vue
The cli comes preloaded with an App.vue
and a demo component called HelloWord. For the sake of this app, we will not need the HellWorld component. Go ahead and remove components/HelloWorld.vue
and remove the reference in App.vue
Your App.vue
should look something like the following
<template>
<div id="app">
<h1>Barbell Racking Calculator</h1>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
#app {
font-family: ua-med,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
To calculate the weights, lets can an input on the page to receive the base value for our calculation. Add this below the h1
tag in App.vue
.
<input type="number" v-model="weight" step="5" min="45" /> lbs
We are using a number input which allows us to step the values in increments of 5. Next we will need to add a weight
variable to our data section to store the desired weight. Your javascript in App.vue
should look something like below:
export default {
name: 'App',
data() {
return {
weight: 45
}
}
}
Calculating plates
Before we can calculate what plates we need to put on the bar we must first know 1) the bar weight and 2) what plates we have available. Along with the bar weight and available plates we need a place to store the calculated plate values. Lets make these variables in the data section:
data() {
return {
weight: 45,
barWeight: 45,
platesAvailable: [45, 35, 25, 10, 5, 5, 2.5, 1.25], // plates available for us
plates: [] // holds the plates need to meet the weight
}
}
Time for the meat. Create a new method called calculate()
inside the default object with the following code:
methods: {
calculate() {
this.plates = [];
var left = this.weight - this.barWeight;
while( left > 0 ) {
// track if we found a plate, otherwise we will spin forever
var foundOne = false;
for( var i = 0; i < this.platesAvailable.length; i++ ) {
var amount = this.platesAvailable[i]*2;
if( amount <= left ) {
left -= amount;
this.plates.push( this.platesAvailable[i] );
foundOne = true;
break;
}
}
if( !foundOne ) break;
}
}
}
There is a bit happening here but the logic is simple. The steps are as follows;
- Determine the total weight (we need to keep the bar weight in mind) we are working with
- Loop through our available plates to see if any are less than the total weight
- Subtract the plate weight from what is left
- Do this until we have no weight left
All the weights will get added to our handy plates
variable.
Output
All the "hard" work is now complete. Next we need to display the plates so we know what to rack up. Create the following html after your input:
<div class="rack">
<div class="bar">{{barWeight}}</div>
<div v-for="plate in plates">
<div class="plate">{{plate}}</div>
</div>
</div>
This will first display the bar weight before looping through the plates.
Listing everything is fine but lets spruce it up just a bit. It would be nice to see the plates resemble the size they actually are. We can do this by adjusting the width of the plate div:
:style="{width: 30+(plate*4)+'px'}"
Basing the width entirely off the weight would make it to small, so we start with a base value of 30px and multiple the plate weight by 4. I'm sure there are many formulas to use here but this suits our needs perfectly.
The CSS
Previewing will yield nothing different. We need some css! In your style tag add the following:
.rack {
margin: 20px auto;
}
.bar {
width: 24px;
margin: 4px auto;
background: #e4e4e4;
height: 50px;
line-height: 50px;
border: 2px solid #c5c5c5;
border-radius: 5px;
}
.plate {
margin: 4px auto;
color: #fff;
min-width: 30px;
border: 2px solid #283544;
border-radius: 5px;
background: radial-gradient(circle, #666666,#444444);
}
input {
font-size: 18pt;
text-align: center;
padding: 5px;
border-radius: 4px;
border: 1px solid #cad3dc;
max-width: 80px;
}
This gives our bar and plates a bit of style. There is also something extra for our input to make it a bit larger.
Rack up!
That is it! You now have a functioning barbell plate calculator that will help you get swol.
Extending
Some ideas to extend this app would be the ability to customize what plates are available for racking and switching the weight units from pounds to kg.