Change Background-color on scroll - javascript

I'm wanting to create an effect like the one on the following page http://readymag.com/ where the background color changes depending on the scroll position but have no idea how to go about it and I can't understand their code.
I've seen a few examples that change from 1 color to another but I'm unsure how to do it with multiple colors.
(I would like to be able to specify each color)
Any help would be greatly appreciated.
Michael.

Here is a simple way to do it:
HTML
<body onscroll="scroll()">
...
</body>
JavaScript
// HSL Colors
var colors = [
[0, 100, 50],
[113, 75, 25],
[240, 87, 40],
[328, 24, 40]
],
el = document.getElementsByTagName('body')[0], // Element to be scrolled
length = colors.length, // Number of colors
height = Math.round(el.offsetHeight / length); // Height of the segment between two colors
function scroll() {
var i = Math.floor(el.scrollTop / height), // Start color index
d = el.scrollTop % height / height, // Which part of the segment between start color and end color is passed
c1 = colors[i], // Start color
c2 = colors[(i+1)%length], // End color
h = c1[0] + Math.round((c2[0] - c1[0]) * d),
s = c1[1] + Math.round((c2[1] - c1[1]) * d),
l = c1[2] + Math.round((c2[2] - c1[2]) * d);
el.style['background-color'] = ['hsl(', h, ', ', s+'%, ', l, '%)'].join('');
}
Working example: http://jsbin.com/elolud/2/edit

Related

Create a dashed or dotted line in jsPDF

I need to draw a dashed line in a PDF created using jsPDF (https://mrrio.github.io/jsPDF/doc/symbols/jsPDF.html)
A simple line is created as:
doc.line(20, 25, 60, 25);
http://jsfiddle.net/S3XRp/78/
How can I create a dashed or dotted line ?
I had the same problem, and did it like so:
/**
* Draws a dotted line on a jsPDF doc between two points.
* Note that the segment length is adjusted a little so
* that we end the line with a drawn segment and don't
* overflow.
*/
function dottedLine(doc, xFrom, yFrom, xTo, yTo, segmentLength)
{
// Calculate line length (c)
var a = Math.abs(xTo - xFrom);
var b = Math.abs(yTo - yFrom);
var c = Math.sqrt(Math.pow(a,2) + Math.pow(b,2));
// Make sure we have an odd number of line segments (drawn or blank)
// to fit it nicely
var fractions = c / segmentLength;
var adjustedSegmentLength = (Math.floor(fractions) % 2 === 0) ? (c / Math.ceil(fractions)) : (c / Math.floor(fractions));
// Calculate x, y deltas per segment
var deltaX = adjustedSegmentLength * (a / c);
var deltaY = adjustedSegmentLength * (b / c);
var curX = xFrom, curY = yFrom;
while (curX <= xTo && curY <= yTo)
{
doc.line(curX, curY, curX + deltaX, curY + deltaY);
curX += 2*deltaX;
curY += 2*deltaY;
}
}
Later versions of jsPDF have a built-in function:
setLineDash [Docs]
The following, for example, draws a dashed line consiting of 10mm of line drawn, followed by 10mm of space repeating along the way from left to right. I've assumed you're drawing onto a page that has all the default settings (i.e. A4, mm units, etc):
doc.setLineDash([10, 10], 0);
doc.line(20, 25, 60, 25);
And the below will draw 7mm of line, 3mm of space, 1mm of line, 3mm of space and then repeats, however, it will start the pattern 10mm in so the first part of the dash to be drawn is the 1mm section:
doc.setLineDash([7, 3, 1, 3], 10);
doc.line(20, 25, 60, 25);

Add text to raphaelJS pie chart?

Trying to create an interactive pie chart and raphaelJS seemed like the best solution. I have almost zero experience with it, (though I'm fairly proficient with jquery) so pardon if I come off as a complete idiot.
Question: I've created a functional interactive pie chart that displays text on hover. My problem is that I can't figure out how to style the text as I need to - adding and tags to the html doesn't have an effect
Here is the script - I'll try to just include the relevant sections:
var angle = -18,
total = 0,
start = 0,
process = function (j) {
var value = values[j],
angleplus = 360 * value / total,
popangle = angle + (angleplus / 2), // angle of text display
color = Raphael.hsb(start, .9, 1),
ms = 300,
delta = 30,
bcolor = Raphael.hsb(start, 1, 1),
p = sector(cx, cy, r, angle, angle + angleplus, {
fill: "90-" + bcolor + "-" + color,
stroke: "#F3F5F2",
"stroke-width": 4,
"stroke-opacity": 1,
opacity: .5}),
txt = paper.text(cx + (r + delta + 155) * Math.cos(0 * rad),
cy + (r + delta + 75) * Math.sin(-10 * rad),
labels[j]).attr({
fill: "#111",
stroke: "none",
opacity: 0,
"font-family": "Gotham",
"font-size": 14});
});
Here's the jsfiddle with the rest of the script and html (can't get it to run correctly though, works fine normally) - http://jsfiddle.net/9L286/
Currently, "txt" defines the style for text - I want to be able to style both the title ("item 1") and the description. Hopefully I'm just blind and there's a simple solution. If more info is needed, just ask. Thanks!

Change background color multiple times while scrolling down the page

I'm working on this project where Iā€™m creating a website using parallax scrolling. It's supposed to be one long one pager. As you scroll down the page the background color is supposed to change when you get to each new section of the page.
I have spent days searching the web and also here on stackoverflow, but I haven't found anything that works in the way i want it to.
I found this script here on stack:
var tStart = 100 // Start transition 100px from top
, tEnd = 500 // End at 500px
, cStart = [250, 195, 56] // Gold
, cEnd = [179, 217, 112] // Lime
, cDiff = [cEnd[0] - cStart[0], cEnd[1] - cStart[1], cEnd[1] - cStart[0]];
$(document).ready(function(){
$(document).scroll(function() {
var p = ($(this).scrollTop() - tStart) / (tEnd - tStart); // % of transition
p = Math.min(1, Math.max(0, p)); // Clamp to [0, 1]
var cBg = [Math.round(cStart[0] + cDiff[0] * p), Math.round(cStart[1] + cDiff[1] * p), Math.round(cStart[2] + cDiff[2] * p)];
$("body").css('background-color', 'rgb(' + cBg.join(',') +')');
});
});
http://jsfiddle.net/dtZDZ/12/ Here is the fiddle
This script does exactly what I want, except that I only change color one time back and forth. I need it to change background color like 4-5 times while you scroll down the page. Also I would like it to have a smooth transition when changing colors like in the fiddle :)
I hope someone out there can help me with this or just point me in the right direction.
Thank you in advance
You could use a gradient background using css :
body {
background-color: linear-gradient(red, blue, green, blue, red)
}
Scrolling down, your background will change. This method is a little bit "cheaty", but it works as it will loop when it arrives at the end of the background.
Here you go :
You may assign as many colors as you like in your colors variable
var colors = [
[250, 195, 56], // Gold
[250, 0, 0], // Red
[0, 250, 0], // Green
[0, 0, 250], // Blue
[179, 217, 112] // Lime
];
var height = $('body').height() - window.innerHeight;
$(document).scroll(function() {
var steps = Math.floor(height / colors.length);
var position = $(this).scrollTop();
var currentStep = Math.floor(position / steps);
if ( currentStep === colors.length ) currentStep = colors.length - 1;
var rgb = $("body").css('background-color').replace('rgb(','').replace(')','').replace(/\s/g, '').split(',');
var previousColor = colors[currentStep] || colors[0];
var nextColor = colors[currentStep+1] || colors[colors.length-1];
var percentFromThisStep = ( position - ( currentStep * steps ) ) / steps;
if ( percentFromThisStep > 1 ) percentFromThisStep = 1;
var newRgb = [
Math.floor(previousColor[0] + ( ( nextColor[0] - previousColor[0] ) * percentFromThisStep )),
Math.floor(previousColor[1] + ( ( nextColor[1] - previousColor[1] ) * percentFromThisStep )),
Math.floor(previousColor[2] + ( ( nextColor[2] - previousColor[2] ) * percentFromThisStep ))
];
$("body").css('background-color', 'rgb('+ newRgb.join(',') +')');
});
Demo here : http://jsbin.com/ulohif/1/edit

Color difference/similarity% between two values with JS

I need to compute the difference between two hex color values so the output is a percentage value. The first thing I discarted was converting the hex value into decimal, as the first one will have much higher weight than the last.
The second option is to compute the difference between each of the RGB values and then add them all. However, the difference between 0, 0, 0 and 30, 30, 30 is much lower than the one between 0, 0, 0 and 90, 0, 0.
This question recommends using YUV, but I can't figure out how to use it to establish the difference.
Also, this other question has a nice formula to compute the difference and output a RGB value, but it's not quite there.
For those just looking for a quick copy/paste, here's the code from this repo by antimatter15 (with some tweaks for ease of use):
function deltaE(rgbA, rgbB) {
let labA = rgb2lab(rgbA);
let labB = rgb2lab(rgbB);
let deltaL = labA[0] - labB[0];
let deltaA = labA[1] - labB[1];
let deltaB = labA[2] - labB[2];
let c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
let c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
let deltaC = c1 - c2;
let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
let sc = 1.0 + 0.045 * c1;
let sh = 1.0 + 0.015 * c1;
let deltaLKlsl = deltaL / (1.0);
let deltaCkcsc = deltaC / (sc);
let deltaHkhsh = deltaH / (sh);
let i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh;
return i < 0 ? 0 : Math.sqrt(i);
}
function rgb2lab(rgb){
let r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, x, y, z;
r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.00000;
z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
x = (x > 0.008856) ? Math.pow(x, 1/3) : (7.787 * x) + 16/116;
y = (y > 0.008856) ? Math.pow(y, 1/3) : (7.787 * y) + 16/116;
z = (z > 0.008856) ? Math.pow(z, 1/3) : (7.787 * z) + 16/116;
return [(116 * y) - 16, 500 * (x - y), 200 * (y - z)]
}
To use it, just pass in two rgb arrays:
deltaE([128, 0, 255], [128, 0, 255]); // 0
deltaE([128, 0, 255], [128, 0, 230]); // 3.175
deltaE([128, 0, 255], [128, 0, 230]); // 21.434
deltaE([0, 0, 255], [255, 0, 0]); // 61.24
The above table is from here. The above code is based on the 1994 version of DeltaE.
the issue is that you want something like a distance on a 3 dimensionnal world,
but that rgb representation is not intuitive at all : 'near' colors can be
much different that 'far' color.
Take for instance two shades of grey c1 : (120,120,120) and c2 : (150,150,150) and a now take c3 : (160,140,140) it is closer to c2 than c1, yet it is purple, and for the eye the darker grey is much closer to grey than a purple.
I would suggest you to use hsv : a color is defined by a 'base' color (hue), the saturation, and the intensity. colors having close hue are indeed very close. colors having very different hue do not relate one to another (expl : yellow and green ) but might seem closer with a (very) low saturation and (very) low intensity.
( At night all colors are alike. )
Since the hue is divided into 6 blocks, cyl = Math.floor( hue / 6 ) gives you the first step of your similarity evalution : if same part of the cylinder -> quite close.
If they don't belong to same cylinder, they might still be (quite) close if (h2-h1) is small, compare it to (1/6). If (h2-h1) > 1/6 this might just be too different colors.
Then you can be more precise with the (s,v). Colors they are nearer if both low/very low saturation and/or low intensity.
Play around with a color picker supporting both rgb and hsv until you know what you would like to have as a difference value. But be aware that you cannot have a 'true' similarity measure.
you have a rgb --> hsv javascript convertor here : http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
Just compute an Euclidean distance:
var c1 = [0, 0, 0],
c2 = [30, 30, 30],
c3 = [90, 0, 0],
distance = function(v1, v2){
var i,
d = 0;
for (i = 0; i < v1.length; i++) {
d += (v1[i] - v2[i])*(v1[i] - v2[i]);
}
return Math.sqrt(d);
};
console.log( distance(c1, c2), distance(c1, c3), distance(c2, c3) );
//will give you 51.96152422706632 90 73.48469228349535
I released an npm/Bower package for calculating the three CIE algorithms: de76, de94, and de00.
It's public domain and on Github:
http://zschuessler.github.io/DeltaE/
Here's a quickstart guide:
Install via npm
npm install delta-e
Usage
// Include library
var DeltaE = require('delta-e');
// Create two test LAB color objects to compare!
var color1 = {L: 36, A: 60, B: 41};
var color2 = {L: 100, A: 40, B: 90};
// 1976 formula
console.log(DeltaE.getDeltaE76(color1, color2));
// 1994 formula
console.log(DeltaE.getDeltaE94(color1, color2));
// 2000 formula
console.log(DeltaE.getDeltaE00(color1, color2));
You will need to convert to LAB color to use this library. d3.js has an excellent API for doing that - and I'm sure you can find something adhoc as well.
The 3rd rule for color comparisons on ColorWiki is "Never attempt to convert between color differences calculated by different equations through the use of averaging factors". This is because colors that are mathematically close to each other aren't always visually similar to us humans.
What you're looking for is probably delta-e, which is a single number that represents the 'distance' between two colors.
The most popular algorithms are listed below, with CIE76 (aka CIE 1976 or dE76) being the most popular.
CIE76
CMC l:c
dE94
dE2000
Each one goes about things in a different way, but for the most part they all require you to convert to a better (for comparison) color model than RGB.
Wikipedia has all the formulae: http://en.wikipedia.org/wiki/Color_difference
You can check your work with online color calculators:
CIE76
CMC l:c
Finally, it's not javascript but there's an open-source c# library I started will do some of these conversions and calculations: https://github.com/THEjoezack/ColorMine
Using Color.js:
let Color = await import("https://cdn.jsdelivr.net/npm/colorjs.io#0.0.5/dist/color.esm.js").then(m => m.default);
let color1 = new Color(`rgb(10,230,95)`);
let color2 = new Color(`rgb(100,20,130)`);
let colorDistance = color1.deltaE2000(color2);
A distance of 0 means the colors are identical, and a value of 100 means they're opposite.
deltaE2000 is the current industry standard (it's better than the 1994 version mentioned in top answer), but Color.js also has other algorithms like deltaE76, deltaECMC and deltaEITP.

Generate colors between red and green for an input range [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Color coding based on number
I want for a user to be able to select from a range from 1-100, where as the numbers become less than 50, the color of the area becomes darker green, and as the color becomes closer to 100, the color becomes more red.
I am trying to make it so that as the range in more towards the center, the color should be close to white (where 50 = full white).
I tried the answer from here: Generate colors between red and green for a power meter? to no avail.... 50 ends up being a muddled green...
I have the following html:
<span><span class="value">50</span><input type="range" /></span>ā€‹
And the following javascript:
$(document).on({
change: function(e) {
var self = this,
span = $(self).parent("span"),
val = parseInt(self.value);
if (val > 100) {
val = 100;
}
else if (val < 0) {
val = 0;
}
$(".value", span).text(val);
var r = Math.floor((255 * val) / 100),
g = Math.floor((255 * (100 - val)) / 100),
b = 0;
span.css({
backgroundColor: "rgb(" + r + "," + g + "," + b + ")"
});
}
}, "input[type='range']");ā€‹
Fiddle: http://jsfiddle.net/maniator/tKrM9/1/
I have tried many different combinations of r,g,b but I really cannot seem to get it right.
You're getting the muddled green because of the way you're creating your gradient in RBG space. To get a "cleaner" gradient, you can use the HSV model as mentioned in the answer of the question you linked to.
RGB gradient (top) vs HSV (bottom)
By scaling the H (hue) value between 0 (red) and 120 (green) you'll get a nice clean transition. However, at the mid point (60) you'll end up with bright yellow instead of your intended white. You can address this by modifying the S (saturation) value -- at 0 saturation, you'll end up with white (1 gives you full colour saturation.
Here's a crude example which scales the saturation from 1 to 0 and back to 1 as the input value goes from 0 to 50 to 100 - http://jsfiddle.net/xgJ2e/2/
var hue = Math.floor((100 - val) * 120 / 100); // go from green to red
var saturation = Math.abs(val - 50)/50; // fade to white as it approaches 50
p.s. Converting between colour models is easy using jquery-colors, although it's not too hard to roll your own.
I came up with this answer with some help from here, which uses an Interpolate function in which I can set the start and end colors easily.
function Interpolate(start, end, steps, count) {
var s = start,
e = end,
final = s + (((e - s) / steps) * count);
return Math.floor(final);
}
function Color(_r, _g, _b) {
var r, g, b;
var setColors = function(_r, _g, _b) {
r = _r;
g = _g;
b = _b;
};
setColors(_r, _g, _b);
this.getColors = function() {
var colors = {
r: r,
g: g,
b: b
};
return colors;
};
}
$(document).on({
change: function(e) {
var self = this,
span = $(self).parent("span"),
val = parseInt(self.value),
red = new Color(232, 9, 26),
white = new Color(255, 255, 255),
green = new Color(6, 170, 60),
start = green,
end = white;
$(".value", span).text(val);
if (val > 50) {
start = white,
end = red;
val = val % 51;
}
var startColors = start.getColors(),
endColors = end.getColors();
var r = Interpolate(startColors.r, endColors.r, 50, val);
var g = Interpolate(startColors.g, endColors.g, 50, val);
var b = Interpolate(startColors.b, endColors.b, 50, val);
span.css({
backgroundColor: "rgb(" + r + "," + g + "," + b + ")"
});
}
}, "input[type='range']");ā€‹
Fiddle: http://jsfiddle.net/maniator/tKrM9/53/
In a very hand wavy way, I think I would do it this way:
Make the range from 1-50 have maximum green (FF), and a scaled value for both red and blue ranging from 00 for red and blue at 1, all the way up to FF for red and blue at 50.
(Sample values: 1 -> 00FF00, 25 -> 7FFF7F, 50 -> FFFFFF)
Then from 51-100, keep red at FF, while scaling back blue and green so that blue and green approach 0 as you reach 100.
(Sample values: 51 -> FFFFFF, 75 -> FF7F7F, 100 -> FF0000)
This is guaranteed to give you brilliant green at 0, white at 50, and red at 100.
The areas in between may not be exactly perfect, simply because the eye interprets different color intensities differently (we're better at some colors than others), but it should get you pretty close.
I modified the code in your fiddle to the following, and it does what I describe:
$(document).on({
change: function(e) {
var self = this,
span = $(self).parent("span"),
val = parseInt(self.value);
if (val > 100) {
val = 100;
}
else if (val < 0) {
val = 0;
}
$(".value", span).text(val);
if (val <= 50)
{
r = Math.floor((255 * (val / 50))),
g = 255,
b = Math.floor((255 * (val / 50)));
}
else
{
r = 255,
g = Math.floor((100 - val) / 50 * 255),
b = Math.floor((100 - val) / 50 * 255);
}
span.css({
backgroundColor: "rgb(" + r + "," + g + "," + b + ")"
});
}
}, "input[type='range']");

Categories