How to change colors percent to RGB - javascript

I created my colors code 0-70 from green to red. Now, I want to change colors 0 - 70 from red to green.
function percentToRGB(percent) {
if (percent === 100) {
percent = 99
}
var r, g, b;
if (percent < 50) {
// green to yellow
r = Math.floor(255 * (percent / 50));
g = 255;
} else {
// yellow to red
r = 255;
g = Math.floor(255 * ((50 - percent % 50) / 50));
}
b = 0;
return "rgb(" + r + "," + g + "," + b + ")";
}
function render(i) {
var item = "<li style='background-color:" + percentToRGB(i) + "'>" + i + "</li>";
$("ul").append(item);
}
function repeat(fn, times) {
for (var i = 0; i < times; i++) fn(i);
}
repeat(render, 100);
li {
font-size:8px;
height:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul></ul>

You should just be able to switch the g and r variables around within the percentToRGB function. Here's a JSFiddle Link.
So the function would become:
function percentToRGB(percent) {
if (percent === 100) {
percent = 99
}
var r, g, b;
if (percent < 50) {
// green to yellow
g = Math.floor(255 * (percent / 50));
r = 255;
} else {
// yellow to red
g = 255;
r = Math.floor(255 * ((50 - percent % 50) / 50));
}
b = 0;
return "rgb(" + r + "," + g + "," + b + ")";
}

Related

Why does this canvas animation occasionally scramble itself?

For a website I am creating, I coded a parallax background using HTML5 canvas. Running the snippet below will produce a 192x192 sample of the background animation.
function parallaxCipher(size, color, speed, grid, sector, seed){
var scr = document.getElementById("parallax" + sector);
var ctx = scr.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0,0,scr.width,scr.height);
var xGridMax = Math.ceil(scr.width / grid);
var yGridMax = Math.ceil(scr.height / grid) + 1;
var seedList = prng(seed,yGridMax * 2);
for(var c = 0; c < yGridMax;c++){
seedList[c] += seedList[c + yGridMax] * 256;
}
seedList.length = yGridMax;
var rotation = Math.floor(Math.floor(Date.now() / speed) / grid) % yGridMax;
rotation = yGridMax - rotation - 1;
if(rotation > 0){
seedList = seedList.slice(seedList.length-rotation).concat(seedList.slice(0,seedList.length - rotation));
}
ctx.fillStyle = color;
for(var a = 0;a<yGridMax;a++){
var row = prng(seedList[a],Math.ceil((xGridMax + 1)/8));
var row2 = [];
for(var d = 0;d<row.length;d++){
row[d] = row[d].toString(2);
row[d] = "0".repeat(8 - row[d].length) + row[d];
row2 = row2.concat(row[d].split(''));
}
row2.length = xGridMax + 1;
var rotation2 = Math.floor(Math.floor(Date.now() / speed) / grid) % xGridMax;
if(rotation2 > 0){
row2 = row2.slice(row2.length-rotation2).concat(row2.slice(0,row2.length - rotation2));
}
for(var b = -1;b<xGridMax;b++){
ctx.font = size + "px monospace";
ctx.fillText(row2[b + 1],(b * grid) - (size * 11 / 30) + ((Date.now() / speed) % grid),(a * grid) + (size / 2) - ((Date.now() / speed) % grid));
}
}
}
function prng(seed, instances){
//PRNG takes a 16 bit seed
var primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627];
var res = [];
var j = seed % 256;
var k = Math.floor(seed / 256);
for(var a = 0;a < instances;a++){
res.push(((primes[j] * primes[k]) % 257) - 1);
if(primes.includes(res[res.length - 1]) || primes.includes(res[res.length - 1] + 1)){
j = (j + 1) % 257;
}else{
k = (k - 1);
if(k == -1){
k = 256;
}
}
if(res[res.length - 1] == undefined){
console.warn("PRNG error: " + j + ", " + k + ", " + primes[j] + ", " + primes[k]);
}
}
return res;
}
function animate(){
window.requestAnimationFrame(animate);
parallaxCipher(15,"#0f0",25,18,"1",9999);
}
window.requestAnimationFrame(animate);
<canvas width="192" height="192" style="border:2px solid black;" id="parallax1">Nope.</canvas>
You may notice that every 10 seconds or so, the bits scramble themselves, as if the seed value randomly changed, even though the seed in this example is fixed at 9999.
I think it may be a problem with the rotation/rotation2 values, but I am not entirely sure. In fact, the bug appears to not be in sync with the rotation cycle at all, leaving me at a complete loss as to how this problem is occuring.
Forgive my terrible code, but can anyone help pinpoint the problem? Any help is appreciated!
I figured it out!
rotation2 is supposed to be calculated modulo xGridMax + 1, not xGridMax.
Thus, the correct animation (large version) is:
function parallaxCipher(size, color, speed, grid, sector, seed){
var scr = document.getElementById("parallax" + sector);
var ctx = scr.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0,0,scr.width,scr.height);
var xGridMax = Math.ceil(scr.width / grid);
var yGridMax = Math.ceil(scr.height / grid) + 1;
var seedList = prng(seed,yGridMax * 2);
for(var c = 0; c < yGridMax;c++){
seedList[c] += seedList[c + yGridMax] * 256;
}
seedList.length = yGridMax;
var rotation = Math.floor(Math.floor(Date.now() / speed) / grid) % yGridMax;
rotation = yGridMax - rotation - 1;
if(rotation > 0){
seedList = seedList.slice(seedList.length-rotation).concat(seedList.slice(0,seedList.length - rotation));
}
ctx.fillStyle = color;
for(var a = 0;a<yGridMax;a++){
var row = prng(seedList[a],Math.ceil((xGridMax + 1)/8));
var row2 = [];
for(var d = 0;d<row.length;d++){
row[d] = row[d].toString(2);
row[d] = "0".repeat(8 - row[d].length) + row[d];
row2 = row2.concat(row[d].split(''));
}
row2.length = xGridMax + 1;
var rotation2 = Math.floor(Math.floor(Date.now() / speed) / grid) % (xGridMax+1);
if(rotation2 > 0){
row2 = row2.slice(row2.length-rotation2).concat(row2.slice(0,row2.length - rotation2));
}
for(var b = -1;b<xGridMax;b++){
ctx.font = size + "px monospace";
ctx.fillText(row2[b + 1],(b * grid) - (size * 11 / 30) + ((Date.now() / speed) % grid),(a * grid) + (size / 2) - ((Date.now() / speed) % grid));
}
}
}
function prng(seed, instances){
//PRNG takes a 16 bit seed
var primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997,1009,1013,1019,1021,1031,1033,1039,1049,1051,1061,1063,1069,1087,1091,1093,1097,1103,1109,1117,1123,1129,1151,1153,1163,1171,1181,1187,1193,1201,1213,1217,1223,1229,1231,1237,1249,1259,1277,1279,1283,1289,1291,1297,1301,1303,1307,1319,1321,1327,1361,1367,1373,1381,1399,1409,1423,1427,1429,1433,1439,1447,1451,1453,1459,1471,1481,1483,1487,1489,1493,1499,1511,1523,1531,1543,1549,1553,1559,1567,1571,1579,1583,1597,1601,1607,1609,1613,1619,1621,1627];
var res = [];
var j = seed % 256;
var k = Math.floor(seed / 256);
for(var a = 0;a < instances;a++){
res.push(((primes[j] * primes[k]) % 257) - 1);
if(primes.includes(res[res.length - 1]) || primes.includes(res[res.length - 1] + 1)){
j = (j + 1) % 257;
}else{
k = (k - 1);
if(k == -1){
k = 256;
}
}
if(res[res.length - 1] == undefined){
console.warn("PRNG error: " + j + ", " + k + ", " + primes[j] + ", " + primes[k]);
}
}
return res;
}
function animate(){
window.requestAnimationFrame(animate);
parallaxCipher(15,"#0f0",25,18,"1",9999);
}
window.requestAnimationFrame(animate);
<canvas width="512" height="512" style="border:2px solid black;" id="parallax1">Nope.</canvas>

Draw oval using Leafletjs Draw Library

I am trying to draw oval and Circle using Leafletjs Draw library, it works fine but the problem is the Circle boundary doesn't touch with the Mouse pointer on mousemove. here is the code and fiddle.
try to draw the oval you will observe the mouse pointer is not touching the circle boundary
https://jsfiddle.net/Lscupxqp/12/
var points = [L.GeoJSON.latLngToCoords(this._startLatLng),L.GeoJSON.latLngToCoords(latlng)];
var x = Math.abs(points[1][0] - points[0][0]);
var y = Math.abs(points[1][1] - points[0][1]);
var x_percent, y_percent;
x_percent = y_percent = 1;
//show in %
if(x < y) {
x_percent = x / y;
}
else {
y_percent = y / x;
}
this._drawShape(latlng);
this._shape.rx = x_percent;
this._shape.ry = y_percent;
GetPathString method
getPathString: function () {
var p = this._point,
r = this._radius;
if (this._checkIfEmpty()) {
return '';
}
//console.log(this);
if (L.Browser.svg) {
var rr = 'M' + p.x + ',' + (p.y - r) + 'A' + (r * this.rx) + ',' + (r * this.ry) + ',0,1,1,' + (p.x - 0.1) + ',' + (p.y - r) + ' z';
return rr;
} else {
p._round();
r = Math.round(r);
return 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r + ' 0,' + (65535 * 360);
}
}
It seems I've got your mistake - change (p.y - r) to (p.y - r * this.ry) :
if (L.Browser.svg) {
var rr = 'M' + p.x + ',' + (p.y - r * this.ry) +
'A' + (r * this.rx) + ',' + (r * this.ry) + ',0,0,0,' + p.x + ',' + (p.y + r * this.ry) +
'A' + (r * this.rx) + ',' + (r * this.ry) + ',0,1,0,' + (p.x) + ',' + (p.y - r * this.ry) +' z';

Pixel by pixel collision detection pinball

I'm currently working on a Pinball game using the HTML5 Canvas and JavaScript. Right now I'm getting a hard time with the pixel by pixel collision, which is fundamental because of the flippers.
Right now my Bounding Box Collision seems to be working
checkCollision(element) {
if (this.checkCollisionBoundingBox(element)) {
console.log("colision with the element bounding box");
if (this.checkCollisionPixelByPixel(element)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
checkCollisionBoundingBox(element) {
if (this.pos.x < element.pos.x + element.width && this.pos.x + this.width > element.pos.x && this.pos.y < element.pos.y + element.height && this.pos.y + this.height > element.pos.y) {
return true;
} else {
return false;
}
}
I've tried several ways of implementing the pixel by pixel one but for some reason it does not work perfectly (on walls, on images, on sprites etc). I'll leave them here:
checkCollisionPixelByPixel(element) {
var x_left = Math.floor(Math.max(this.pos.x, element.pos.x));
var x_right = Math.floor(Math.min(this.pos.x + this.width, element.pos.x + element.width));
var y_top = Math.floor(Math.max(this.pos.y, element.pos.y));
var y_bottom = Math.floor(Math.min(this.pos.y + this.height, element.pos.y + element.height));
for (var y = y_top; y < y_bottom; y++) {
for (var x = x_left; x < x_right; x++) {
var x_0 = Math.round(x - this.pos.x);
var y_0 = Math.round(y - this.pos.y);
var n_pix = y_0 * (this.width * this.total) + (this.width * (this.actual-1)) + x_0; //n pixel to check
var pix_op = this.imgData.data[4 * n_pix + 3]; //opacity (R G B A)
var element_x_0 = Math.round(x - element.pos.x);
var element_y_0 = Math.round(y - element.pos.y);
var element_n_pix = element_y_0 * (element.width * element.total) + (element.width * (element.actual-1)) + element_x_0; //n pixel to check
var element_pix_op = element.imgData.data[4 * element_n_pix + 3]; //opacity (R G B A)
console.log(element_pix_op);
if (pix_op == 255 && element_pix_op == 255) {
console.log("Colision pixel by pixel");
/*Debug*/
/*console.log("This -> (R:" + this.imgData.data[4 * n_pix] + ", G:" + this.imgData.data[4 * n_pix + 1] + ", B:" + this.imgData.data[4 * n_pix + 2] + ", A:" + pix_op + ")");
console.log("Element -> (R:" + element.imgData.data[4 * element_n_pix] + ", G:" + element.imgData.data[4 * element_n_pix + 1] + ", B:" + element.imgData.data[4 * element_n_pix + 2] + ", A:" + element_pix_op + ")");
console.log("Collision -> (x:" + x + ", y:" + y +")");
console.log("This(Local) -> (x:" + x_0 + ", y:" + y_0+")");
console.log("Element(Local) -> (x:" + element_x_0 + ", y:" + element_y_0+")");*/
/*ball vector*/
var vector = {
x: (x_0 - Math.floor(this.imgData.width / 2)),
y: -(y_0 - Math.floor(this.imgData.height / 2))
};
//console.log("ball vector -> ("+vector.x+", "+vector.y+") , Angulo: "+ Math.atan(vector.y/vector.x)* 180/Math.PI);
// THIS WAS THE FIRST TRY, IT DIDN'T WORK WHEN THE BALL WAS GOING NORTHEAST AND COLLIDED WITH A WALL. DIDN'T WORK AT ALL WITH SPRITES
//this.angle = (Math.atan2(vector.y, vector.x) - Math.PI) * (180 / Math.PI);
// THIS WAS THE SECOND ATTEMPT, WORKS WORSE THAN THE FIRST ONE :/
//normal vector
var normal = {
x: (x_0 - (this.imgData.width / 2)),
y: -(y_0 - (this.imgData.height / 2))
};
//Normalizar o vetor
var norm = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
if (norm != 0) {
normal.x = normal.x / norm;
normal.y = normal.y / norm;
}
var n_rad = Math.atan2(normal.y, normal.x);
var n_deg = (n_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Normal -> (" + normal.x + ", " + normal.y + ") , Angulo: " + n_deg);
//Vetor Velocidade
var velocity = {
x: Math.cos((this.angle * Math.PI / 180) - Math.PI),
y: Math.sin((this.angle * Math.PI / 180) - Math.PI)
};
console.log("Vetor Velocidade -> (" + velocity.x + ", " + velocity.y + ") , Angulo: " + this.angle);
//Vetor Reflexao
var ndotv = normal.x * velocity.x + normal.y * velocity.y;
var reflection = {
x: -2 * ndotv * normal.x + velocity.x,
y: -2 * ndotv * normal.y + velocity.y
};
var r_rad = Math.atan2(reflection.y, reflection.x);
var r_deg = (r_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Reflexao -> (" + reflection.x + ", " + reflection.y + ") , Angulo: " + r_deg);
this.angle = r_deg;
return true;
}
}
}
return false;
}
}
The ball class
class Ball extends Element {
constructor(img, pos, width, height, n, sound, angle, speed) {
super(img, pos, width, height, n, sound);
this.angle = angle; //direction [0:360[
this.speed = speed;
}
move(ctx, cw, ch) {
var rads = this.angle * Math.PI / 180
var vx = Math.cos(rads) * this.speed / 60;
var vy = Math.sin(rads) * this.speed / 60;
this.pos.x += vx;
this.pos.y -= vy;
ctx.clearRect(0, 0, cw, ch);
this.draw(ctx, 1);
}
}
Assuming a "flipper" is composed of 2 arcs and 2 lines it would be much faster to do collision detection mathematically rather than by the much slower pixel-test method. Then you just need 4 math collision tests.
Even if your flippers are a bit more complicated than arcs+lines, the math hit tests would be "good enough" -- meaning in your fast-moving game, the user cannot visually notice the approximate math results vs the pixel-perfect results and the difference between the 2 types of tests will not affect gameplay at all. But the pixel-test version will take magnitudes more time and resources to accomplish. ;-)
First two circle-vs-circle collision tests:
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
Then two circle-vs-line-segment collision tests:
// [x0,y0] to [x1,y1] define a line segment
// [cx,cy] is circle centerpoint, cr is circle radius
function isCircleSegmentColliding(x0,y0,x1,y1,cx,cy,cr){
// calc delta distance: source point to line start
var dx=cx-x0;
var dy=cy-y0;
// calc delta distance: line start to end
var dxx=x1-x0;
var dyy=y1-y0;
// Calc position on line normalized between 0.00 & 1.00
// == dot product divided by delta line distances squared
var t=(dx*dxx+dy*dyy)/(dxx*dxx+dyy*dyy);
// calc nearest pt on line
var x=x0+dxx*t;
var y=y0+dyy*t;
// clamp results to being on the segment
if(t<0){x=x0;y=y0;}
if(t>1){x=x1;y=y1;}
return( (cx-x)*(cx-x)+(cy-y)*(cy-y) < cr*cr );
}

Weird Streaks in HTML5 Canvas Drawing?

I'm trying to draw a square of procedurally generated colors using an HTML5 canvas. It should show black at the origin, white at the corner furthest from the origin, and yellow and purple opposite each other in the remaining corners, with various reds in the middle.
Here's a low resolution version I did by hand. In my system, the Y axis is opposite that of canvas's, but that's not important to me, only the generated colors are important. Also in my system, color channels are floats between 0 and 1, not 0 and 255, while the coordinates range from 0 to 1, not 0 to 255.
Here's the code - dividing by 255 is to account for the fact x and y go from 0 to 255 rather than 0 to 1, dividing by 65025 is just 255^2, to account for the fact x and y are both involved, and multiplying by 255 is because the color channels go from 0 to 255, rather than 0 to 1.
<canvas id = "mainCanvas" width="256" height="256"><p>Your browser doesn't support canvas.</p></canvas>
<script>
function drawPix(x, y, r, g, b) {
if (r > 255) {
r = 255;
}
if (g < 0) {
g = 0;
}
if (b < 0) {
b = 0;
}
var c = document.getElementById("mainCanvas").getContext("2d");
c.beginPath();
c.moveTo(x, y);
c.lineTo(x + 1, y + 1);
c.closePath();
var style = "rgb(" + r + ", " + g + ", " + b + ")";
c.strokeStyle = style;
c.stroke();
}
for (var x = 0; x <= 255; x++) {
for (var y = 0; y <= 255; y++) {
drawPix(x, y, x + y, ((2*x/255.0) - (x*y/65025.0) + (y/255.0) - 1) * 255, ((2*y/255.0) - (x*y/65025.0) + (x/255.0) - 1) * 255);
}
}
</script>
I checked this with Chrome, Firefox, and Safari on OS X, plus with Chrome on Windows 7, and all of them output this image with weird streaks of purple on the right side, and the bottom left corner doesn't have a field of purple like it should.
I tried checking the logs, but got lost in a sea of information printing every color at every coordinate, so then I made it only output 1/225th as much as below. The weird streaks of purple towards the right still appear in the canvas produced by the below code, but there's no indication of the blue channel being so hight with such a low y value in the console.
<canvas id = "mainCanvas" width="256" height="256"><p>Your browser doesn't support canvas.</p></canvas>
<script>
function drawPix(x, y, r, g, b) {
if (r > 255) {
r = 255;
}
if (g < 0) {
g = 0;
}
if (b < 0) {
b = 0;
}
if (x % 15 == 0 && y % 15 == 0) {
var c = document.getElementById("mainCanvas").getContext("2d");
c.beginPath();
c.moveTo(x, y);
c.lineTo(x + 15, y + 15);
c.closePath();
var style = "rgb(" + r + ", " + g + ", " + b + ")";
console.log("X: " + x + " Y: " + y + ", " + style);
c.strokeStyle = style;
c.stroke();
}
}
for (var x = 0; x <= 255; x++) {
for (var y = 0; y <= 255; y++) {
drawPix(x, y, x + y, ((2*x/255.0) - (x*y/65025.0) + (y/255.0) - 1) * 255, ((2*y/255.0) - (x*y/65025.0) + (x/255.0) - 1) * 255);
}
}
</script>
Here's a JSFiddle of my code, if that helps you help me:
http://jsfiddle.net/ArtOfWarfare/wjzhnhy8/
rgb() does not seem to accept decimals.
Use this instead:
var style = "rgb(" + r.toFixed(0) + ", " + g.toFixed(0) + ", " + b.toFixed(0) + ")";

JavaScript Calculate brighter colour

I have a colour value in JS as a string
#ff0000
How would I go about programatically calculating a brighter/lighter version of this colour, for example #ff4848, and be able to calculate the brightness via a percentage, e.g.
increase_brightness('#ff0000', 50); // would make it 50% brighter
function increase_brightness(hex, percent){
// strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, '');
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if(hex.length == 3){
hex = hex.replace(/(.)/g, '$1$1');
}
var r = parseInt(hex.substr(0, 2), 16),
g = parseInt(hex.substr(2, 2), 16),
b = parseInt(hex.substr(4, 2), 16);
return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
}
/**
* ('#000000', 50) --> #808080
* ('#EEEEEE', 25) --> #F2F2F2
* ('EEE , 25) --> #F2F2F2
**/
Update
#zyklus's answer is simpler and has the same effect. Please refer to this answer only if you are interested in converting between RGB and HSL.
To set the brightness of RGB:
Convert RGB to HSL
Set the brightness of HSL
Convert back from HSL to RGB
This link used to have code to convert RGB to HSL and reverse:
http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* #param Number r The red color value
* #param Number g The green color value
* #param Number b The blue color value
* #return Array The HSL representation
*/
function rgbToHsl(r, g, b){
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param Number h The hue
* #param Number s The saturation
* #param Number l The lightness
* #return Array The RGB representation
*/
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [r * 255, g * 255, b * 255];
}
I made some example with it. Check this link: http://jsfiddle.net/sangdol/euSLy/4/
And this is the increase_brightness() function:
function increase_brightness(rgbcode, percent) {
var r = parseInt(rgbcode.slice(1, 3), 16),
g = parseInt(rgbcode.slice(3, 5), 16),
b = parseInt(rgbcode.slice(5, 7), 16),
HSL = rgbToHsl(r, g, b),
newBrightness = HSL[2] + HSL[2] * (percent / 100),
RGB;
RGB = hslToRgb(HSL[0], HSL[1], newBrightness);
rgbcode = '#'
+ convertToTwoDigitHexCodeFromDecimal(RGB[0])
+ convertToTwoDigitHexCodeFromDecimal(RGB[1])
+ convertToTwoDigitHexCodeFromDecimal(RGB[2]);
return rgbcode;
}
function convertToTwoDigitHexCodeFromDecimal(decimal){
var code = Math.round(decimal).toString(16);
(code.length > 1) || (code = '0' + code);
return code;
}
You can pass a negative value as a percent argument to make it darken.
In case anyone needs it, I converted the color brightness JavaScript code to ASP / VBScript for a project and thought I would share it with you:
'::Color Brightness (0-100)
'ex. ColorBrightness("#FF0000",25) 'Darker
'ex. ColorBrightness("#FF0000",50) 'Mid
'ex. ColorBrightness("#FF0000",75) 'Lighter
Function ColorBrightness(strRGB,intBrite)
strRGB = Replace(strRGB,"#","")
r = CInt("&h" & Mid(strRGB,1,2))
g = CInt("&h" & Mid(strRGB,3,2))
b = CInt("&h" & Mid(strRGB,5,2))
arrHSL = RGBtoHSL(r, g, b)
dblOrigBrite = CDbl(arrHSL(2) * 100)
arrRGB = HSLtoRGB(arrHSL(0), arrHSL(1), intBrite/100)
newRGB = "#" & HEXtoDEC(arrRGB(0)) & HEXtoDEC(arrRGB(1)) & HEXtoDEC(arrRGB(2))
ColorBrightness = newRGB
End Function
'::RGB to HSL Function
Function RGBtoHSL(r,g,b)
r = CDbl(r/255)
g = CDbl(g/255)
b = CDbl(b/255)
max = CDbl(MaxCalc(r & "," & g & "," & b))
min = CDbl(MinCalc(r & "," & g & "," & b))
h = CDbl((max + min) / 2)
s = CDbl((max + min) / 2)
l = CDbl((max + min) / 2)
If max = min Then
h = 0
s = 0
Else
d = max - min
s = IIf(l > 0.5, d / (2 - max - min), d / (max + min))
Select Case CStr(max)
Case CStr(r)
h = (g - b) / d + (IIf(g < b, 6, 0))
Case CStr(g)
h = (b - r) / d + 2
Case CStr(b)
h = (r - g) / d + 4
End Select
h = h / 6
End If
RGBtoHSL = Split(h & "," & s & "," & l, ",")
End Function
'::HSL to RGB Function
Function HSLtoRGB(h,s,l)
If s = 0 Then
r = l
g = l
b = l
Else
q = IIf(l < 0.5, l * (1 + s), l + s - l * s)
p = 2 * l - q
r = HUEtoRGB(p, q, h + 1/3)
g = HUEtoRGB(p, q, h)
b = HUEtoRGB(p, q, h - 1/3)
End If
HSLtoRGB = Split(r * 255 & "," & g * 255 & "," & b * 255, ",")
End Function
'::Hue to RGB Function
Function HUEtoRGB(p,q,t)
If CDbl(t) < 0 Then t = t + 1
If CDbl(t) > 1 Then t = t - 1
If CDbl(t) < (1/6) Then
HUEtoRGB = p + (q - p) * 6 * t
Exit Function
End If
If CDbl(t) < (1/2) Then
HUEtoRGB = q
Exit Function
End If
If CDbl(t) < (2/3) Then
HUEtoRGB = p + (q - p) * (2/3 - t) * 6
Exit Function
End If
HUEtoRGB = p
End Function
'::Hex to Decimal Function
Function HEXtoDEC(d)
h = Hex(Round(d,0))
h = Right(String(2,"0") & h,2)
HEXtoDEC = h
End Function
'::Max Function
Function MaxCalc(valList)
valList = Split(valList,",")
b = 0
For v = 0 To UBound(valList)
a = valList(v)
If CDbl(a) > CDbl(b) Then b = a
Next
MaxCalc = b
End Function
'::Min Function
Function MinCalc(valList)
valList = Split(valList,",")
For v = 0 To UBound(valList)
a = valList(v)
If b = "" Then b = a
If CDbl(a) < CDbl(b) AND b <> "" Then b = a
Next
MinCalc = b
End Function
'::IIf Emulation Function
Function IIf(condition,conTrue,conFalse)
If (condition) Then
IIf = conTrue
Else
IIf = conFalse
End If
End Function
That way you won't need any conversion of the source color.
check out this fiddle : https://jsfiddle.net/4c47otou/
increase_brightness = function(color,percent){
var ctx = document.createElement('canvas').getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0,0,1,1);
var color = ctx.getImageData(0,0,1,1);
var r = color.data[0] + Math.floor( percent / 100 * 255 );
var g = color.data[1] + Math.floor( percent / 100 * 255 );
var b = color.data[2] + Math.floor( percent / 100 * 255 );
return 'rgb('+r+','+g+','+b+')';
}
Example usage :
increase_brightness('#0000ff',20);
increase_brightness('khaki',20);
increase_brightness('rgb(12, 7, 54)',20);
// color is a hex color like #aaaaaa and percent is a float, 1.00=100%
// increasing a color by 50% means a percent value of 1.5
function brighten(color, percent) {
var r=parseInt(color.substr(1,2),16);
var g=parseInt(color.substr(3,2),16);
var b=parseInt(color.substr(5,2),16);
return '#'+
Math.min(255,Math.floor(r*percent)).toString(16)+
Math.min(255,Math.floor(g*percent)).toString(16)+
Math.min(255,Math.floor(b*percent)).toString(16);
}
Live sample: http://jsfiddle.net/emM55/
Here is the increaseBrightness function with the RGB->HSL->RGB method. "amount" should be in percent.
HSL<->RGB conversion functions taken from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
function increaseBrightness( color, amount ) {
var r = parseInt(color.substr(1, 2), 16);
var g = parseInt(color.substr(3, 2), 16);
var b = parseInt(color.substr(5, 2), 16);
hsl = rgbToHsl( r, g, b );
hsl.l += hsl.l + (amount / 100);
if( hsl.l > 1 ) hsl.l = 1;
rgb = hslToRgb( hsl.h, hsl.s, hsl.l );
var v = rgb.b | (rgb.g << 8) | (rgb.r << 16);
return '#' + v.toString(16);
}
function rgbToHsl(r, g, b){
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return {'h':h, 's':s, 'l':l};
}
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return { 'r':r * 255, 'g':g * 255, 'b':b * 255 };
}
I found a variation of Sanghyun Lee's reply generates the best result.
Convert RGB to HSL
Set the brightness of HSL
Convert back from HSLto RGB
The difference/variation is how you increase/decrease the brightness.
newBrightness = HSL[2] + HSL[2] * (percent / 100) // Original code
Instead of applying a percentage on the current brightness, it works better if it is treated as absolute increment/decrement. Since the luminosity range is 0 to 1, the percent can be applied on the whole range (1 - 0) * percent/100.
newBrightness = HSL[2] + (percent / 100);
newBrightness = Math.max(0, Math.min(1, newBrightness));
Another nice property of this approach is increment & decrement negate each other.
Image below shows darker and lighter colors with 5% increment. Note, how the palette is reasonably smooth and often ends with black and white.
Palette with original approach - gets stuck at certain colors.
I know this an old question, but I found no answer that simply manipulates css hsl color. I found the old answers here to be too complex and slow, even producing poor results, so a different approach seems warranted. The following alternative is much more performant and less complex.
Of course, this answer requires you to use hsl colors throughout your app, otherwise you still have to do a bunch of conversions! Though, if you need to manipulate brightness eg in a game loop, you should be using hsl values anyway as they are much better suited for programmatic manipulation. The only drawback with hsl from rgb as far as I can tell, is that it's harder to "read" what hue you're seeing like you can with rgb strings.
function toHslArray(hslCss) {
let sep = hslCss.indexOf(",") > -1 ? "," : " "
return hslCss.substr(4).split(")")[0].split(sep)
}
function adjustHslBrightness(color, percent) {
let hsl = toHslArray(color)
return "hsl(" + hsl[0] + "," + hsl[1] + ", " + (percent + "%") + ")"
}
let hsl = "hsl(200, 40%, 40%)"
let hsl2 = adjustHslBrightness(hsl, 80)
function brighten(color, c) {
const calc = (sub1,sub2)=> Math.min(255,Math.floor(parseInt(color.substr(sub1,sub2),16)*c)).toString(16).padStart(2,"0")
return `#${calc(1,2)}${calc(3,2)}${calc(5,2)}`
}
const res = brighten("#23DA4C", .5) // "#116d26"
console.log(res)
What I use:
//hex can be string or number
//rate: 1 keeps the color same. < 1 darken. > 1 lighten.
//to_string: set to true if you want the return value in string
function change_brightness(hex, rate, to_string = false) {
if (typeof hex === 'string') {
hex = hex.replace(/^\s*#|\s*$/g, '');
} else {
hex = hex.toString(16);
}
if (hex.length == 3) {
hex = hex.replace(/(.)/g, '$1$1');
} else {
hex = ("000000" + hex).slice(-6);
}
let r = parseInt(hex.substr(0, 2), 16);
let g = parseInt(hex.substr(2, 2), 16);
let b = parseInt(hex.substr(4, 2), 16);
let h, s, v;
[h, s, v] = rgb2hsv(r, g, b);
v = parseInt(v * rate);
[r, g, b] = hsv2rgb(h, s, v);
hex = ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
if (to_string) return "#" + hex;
return parseInt(hex, 16);
}
function rgb2hsv(r,g,b) {
let v = Math.max(r,g,b), n = v-Math.min(r,g,b);
let h = n && ((v === r) ? (g-b)/n : ((v === g) ? 2+(b-r)/n : 4+(r-g)/n));
return [60*(h<0?h+6:h), v&&n/v, v];
}
function hsv2rgb(h,s,v) {
let f = (n,k=(n+h/60)%6) => v - v*s*Math.max( Math.min(k,4-k,1), 0);
return [f(5),f(3),f(1)];
}
A variant with lodash:
// color('#EBEDF0', 30)
color(hex, percent) {
return '#' + _(hex.replace('#', '')).chunk(2)
.map(v => parseInt(v.join(''), 16))
.map(v => ((0 | (1 << 8) + v + (256 - v) * percent / 100).toString(16))
.substr(1)).join('');
}
First get a quick understanding of hex color codes.
Then it should be pretty easy to break down your color value into RGB, make the adjustments and then return the new color code.

Categories