Skip to content

Instantly share code, notes, and snippets.

@LOZORD
Last active August 29, 2015 14:05
Show Gist options
  • Save LOZORD/c33f4420d5c18a696bdd to your computer and use it in GitHub Desktop.
Save LOZORD/c33f4420d5c18a696bdd to your computer and use it in GitHub Desktop.
Since HTML <progress> bars don't have (non-pseudo) CSS attributes to animate for their width and inner color, I had to break out some nasty JS. Expect an actual library using just vanilla JS some time in the future...
/*
I used this tutorial below to get some nice looking bars to start out with:
http://css-tricks.com/html5-progress-element
Some page-specific code has been redacted, only the meat remains
Don't blame me if this doesn't compile! This meant for education!
*/
const IS_WEBKIT = 'WebkitAppearance' in document.documentElement.style;
const RGB_STR_REGEX = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/;
/*
Rounding RGB changes to nearest 10 (faster & less writes)
PLEASE make sure that your code (i.e. CSS specifies colors in rgb(x,y,z) format,
where x, y, and z are all divisible by 10
*/
const RGB_CHANGE_VELOCITY = 10;
const ANIMATE_INTERVAL_PERIOD_TIME = 10; //10 ms
/*
these are not the true rgb values of these colors,
they have been rounded to the nearest 10 for speed
*/
const GOOD_COLOR =
{
name: 'palegreen',
r: 150,
g: 250,
b: 150
};
const WARN_COLOR = {
name: 'gold',
r: 260,
g: 220,
b: 0
};
const BAD_COLOR = {
name: 'tomato',
r: 220,
g: 100,
b: 70
};
/* Now set up the add-on CSS file */
//now append a special css style doc for the bar colors
var bar_style = document.createElement('style');
document.head.appendChild(bar_style);
var bar_color_sheet = bar_style.sheet;
//map of (selector -> rule #) in bar_color_sheet
bar_color_sheet.selectors = {};
var bar_selectors_count = 0;
//this is called from a Ajax promise obj
function showData (text, status, jQXHR)
{
var data = jQuery.parseJSON(text);
var bar_color = calcBarColor(rating.percent, RGB_CHANGE_VELOCITY);
var the_guid = data.guid;
adjustBar(the_guid, bar_color, rating.percent);
}
function calcBarColor (perc, round_num, end_is_good)
{
//default arguments
round_num = typeof round_num === 'undefined' ? 1 : round_num;
end_is_good = typeof end_is_good === 'undefined' ? true : end_is_good;
//an RGB obj
var the_color = {};
var start_color, mid_color, end_color;
mid_color = WARN_COLOR;
//[bad -> meh -> good]
if (end_is_good)
{
start_color = BAD_COLOR;
end_color = GOOD_COLOR;
}
//[good -> meh -> bad]
else
{
start_color = GOOD_COLOR;
end_color = BAD_COLOR;
}
//the first half
if (perc < 50)
{
//since perc is below 100
var temp = perc * 2;
the_color.r = ( start_color.r * (100 - temp)/100 ) + ( mid_color.r * (temp)/100 );
the_color.g = ( start_color.g * (100 - temp)/100 ) + ( mid_color.g * (temp)/100 );
the_color.b = ( start_color.b * (100 - temp)/100 ) + ( mid_color.b * (temp)/100 );
}
//the second half
else
{
//since perc is above 50
var temp = (perc - 50) * 2;
the_color.r = ( mid_color.r * (100 - temp)/100 ) + ( end_color.r * (temp)/100 );
the_color.g = ( mid_color.g * (100 - temp)/100 ) + ( end_color.g * (temp)/100 );
the_color.b = ( mid_color.b * (100 - temp)/100 ) + ( end_color.b * (temp)/100 );
}
for (var key in the_color)
{
//get an integer rounded to the nearest round_num
the_color[key] = Math.floor(round_num * Math.round(the_color[key]/round_num));
}
return the_color;
}
function adjustBar(some_guid, some_color, some_perc)
{
var device_progress_bar = $('progress#isp_bar_' + some_guid);
animatePercentage(device_progress_bar, some_perc);
changeBarColor(device_progress_bar, some_color);
}
//for changing the % or width of the inner bar
function animatePercentage(some_bar, some_perc)
{
var begin_perc = parseFloat(some_bar.attr('value'));
var dist = Math.abs(some_perc - begin_perc);
var dir = getDir(begin_perc, some_perc);
//for animation
function frame ()
{
if (parseInt(some_bar.attr('value'), 10) === parseInt(some_perc, 10))
{
clearInterval(id);
}
else
{
var curr_perc = parseFloat(some_bar.attr('value'));
some_bar.attr('value', (curr_perc + dir).toString());
}
}
var id = setInterval(frame, ANIMATE_INTERVAL_PERIOD_TIME);
}
//I basically wanted to implement Ruby's <=> (spaceship/UFO/compare-to) operator
function getDir (old_val, new_val)
{
if (old_val < new_val)
{
return 1;
}
else if (old_val > new_val)
{
return -1;
}
else
{
return 0;
}
}
function getRGBDirObj (old_rgb, new_rgb, vel)
{
//default arg, used to speed up change
vel = typeof vel === 'undefined' ? 1 : vel;
return {
r: getDir(old_rgb.r, new_rgb.r) * vel,
g: getDir(old_rgb.g, new_rgb.g) * vel,
b: getDir(old_rgb.b, new_rgb.b) * vel
};
}
function changeBarColor(some_bar, some_rgb_obj)
{
var the_guid = some_bar.attr('id');
var begin_rgb_obj = rgbStrToRgbObj(getBarColor(the_guid));
var dir_obj = getRGBDirObj(begin_rgb_obj, some_rgb_obj, RGB_CHANGE_VELOCITY);
//for animation
function frame ()
{
var curr_color = rgbStrToRgbObj(getBarColor(the_guid));
if (rgbEquality(curr_color, some_rgb_obj))
{
clearInterval(id);
}
else
{
var update_rgb_obj = {
r: curr_color.r + dir_obj.r,
g: curr_color.g + dir_obj.g,
b: curr_color.b + dir_obj.b
};
var update_rgb_str = rgbObjToRgbStr(update_rgb_obj);
setBarColorRule(the_guid, update_rgb_str);
dir_obj = getRGBDirObj(update_rgb_obj, some_rgb_obj, RGB_CHANGE_VELOCITY);
}
}
var id = setInterval(frame, ANIMATE_INTERVAL_PERIOD_TIME);
}
//changes CSS pseudo-element value bgcolor attr of bar w/this guid
function setBarColorRule(some_guid, some_color)
{
var style_num = -1;
//if the rule already exists
if (bar_color_sheet.selectors.hasOwnProperty(some_guid))
{
style_num = bar_color_sheet.selectors[some_guid];
//then destroy the matched rule
bar_color_sheet.removeRule ?
bar_color_sheet.removeRule(style_num) :
bar_color_sheet.deleteRule(style_num) ;
}
else
{
style_num = bar_selectors_count++;
bar_color_sheet.selectors[some_guid] = style_num;
}
var selector_str = '#' + some_guid;
//we can only style webkit and mozilla
selector_str += IS_WEBKIT ? '::-webkit-progress-value' : '::-moz-progress-bar';
var bg_clr = '{background-color: ' + some_color + '}';
if (bar_color_sheet.insertRule)
{
bar_color_sheet.insertRule(selector_str + ' ' + bg_clr, style_num);
}
else
{
bar_color_sheet.addRule(selector_str, bg_clr, style_num);
}
}
function getBarColor(some_guid)
{
if (bar_color_sheet.selectors.hasOwnProperty(some_guid))
{
var rule_num = bar_color_sheet.selectors[some_guid];
return (bar_color_sheet.rules[rule_num].style.backgroundColor);
}
else
{
//the default grey
return 'rgb(200, 200, 200)';
}
}
function rgbStrToRgbObj(rgb_str)
{
if (!rgb_str.match(RGB_STR_REGEX))
{
return null;
}
//remove the 'rgb(' header
rgb_str = rgb_str.substring( ( 'rgb(' ).length );
//remove the last ')'
rgb_str = rgb_str.substring(0, rgb_str.length -1);
var rgb_arr = rgb_str.split(',');
return {
r: parseInt(rgb_arr[0], 10),
g: parseInt(rgb_arr[1], 10),
b: parseInt(rgb_arr[2], 10)
};
}
function rgbObjToRgbStr(rgb_obj)
{
return ( 'rgb(' + rgb_obj.r + ',' + rgb_obj.g + ',' + rgb_obj.b + ')' );
}
function rgbEquality(ob1, ob2)
{
return (
ob1.r === ob2.r &&
ob1.g === ob2.g &&
ob1.b === ob2.b
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment