Related
I've created this function to convert RGB color to HSL color. It works perfect.
But I need to make it run faster, because it's used to replace colors on a canvas, and I need to reduce the time of replacement. Since the image contains 360k pixels (600x600px) anything can make it faster.
That's my current implementation:
/**
* Convert RGB Color to HSL Color
* #param {{R: integer, G: integer, B: integer}} rgb
* #returns {{H: number, S: number, L: number}}
*/
Colorize.prototype.rgbToHsl = function(rgb) {
var R = rgb.R/255;
var G = rgb.G/255;
var B = rgb.B/255;
var Cmax = Math.max(R,G,B);
var Cmin = Math.min(R,G,B);
var delta = Cmax - Cmin;
var L = (Cmax + Cmin) / 2;
var S = 0;
var H = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2*L) - 1));
switch (Cmax) {
case R:
H = ((G - B) / delta) % 6;
break;
case G:
H = ((B - R) / delta) + 2;
break;
case B:
H = ((R - G) / delta) + 4;
break;
}
H *= 60;
}
// Convert negative angles from Hue
while (H < 0) {
H += 360;
}
return {
H: H,
S: S,
L: L
};
};
tl;dr
Define everything, before calculations
Switch is bad for performance
Loops are bad
Use Closure Compiler for automated optimizations
MEMOIZE! (this one is not available in the benchmark because it uses only one color at the time)
Compare pairs in Math.max and Math.min (if-else works better for bigger numbers as far as I can see)
Benchmark
The benchmark is quite basic; I'm generating a random RGB color every time and use it for the test suit.
The same color for all implementations of a converter.
I'm currently using a fast computer, so your numbers may differ.
At this point it is hard to optimize further, because performance differs, depending on the data.
Optimisations
Define the object for the result value at the very beginning. Allocating memory for the objects upfront somehow improves the performance.
var res = {
H: 0,
S: 0,
L: L
}
// ...
return res;
Not using switch will yield easy performance improvement.
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
if (Cmax === R) {
H = ((G - B) / delta) % 6;
} else if (Cmax === G) {
H = ((B - R) / delta) + 2;
} else if (Cmax === B) {
H = ((R - G) / delta) + 4;
}
H *= 60;
}
While loop is easily removable by:
if (H < 0) {
remainder = H % 360;
if (remainder !== 0) {
H = remainder + 360;
}
}
I have also applied Closure Compiler to remove redundant operations inside of the code.
You should memoize the results!
Consider refactoring the function to use three arguments so it's possible to have a multi-dimensional hash-map for memozing cache. Alternatively you can try using WeakMap, but the performance of this solution is unknown.
See the article Faster JavaScript Memoization For Improved Application Performance
Results
Node.js
The best one is rgbToHslOptimizedClosure.
node -v
v6.5.0
rgbToHsl x 16,468,872 ops/sec ±1.64% (85 runs sampled)
rgbToHsl_ x 15,795,460 ops/sec ±1.28% (84 runs sampled)
rgbToHslIfElse x 16,091,606 ops/sec ±1.41% (85 runs sampled)
rgbToHslOptimized x 22,147,449 ops/sec ±1.96% (81 runs sampled)
rgbToHslOptimizedClosure x 46,493,753 ops/sec ±1.55% (85 runs sampled)
rgbToHslOptimizedIfElse x 21,825,646 ops/sec ±2.93% (85 runs sampled)
rgbToHslOptimizedClosureIfElse x 38,346,283 ops/sec ±9.02% (73 runs sampled)
rgbToHslOptimizedIfElseConstant x 30,461,643 ops/sec ±2.68% (81 runs sampled)
rgbToHslOptimizedIfElseConstantClosure x 40,625,530 ops/sec ±2.70% (73 runs sampled)
Fastest is rgbToHslOptimizedClosure
Slowest is rgbToHsl_
Browser
Chrome Version 55.0.2883.95 (64-bit)
rgbToHsl x 18,456,955 ops/sec ±0.78% (62 runs sampled)
rgbToHsl_ x 16,629,042 ops/sec ±2.34% (63 runs sampled)
rgbToHslIfElse x 17,177,059 ops/sec ±3.85% (59 runs sampled)
rgbToHslOptimized x 27,552,325 ops/sec ±0.95% (62 runs sampled)
rgbToHslOptimizedClosure x 47,659,771 ops/sec ±3.24% (47 runs sampled)
rgbToHslOptimizedIfElse x 26,033,751 ops/sec ±2.63% (61 runs sampled)
rgbToHslOptimizedClosureIfElse x 43,430,875 ops/sec ±3.55% (59 runs sampled)
rgbToHslOptimizedIfElseConstant x 33,696,558 ops/sec ±3.97% (58 runs sampled)
rgbToHslOptimizedIfElseConstantClosure x 44,529,209 ops/sec ±3.56% (60 runs sampled)
Fastest is rgbToHslOptimizedClosure
Slowest is rgbToHsl_
Run the benchmark yourself
Note, that browser will freeze for a moment.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var RGB = { R: getRandomInt(0, 255), G: getRandomInt(0, 255), B: getRandomInt(0, 255) }
// http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
/**
* 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 ];
}
function rgbToHsl(rgb) {
var R = rgb.R / 255;
var G = rgb.G / 255;
var B = rgb.B / 255;
var Cmax = Math.max(R, G, B);
var Cmin = Math.min(R, G, B);
var delta = Cmax - Cmin;
var L = (Cmax + Cmin) / 2;
var S = 0;
var H = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
switch (Cmax) {
case R:
H = ((G - B) / delta) % 6;
break;
case G:
H = ((B - R) / delta) + 2;
break;
case B:
H = ((R - G) / delta) + 4;
break;
}
H *= 60;
}
// Convert negative angles from Hue
while (H < 0) {
H += 360;
}
return {
H: H,
S: S,
L: L
};
};
function rgbToHslIfElse(rgb) {
var R = rgb.R / 255;
var G = rgb.G / 255;
var B = rgb.B / 255;
var Cmax = Math.max(R, G, B);
var Cmin = Math.min(R, G, B);
var delta = Cmax - Cmin;
var L = (Cmax + Cmin) / 2;
var S = 0;
var H = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
if (Cmax === R) {
H = ((G - B) / delta) % 6;
} else if (Cmax === G) {
H = ((B - R) / delta) + 2;
} else if (Cmax === B) {
H = ((R - G) / delta) + 4;
}
H *= 60;
}
// Convert negative angles from Hue
while (H < 0) {
H += 360;
}
return {
H: H,
S: S,
L: L
};
};
function rgbToHslOptimized(rgb) {
var R = rgb.R / 255;
var G = rgb.G / 255;
var B = rgb.B / 255;
var Cmax = Math.max(Math.max(R, G), B);
var Cmin = Math.min(Math.min(R, G), B);
var delta = Cmax - Cmin;
var S = 0;
var H = 0;
var L = (Cmax + Cmin) / 2;
var res = {
H: 0,
S: 0,
L: L
}
var remainder = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
switch (Cmax) {
case R:
H = ((G - B) / delta) % 6;
break;
case G:
H = ((B - R) / delta) + 2;
break;
case B:
H = ((R - G) / delta) + 4;
break;
}
H *= 60;
}
if (H < 0) {
remainder = H % 360;
if (remainder !== 0) {
H = remainder + 360;
}
}
res.H = H;
res.S = S;
return res;
}
function rgbToHslOptimizedIfElse(rgb) {
var R = rgb.R / 255;
var G = rgb.G / 255;
var B = rgb.B / 255;
var Cmax = Math.max(Math.max(R, G), B);
var Cmin = Math.min(Math.min(R, G), B);
var delta = Cmax - Cmin;
var S = 0;
var H = 0;
var L = (Cmax + Cmin) / 2;
var res = {
H: 0,
S: 0,
L: L
}
var remainder = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
if (Cmax === R) {
H = ((G - B) / delta) % 6;
} else if (Cmax === G) {
H = ((B - R) / delta) + 2;
} else if (Cmax === B) {
H = ((R - G) / delta) + 4;
}
H *= 60;
}
if (H < 0) {
remainder = H % 360;
if (remainder !== 0) {
H = remainder + 360;
}
}
res.H = H;
res.S = S;
return res;
}
function rgbToHslOptimizedIfElseConstant(rgb) {
var R = rgb.R * 0.00392156862745;
var G = rgb.G * 0.00392156862745;
var B = rgb.B * 0.00392156862745;
var Cmax = Math.max(Math.max(R, G), B);
var Cmin = Math.min(Math.min(R, G), B);
var delta = Cmax - Cmin;
var S = 0;
var H = 0;
var L = (Cmax + Cmin) * 0.5;
var res = {
H: 0,
S: 0,
L: L
}
var remainder = 0;
if (delta !== 0) {
S = delta / (1 - Math.abs((2 * L) - 1));
if (Cmax === R) {
H = ((G - B) / delta) % 6;
} else if (Cmax === G) {
H = ((B - R) / delta) + 2;
} else if (Cmax === B) {
H = ((R - G) / delta) + 4;
}
H *= 60;
}
if (H < 0) {
remainder = H % 360;
if (remainder !== 0) {
H = remainder + 360;
}
}
res.H = H;
res.S = S;
return res;
}
function rgbToHslOptimizedIfElseConstantClosure(c) {
var a = .00392156862745 * c.h, e = .00392156862745 * c.f, f = .00392156862745 * c.c, g = Math.max(Math.max(a, e), f), d = Math.min(Math.min(a, e), f), h = g - d, b = c = 0, k = (g + d) / 2, d = {
a: 0,
b: 0,
g: k
};
0 !== h && (c = h / (1 - Math.abs(2 * k - 1)), g === a ? b = (e - f) / h % 6 : g === e ? b = (f - a) / h + 2 : g === f && (b = (a - e) / h + 4), b *= 60);
0 > b && (a = b % 360, 0 !== a && (b = a + 360));
d.a = b;
d.b = c;
return d;
};
function rgbToHslOptimizedClosure(c) {
var a = c.f / 255, e = c.b / 255, f = c.a / 255, k = Math.max(Math.max(a, e), f), d = Math.min(Math.min(a, e), f), g = k - d, b = c = 0, l = (k + d) / 2, d = {
c: 0,
g: 0,
h: l
};
if (0 !== g) {
c = g / (1 - Math.abs(2 * l - 1));
switch (k) {
case a:
b = (e - f) / g % 6;
break;
case e:
b = (f - a) / g + 2;
break;
case f:
b = (a - e) / g + 4;
}
b *= 60;
}
0 > b && (a = b % 360, 0 !== a && (b = a + 360));
d.c = b;
d.g = c;
return d;
}
function rgbToHslOptimizedClosureIfElse(c) {
var a = c.f / 255, e = c.b / 255, f = c.a / 255, g = Math.max(Math.max(a, e), f), d = Math.min(Math.min(a, e), f), h = g - d, b = c = 0, l = (g + d) / 2, d = {
c: 0,
g: 0,
h: l
};
0 !== h && (c = h / (1 - Math.abs(2 * l - 1)), g === a ? b = (e - f) / h % 6 : g === e ? b = (f - a) / h + 2 : g === f && (b = (a - e) / h + 4), b *= 60);
0 > b && (a = b % 360, 0 !== a && (b = a + 360));
d.c = b;
d.g = c;
return d;
}
new Benchmark.Suite()
.add('rgbToHsl', function () {
rgbToHsl(RGB);
})
.add('rgbToHsl_', function () {
rgbToHsl_(RGB.R, RGB.G, RGB.B);
})
.add('rgbToHslIfElse', function () {
rgbToHslIfElse(RGB);
})
.add('rgbToHslOptimized', function () {
rgbToHslOptimized(RGB);
})
.add('rgbToHslOptimizedClosure', function () {
rgbToHslOptimizedClosure(RGB);
})
.add('rgbToHslOptimizedIfElse', function () {
rgbToHslOptimizedIfElse(RGB);
})
.add('rgbToHslOptimizedClosureIfElse', function () {
rgbToHslOptimizedClosureIfElse(RGB);
})
.add('rgbToHslOptimizedIfElseConstant', function () {
rgbToHslOptimizedIfElseConstant(RGB);
})
.add('rgbToHslOptimizedIfElseConstantClosure', function () {
rgbToHslOptimizedIfElseConstantClosure(RGB);
})
// add listeners
.on('cycle', function (event) {
console.log(String(event.target));
})
.on('complete', function () {
console.log('Fastest is ' + this.filter('fastest').map('name'));
console.log('Slowest is ' + this.filter('slowest').map('name'));
})
// run async
.run({ 'async': false });
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdn.rawgit.com/bestiejs/benchmark.js/master/benchmark.js"></script>
Avoid the divisions by all means. You can probably eliminate a few by rescaling the relevant variables and constants.
You can also avoid divisions by using a lookup-table of inverses.
I don't think that the switch case is very efficient. I would advise to replace the max/min/switch by a single discussion using a triply nested if where you compare the RGB components, ending in the 6 possible orderings and applying ad-hoc processing to each.
Use a rough approximation instead:
third = Math.PI * 2 / 3;
ctx.fillStyle = 'rgb('+ [
127 + 127 * Math.cos(time - third),
127 + 127 * Math.cos(time),
127 + 127 * Math.cos(time + third)
] +')';
Based on:
http://www.p01.org/artjs_at_ffconf/
http://www.p01.org/artjs_at_ffconf/talk.html
I want to set contrast, saturation and hue in my image editor. for this i use fabric.js but it have only brightness option..
Here is the my fabric js code
(function() {
fabric.Object.prototype.transparentCorners = false;
var $ = function(id){return document.getElementById(id)};
console.log($);
function applyFilter(index, filter) {
console.log(filter);
var obj = canvas.getActiveObject();
obj.filters[index] = filter;
obj.applyFilters(canvas.renderAll.bind(canvas));
}
function applyFilterValue(index, prop, value) {
var obj = canvas.getActiveObject();
if (obj.filters[index]) {
obj.filters[index][prop] = value;
obj.applyFilters(canvas.renderAll.bind(canvas));
}
}
fabric.Object.prototype.padding = 5;
fabric.Object.prototype.transparentCorners = false;
var canvas = this.__canvas = new fabric.Canvas('c'),
f = fabric.Image.filters;
fabric.Image.fromURL('../lib/bg.png', function(img) {
canvas.backgroundImage = img;
canvas.backgroundImage.width = 400;
canvas.backgroundImage.height = 400;
});
canvas.on({
'object:selected': function() {
fabric.util.toArray(document.getElementsByTagName('input'))
.forEach(function(el){ el.disabled = false; })
var filters = ['brightness',];
// var filters = ['grayscale', 'invert', 'remove-white', 'sepia', 'sepia2',
// 'brightness', 'noise', 'gradient-transparency', 'pixelate',
// 'blur', 'sharpen', 'emboss', 'tint', 'multiply', 'blend'];
for (var i = 0; i < filters.length; i++) {
$(filters[i]).checked = !!canvas.getActiveObject().filters[i];
}
applyFilter(5, true && new f.Brightness({
brightness: parseInt($('brightness-value').value, 10)
}));
},
'selection:cleared': function() {
fabric.util.toArray(document.getElementsByTagName('input'))
.forEach(function(el){ el.disabled = true; })
}
});
fabric.Image.fromURL('../upload/Chrysanthemum.jpg', function(img) {
var oImg = img.set({ left: 50, top: 100, angle: 0 }).scale(0.9);
canvas.add(oImg).renderAll();
canvas.setActiveObject(oImg);
});
$('brightness').onclick = function () {
applyFilter(5, this.checked && new f.Brightness({
brightness: parseInt($('brightness-value').value, 10)
}));
};
$('brightness-value').onchange = function() {
applyFilterValue(5, 'brightness', parseInt(this.value, 10));
};
})();
Last year I wrote a HSL plugin which might be of use:
fabric.Image.filters.HSL = fabric.util.createClass(fabric.Image.filters.BaseFilter, {
type: 'HSL',
initialize: function(options) {
options || (options = {});
this.hue = options.hue || 0;
this.saturation = options.saturation || 0;
this.lightness = options.lightness || 0;
},
rgbToHsl: function(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;
} 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];
},
hslToRgb: function(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l;
} 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];
},
applyTo: function(canvasEl) {
var context = canvasEl.getContext('2d'),
imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
data = imageData.data;
for (var i=0; i<data.length; i+=4)
{
// Convert RGB to HSL
var hsl = this.rgbToHsl(data[i], data[i+1], data[i+2]);
// Apply HSL values
if (this.hue ) hsl[0] = this.hue;
if (this.saturation) hsl[1] = this.saturation;
if (this.lightness ) hsl[2] = this.lightness;
// Convert HSL back to RGB
var rgb = this.hslToRgb(hsl[0], hsl[1], hsl[2]);
// Update data
data[i] = rgb[0];
data[i+1] = rgb[1];
data[i+2] = rgb[2];
}
context.putImageData(imageData, 0, 0);
},
toObject: function() {
return extend(this.callSuper('toObject'), {
hue: this.hue,
saturation: this.saturation,
lightness: this.lightness
});
}
});
Add to your page after FabricJS has loaded and call like this:
var hue = 1; // Range: 0 to 1
var brightness = 1; // Range: 0 to 1
var lightness = 1; // Range: 0 to 1
var filterHSL = new img.filters.HSL({
hue: hue,
saturation: saturation,
lightness: lightness
});
img.filters = [filterHSL];
img.applyFilters(canvas.renderAll.bind(canvas));
...where canvas is your FabricJS canvas object.
I hope this helps.
You can now do this with the latest Fabricjs release, 1.6.6
fabric.Image.filters.Contrast and fabric.Image.filters.Saturate were added in this release.
I made a fiddle to show you how to achieve it (note: change the applyFilter() function to achieve different filters):
DEMO
My code is giving me this error:
Uncaught TypeError: Cannot read property 'left' of undefined
Code:
function responsiveSlider() {
jQuery(".slider-bloc").each(function() {
var a = jQuery(this),
b = this;
if (1 != a.data("is_slider")) {
a.data("is_slider", 1);
var c = a.find(".nav-slider .item-nav"),
d = jQuery(a.find(".content-rel-slider")[0]),
e = jQuery(a.find(".content-abs-slider")[0]);
a.find(".item-slider").each(function(a) {
jQuery(this).attr("data-inc", a + 1)
}), a.find(".item-slider").clone().appendTo(e), a.find(".item-slider").clone().appendTo(e);
var f = a.find(".item-slider"),
g = a.hasClass("full-slider"),
h = 0,
i = function(a) {
e.css({
width: "100%"
}), g && f.css({
width: d.width()
}), e.css({
width: jQuery(f[0]).width() * f.length
});
Math.ceil(d.width() / jQuery(f[0]).width());
f.length * jQuery(f[0]).width() < d.width() ? e.css({
left: 0
}) : 0 == a && e.css({
left: -jQuery(f[0]).width()
})
},
j = function(b) {
var c = Math.ceil(d.width() / jQuery(f[0]).width());
durationanim = 2 >= c ? 300 : 800;
var g = jQuery(f[0]).width();
if (b + c - 1 >= f.length ? b = 0 : 0 > b && (b = f.length - c), f[b]) {
if (f.length * g < d.width()) return;
if (a.hasClass("moving")) return;
a.addClass("moving"), a.find(".item-slider").removeClass("current"), jQuery(a.find(".item-slider")[1]).addClass("current");
var i = "next",
j = 1,
k = e.position().left,
l = -g * c - g;
if (1 > b) {
i = "prev";
for (var m = 0; c > m; m++) jQuery(a.find(".item-slider")[f.length - 1]).prependTo(e);
k -= g * c, l = -g
} else j += c;
f = a.find(".item-slider"), h = 1;
var o = e.find(".item-slider.current").index();
if (!isMobile && c > 1) {
"next" == i ? (initforfirstslide = o, lastforfirstslide = o + c - 1, initforsecondslide = o + c, lastforsecondslide = o + 2 * c - 1) : (initforfirstslide = o - c, lastforfirstslide = o - 1, initforsecondslide = o, lastforsecondslide = o + c - 1);
for (var m = initforfirstslide; m <= lastforfirstslide; m++) {
var p = durationanim / 2,
q = durationanim / 2;
"next" == i ? movexfirstslide = -jQuery(f[m]).width() * (c - (m - o + 1)) / 4 : movexfirstslide = jQuery(f[m]).width() * (m - o + 1) / 4;
var r = new TimelineLite({
paused: !0
});
r.fromTo(jQuery(f[m]), p / 1e3, {
x: 0,
force3D: !0,
ease: Cubic.easeInOut
}, {
x: movexfirstslide,
force3D: !0,
ease: Cubic.easeInOut
}), r.to(jQuery(f[m]), q / 1e3, {
x: 0,
force3D: !0,
ease: Cubic.easeInOut
}), r.play()
}
for (var m = initforsecondslide; m <= lastforsecondslide; m++) {
var p = durationanim / 2,
q = durationanim / 2;
"next" == i ? movexsecondslide = jQuery(f[m]).width() * (m - o - c + 1) / 4 : movexsecondslide = jQuery(f[m]).width() * (m - o + 1) / 4;
var r = new TimelineLite({
paused: !0
});
r.fromTo(jQuery(f[m]), p / 1e3, {
x: 0,
force3D: !0,
ease: Cubic.easeInOut
}, {
x: movexsecondslide,
force3D: !0,
ease: Cubic.easeInOut
}), r.to(jQuery(f[m]), q / 1e3, {
x: 0,
force3D: !0,
ease: Cubic.easeInOut
}), r.play()
}
}
if (TweenMax.fromTo(e, durationanim / 1e3, {
left: k,
force3D: !0,
ease: n
}, {
left: l,
force3D: !0,
ease: n,
onComplete: function() {
if ("next" == i) {
h--;
for (var b = 0; c > b; b++) jQuery(a.find(".item-slider")[0]).appendTo(e);
e.css({
left: -g
})
}
f.removeClass("current"), jQuery(f[1]).addClass("current"), f = a.find(".item-slider"), a.removeClass("moving")
}
}), a.find(".nav-bullet").length > 0) {
var s = a.find(".nav-bullet .bullet").length - 1,
t = jQuery(a.find(".item-slider")[j]).attr("data-inc"),
u = t * s / (f.length / 4);.5 == u && (u = .4), currentbullet = Math.round(u), a.find(".nav-bullet .bullet").removeClass("current"), jQuery(a.find(".nav-bullet .bullet")[currentbullet]).addClass("current")
}
}
};
c.each(function() {
var a = jQuery(this);
a.on({
click: function() {
var b = a.hasClass("prev-button") ? 0 : 2;
return j(b), !1
}
})
}), _touchstart = !1, swipedirection = !1, _lastcoord = 0, coord = 0, initmoveposition = 0, touchmove = !1;
var k = new Hammer(b),
l = e.position().left,
m = l,
n = Cubic.easeInOut;
k.on("panstart", function(b) {
a.hasClass("moving") || (n = Strong.easeOut, l = e.position().left, m = l)
}), k.on("panend", function(b) {
a.hasClass("moving") || (2 == b.direction && j(1), 4 == b.direction && j(0), 1 == b.direction && TweenMax.to(e, .6, {
left: m,
force3D: !0,
ease: Strong.easeOut,
throwProps: !0
}))
}), k.on("panleft panright", function(b) {
a.hasClass("moving") || (0 == l && (l = e.position().left), TweenMax.to(e, .6, {
left: l + b.deltaX,
force3D: !0,
ease: Strong.easeOut,
throwProps: !0
}))
}), jQuery(window).on("resize", function() {
i(!1)
}), jQuery(a.find(".item-slider")[f.length - 1]).appendTo(e), e.css({
left: -jQuery(f[0]).width() * (h + 1)
}), f = a.find(".item-slider"), jQuery(f[1]).addClass("current"), h++, i(!0)
}
})}
This is the code of my responsive slider but sometimes it gave me the error
l = e.position().left, on this line, i tried to remove this line but then slider stops working, i don't know why sometimes it doesn't gave me the error please explain this to me will be very thankful to you.
Just place this line :
var e = jQuery(a.find(".content-abs-slider")[0]);
after or within
j = function(b) {
I've been asked to create the same ripple effect as the one found with the bit.ly 404 page: http://bit.ly/khgefiyueagf734
Javascript and canvas are pretty new to me and I am having this problem. When I change the image to one that I want. Not the fish one that is on the 404 page it renders the image as the same size as the fish and wont change.
Can you tell me how to change the size of the ship (bit.ly fish) image?
Here is the JS Fiddle: http://jsfiddle.net/UzpAw/8/
and Here is the javascript that comes with it:
(function() {
function F() {
if (g.getContext) {
var a, b = a = 0;
if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) a = document.documentElement.clientWidth, b = document.documentElement.clientHeight;
if (document.body && (document.body.clientWidth || document.body.clientHeight)) a = Math.max(a, document.body.clientWidth), b = Math.max(b, document.body.clientHeight);
a = [a, b];
e = a[1];
d = a[0] + 1;
r = Math.floor(0.5 * e);
i = e - r;
j = i - G;
g.setAttribute("width", d);
g.setAttribute("height",
e);
g.style.width = d + "px";
g.style.height = e + "px";
s = [Math.floor(0.75 * d), 0];
c = g.getContext("2d");
c.fillStyle = "#00022f"; //rgba(104,168,220,.8)
v()
}
}
function v() {
c.fillRect(0, i, d, e - j)
}
function m(a, b) {
if (!w) k.style.top = "-9999px", k.style.left = "-9999px",k.style.height = "20px", c.clearRect(0, j, d, e - j);
c.clearRect(s[0], s[1] - 5, n[0], n[1]);
a = a || Math.floor(0.6 * d);
b = b || 0;
b += r - 0.5 * n[1];
c.drawImage(k, a, b, n[0], n[1]);
s = [a, b];
w || (v(), w = !0)
}
function O() {
midPointY = Math.sin(h * 10 * P) * (o - h) * Q;
h <= o && (f[p] = [0, midPointY], h % 9 == 0 && h % 2 == 1 && (f[z] = [-1, midPointY], z++));
c.clearRect(0, j, d, e - j);
m(Math.floor(0.6 * d), h < o ? Math.floor(midPointY) : 0);
c.beginPath();
c.moveTo(0, i);
for (var a = [0, i], b = 0; b < t; b++) if (f[b]) {
b < p && (f[b][0] = f[b][0] * R - H, f[p - b + p] = [-f[b][0], f[b][1]]);
var q = f[b][0] + I,
A = f[b][1] + i;
c.bezierCurveTo((q - a[0]) / 2 + a[0], a[1], q - (q - a[0]) / 2, A, q, A);
a[0] = q;
a[1] = A
}
c.lineTo(d, i);
c.lineTo(d, e);
c.lineTo(0, e);
c.closePath();
c.fill();
h++;
h == o && (B = !1);
h >= o * 2 && clearInterval(C)
}
function D(a, b) {
if (!J) return !1;
b && c.clearRect(x, y, b.w, b.h);
a = a || E[0];
x = K[0];
y = K[1] + a.mt;
c.drawImage(l, Math.abs(a.l),
Math.abs(a.t), a.w, a.h, x, y, a.w, a.h)
}
function S() {
var a = -1,
b = 0,
c = E.length,
d;
clearInterval(L);
L = setInterval(function() {
b = ++a > c - 1 ? c * 2 - 2 - a : a;
d = E[b];
D(d, M);
M = d;
a >= c * 2 - 2 && (a = 0)
}, 200)
}
var u = function(a, b, c) {
var d = c,
c = function(b) {
d.call(a, b)
};
return a.attachEvent ? a.attachEvent("on" + b, c) : a.addEventListener(b, c, !1)
};
if (!window.getComputedStyle) window.getComputedStyle = function(a) {
return a.currentStyle
};
var t = 13,
Q = 0.3,
d = 960,
e = 600,
G = 30,
r = Math.floor(0.5 * e),
R = 1.01,
H = 20,
i = e - r,
j = i - G,
o = (t - 3) * 9,
p = Math.floor(t / 2),
I, g,
c, C, B = !1,
P = Math.PI / 180,
f, z, h, k, s = [Math.floor(0.75 * d), 0],
n = [236, 195],
w, l, L, J, M, K = [10, 10], // 75, 150
E = [{
h: 58,
w: 140,
t: 0,
l: 0,
mt: 10
}, {
h: 72,
w: 150,
t: -64,
l: 0,
mt: 0
}, {
h: 61,
w: 150,
t: -151,
l: 0,
mt: 30
}],
N = !1;
g = document.createElement("canvas");
g.getContext && (N = !0, document.body.removeChild(document.getElementById("ripple")), g.setAttribute("id", "ripple"), document.body.appendChild(g), k = document.getElementById("ship"));
//, l = document.getElementById("gull"), l.setAttribute("src", "/static/graphics/gulls-404.png")
N && (function() {
try {
if (fish.complete) m();
else if (parseInt(15, 5)) setTimeout(m, 1E3); // 15 IS: getComputedStyle(k).height
else throw "no ship";
} catch (a) {
u(k, "load", function() {
m()
})
}
u(document.getElementById("ripple-control"), "mouseover", function(a) {
if (g.getContext && !B) clearInterval(C), p = Math.floor(t / 2), I = a.pageX, z = 1, h = 0, f = [], f[0] = [H, 0], C = setInterval(O, 30), B = !0
});
u(window, "resize", function() {
F();
m();
v();
D()
})
}(), F());
(function() {
var a = 0,
b = document.getElementById("cloud1"),
c = document.getElementById("cloud2"),
e = parseInt(getComputedStyle(b).left, 10),
f = parseInt(getComputedStyle(c).left, 10);
setInterval(function() {
if (++a == 2) a = 0, e += 1, b.style.left = e + "px";
f += 1;
c.style.left = f + "px";
e > d + 50 && (e = -200);
f > d + 50 && (f = -100)
}, 50)
})()
})();
You have to hover over the blue to get the image to show but as you can see, the size of the ship is way too large. I need it to be: 183x116.
Thanks for the help :)
The size of the ship is controlled by the variable on line#110.
Does anybody know, off the top of your heads, a Javascript solution for calculating the complementary colour of a hex value?
There are a number of colour picking suites and palette generators on the web but I haven't seen any that actually calculate the colour dynamically using JS.
A detailed hint or a snippet would be very much appreciated.
Parsed through http://design.geckotribe.com/colorwheel/
// Complement
temprgb={ r: 0, g: 0xff, b: 0xff }; // Cyan
temphsv=RGB2HSV(temprgb);
temphsv.hue=HueShift(temphsv.hue,180.0);
temprgb=HSV2RGB(temphsv);
console.log(temprgb); // Complement is red (0xff, 0, 0)
function RGB2HSV(rgb) {
hsv = new Object();
max=max3(rgb.r,rgb.g,rgb.b);
dif=max-min3(rgb.r,rgb.g,rgb.b);
hsv.saturation=(max==0.0)?0:(100*dif/max);
if (hsv.saturation==0) hsv.hue=0;
else if (rgb.r==max) hsv.hue=60.0*(rgb.g-rgb.b)/dif;
else if (rgb.g==max) hsv.hue=120.0+60.0*(rgb.b-rgb.r)/dif;
else if (rgb.b==max) hsv.hue=240.0+60.0*(rgb.r-rgb.g)/dif;
if (hsv.hue<0.0) hsv.hue+=360.0;
hsv.value=Math.round(max*100/255);
hsv.hue=Math.round(hsv.hue);
hsv.saturation=Math.round(hsv.saturation);
return hsv;
}
// RGB2HSV and HSV2RGB are based on Color Match Remix [http://color.twysted.net/]
// which is based on or copied from ColorMatch 5K [http://colormatch.dk/]
function HSV2RGB(hsv) {
var rgb=new Object();
if (hsv.saturation==0) {
rgb.r=rgb.g=rgb.b=Math.round(hsv.value*2.55);
} else {
hsv.hue/=60;
hsv.saturation/=100;
hsv.value/=100;
i=Math.floor(hsv.hue);
f=hsv.hue-i;
p=hsv.value*(1-hsv.saturation);
q=hsv.value*(1-hsv.saturation*f);
t=hsv.value*(1-hsv.saturation*(1-f));
switch(i) {
case 0: rgb.r=hsv.value; rgb.g=t; rgb.b=p; break;
case 1: rgb.r=q; rgb.g=hsv.value; rgb.b=p; break;
case 2: rgb.r=p; rgb.g=hsv.value; rgb.b=t; break;
case 3: rgb.r=p; rgb.g=q; rgb.b=hsv.value; break;
case 4: rgb.r=t; rgb.g=p; rgb.b=hsv.value; break;
default: rgb.r=hsv.value; rgb.g=p; rgb.b=q;
}
rgb.r=Math.round(rgb.r*255);
rgb.g=Math.round(rgb.g*255);
rgb.b=Math.round(rgb.b*255);
}
return rgb;
}
//Adding HueShift via Jacob (see comments)
function HueShift(h,s) {
h+=s; while (h>=360.0) h-=360.0; while (h<0.0) h+=360.0; return h;
}
//min max via Hairgami_Master (see comments)
function min3(a,b,c) {
return (a<b)?((a<c)?a:c):((b<c)?b:c);
}
function max3(a,b,c) {
return (a>b)?((a>c)?a:c):((b>c)?b:c);
}
I find that taking the bit-wise complement works well, and quickly.
var color = 0x320ae3;
var complement = 0xffffff ^ color;
I'm not sure if it's a perfect complement in the sense of "mixes together to form a 70% grey", however a 70% grey is "pure white" in terms of color timing in film. It occurred to me that XORing the RGB hex out of pure white might be a good first approximation. You could also try a darker grey to see how that works for you.
Again, this is a fast approximation and I make no guarantees that it'll be perfectly accurate.
See https://github.com/alfl/textful/blob/master/app.js#L38 for my implementation.
None of the other functions here worked out the box, so I made this one.
It takes a hex value, converts it to HSL, shifts the hue 180 degrees and converts back to Hex
/* hexToComplimentary : Converts hex value to HSL, shifts
* hue by 180 degrees and then converts hex, giving complimentary color
* as a hex value
* #param [String] hex : hex value
* #return [String] : complimentary color as hex value
*/
function hexToComplimentary(hex){
// Convert hex to rgb
// Credit to Denis http://stackoverflow.com/a/36253499/4939630
var rgb = 'rgb(' + (hex = hex.replace('#', '')).match(new RegExp('(.{' + hex.length/3 + '})', 'g')).map(function(l) { return parseInt(hex.length%2 ? l+l : l, 16); }).join(',') + ')';
// Get array of RGB values
rgb = rgb.replace(/[^\d,]/g, '').split(',');
var r = rgb[0], g = rgb[1], b = rgb[2];
// Convert RGB to HSL
// Adapted from answer by 0x000f http://stackoverflow.com/a/34946092/4939630
r /= 255.0;
g /= 255.0;
b /= 255.0;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var h, s, l = (max + min) / 2.0;
if(max == min) {
h = s = 0; //achromatic
} else {
var d = max - min;
s = (l > 0.5 ? d / (2.0 - max - min) : d / (max + min));
if(max == r && g >= b) {
h = 1.0472 * (g - b) / d ;
} else if(max == r && g < b) {
h = 1.0472 * (g - b) / d + 6.2832;
} else if(max == g) {
h = 1.0472 * (b - r) / d + 2.0944;
} else if(max == b) {
h = 1.0472 * (r - g) / d + 4.1888;
}
}
h = h / 6.2832 * 360.0 + 0;
// Shift hue to opposite side of wheel and convert to [0-1] value
h+= 180;
if (h > 360) { h -= 360; }
h /= 360;
// Convert h s and l values into r g and b values
// Adapted from answer by Mohsen http://stackoverflow.com/a/9493060/4939630
if(s === 0){
r = g = b = l; // achromatic
} else {
var hue2rgb = 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);
}
r = Math.round(r * 255);
g = Math.round(g * 255);
b = Math.round(b * 255);
// Convert r b and g values to hex
rgb = b | (g << 8) | (r << 16);
return "#" + (0x1000000 | rgb).toString(16).substring(1);
}
Rather than reinventing the wheel, I found a library to work with colors.
Tiny Color
This is how you would implement some of the other answers using it.
color1 = tinycolor2('#f00').spin(180).toHexString(); // Hue Shift
color2 = tinycolor2("#f00").complement().toHexString(); // bitwise
Hex and RGB complementry
This is most correct and efficient way to get complementary hex color value
function complementryHexColor(hex){
let r = hex.length == 4 ? parseInt(hex[1] + hex[1], 16) : parseInt(hex.slice(1, 3), 16);
let g = hex.length == 4 ? parseInt(hex[2] + hex[2], 16) : parseInt(hex.slice(3, 5), 16);
let b = hex.length == 4 ? parseInt(hex[3] + hex[3], 16) : parseInt(hex.slice(5), 16);
[r, g, b] = complementryRGBColor(r, g, b);
return '#' + (r < 16 ? '0' + r.toString(16) : r.toString(16)) + (g < 16 ? '0' + g.toString(16) : g.toString(16)) + (b < 16 ? '0' + b.toString(16) : b.toString(16));
}
function complementryRGBColor(r, g, b) {
if (Math.max(r, g, b) == Math.min(r, g, b)) {
return [255 - r, 255 - g, 255 - b];
} else {
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;
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 = Math.round((h*60) + 180) % 360;
h /= 360;
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 [Math.round(r*255), Math.round(g*255), Math.round(b*255)];
}
}
RGB Complimentary
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
function rgbComplimentary(r,g,b){
var hex = "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
var rgb = 'rgb(' + (hex = hex.replace('#', '')).match(new RegExp('(.{' + hex.length/3 + '})', 'g')).map(function(l) { return parseInt(hex.length%2 ? l+l : l, 16); }).join(',') + ')';
// Get array of RGB values
rgb = rgb.replace(/[^\d,]/g, '').split(',');
var r = rgb[0]/255.0, g = rgb[1]/255.0, b = rgb[2]/255.0;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var h, s, l = (max + min) / 2.0;
if(max == min) {
h = s = 0; //achromatic
} else {
var d = max - min;
s = (l > 0.5 ? d / (2.0 - max - min) : d / (max + min));
if(max == r && g >= b) {
h = 1.0472 * (g - b) / d ;
} else if(max == r && g < b) {
h = 1.0472 * (g - b) / d + 6.2832;
} else if(max == g) {
h = 1.0472 * (b - r) / d + 2.0944;
} else if(max == b) {
h = 1.0472 * (r - g) / d + 4.1888;
}
}
h = h / 6.2832 * 360.0 + 0;
// Shift hue to opposite side of wheel and convert to [0-1] value
h+= 180;
if (h > 360) { h -= 360; }
h /= 360;
// Convert h s and l values into r g and b values
// Adapted from answer by Mohsen http://stackoverflow.com/a/9493060/4939630
if(s === 0){
r = g = b = l; // achromatic
} else {
var hue2rgb = 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);
}
r = Math.round(r * 255);
g = Math.round(g * 255);
b = Math.round(b * 255);
// Convert r b and g values to hex
rgb = b | (g << 8) | (r << 16);
return hexToRgb("#" + (0x1000000 | rgb).toString(16).substring(1));
}
console.log(rgbComplimentary(242, 211, 215));
HEX Complimentary
function hexComplimentary(hex){
var rgb = 'rgb(' + (hex = hex.replace('#', '')).match(new RegExp('(.{' + hex.length/3 + '})', 'g')).map(function(l) { return parseInt(hex.length%2 ? l+l : l, 16); }).join(',') + ')';
// Get array of RGB values
rgb = rgb.replace(/[^\d,]/g, '').split(',');
var r = rgb[0]/255.0, g = rgb[1]/255.0, b = rgb[2]/255.0;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var h, s, l = (max + min) / 2.0;
if(max == min) {
h = s = 0; //achromatic
} else {
var d = max - min;
s = (l > 0.5 ? d / (2.0 - max - min) : d / (max + min));
if(max == r && g >= b) {
h = 1.0472 * (g - b) / d ;
} else if(max == r && g < b) {
h = 1.0472 * (g - b) / d + 6.2832;
} else if(max == g) {
h = 1.0472 * (b - r) / d + 2.0944;
} else if(max == b) {
h = 1.0472 * (r - g) / d + 4.1888;
}
}
h = h / 6.2832 * 360.0 + 0;
// Shift hue to opposite side of wheel and convert to [0-1] value
h+= 180;
if (h > 360) { h -= 360; }
h /= 360;
if(s === 0){
r = g = b = l; // achromatic
} else {
var hue2rgb = 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);
}
r = Math.round(r * 255);
g = Math.round(g * 255);
b = Math.round(b * 255);
// Convert r b and g values to hex
rgb = b | (g << 8) | (r << 16);
return "#" + (0x1000000 | rgb).toString(16).substring(1);
}
console.log(hexComplimentary("#ff5a5a"));
Source: Updated https://stackoverflow.com/a/37657940/6569224 answer