Css3 transform touch input - javascript

I have a transform 3D on a map object, however, when I transform the whole map the touch input doesn't follow and it results in panning not functioning when rotating the map full 180 the controls are inverted.
Is there a way to tell CSS to invert or rotate the way touch inputs are read by the browser to allow for a normal panning even when the map is rotated.
I do know this isn't the preferred way of rotating a map, the library should have a function for it but in this case, it doesn't and the only solution is to rotate the whole div containing the map.
What I am wondering about is a way to do this either with CSS or to override angular in some way to modify the touch on a 360-degree variable.
The map changes rotation frequently, so it can't be a static solution.
Css used to rotate the map:
transform-origin: 50% 50%; transform: rotate({{deg}}deg); transition: 300ms ease-out;
Code behind that is:
$scope.degraw = Math.round(heading.magneticHeading);
var aR;
$scope.rot = $scope.rot || 0; // if rot undefined or 0, make 0, else rot
aR = $scope.rot % 360;
if ( aR < 0 ) { aR += 360; }
if ( aR < 180 && ($scope.degraw > (aR + 180)) ) { $scope.rot -= 360; }
if ( aR >= 180 && ($scope.degraw <= (aR - 180)) ) { $scope.rot += 360; }
$scope.rot += ($scope.degraw - aR);
if($scope.isInCompass == 1) {
$scope.deg = $scope.rot * -1;
$scope.northdeg = $scope.rot * -1;
}
The panning and all movement is controlled by the map library, however the code I've been able to get out of the library (Might not be the code at all but atleast this is what I'm looking at right now)
_touchMove: function(a) {
a.preventDefault();
this._updateTouch(a);
var b = this._touches,
c, d = a.changedTouches.length,
f, e, g, h;
if (!(l("android") && l("safari") && 1 === a.targetTouches.length && a.touches.length === a.targetTouches.length && a.targetTouches.length === a.changedTouches.length && 0 === a.changedTouches[0].identifier && b[a.changedTouches[0].identifier] && 1 < this._touchIds.length)) {
for (c = 0; c < d; c++)
if (f = a.changedTouches[c], e = b[f.identifier]) g = Math.abs(f.pageX -
e.startX), f = Math.abs(f.pageY - e.startY), !e.moved && (g >= this.tapRadius || f >= this.tapRadius) && (e.moved = e.absMoved = !0), h = h ? h : e.moved;
1 === this._numTouches ? (b = a.changedTouches[0], this._swipeActive ? this._fire("onSwipeMove", this._processTouchEvent(a, b)) : h && (this._swipeActive = !0, this._fire("onSwipeStart", this._processTouchEvent(a, b)))) : 2 === this._numTouches && (c = this._nodeTouches[0], d = this._nodeTouches[1], this._pinchActive ? this._fire("onPinchMove", this._processTouchEvent(a, [c, d])) : h && (h = b[c.identifier], e = b[d.identifier],
b = Math.abs(h.startX - e.startX), h = Math.abs(h.startY - e.startY), e = Math.abs(c.pageX - d.pageX), g = Math.abs(c.pageY - d.pageY), Math.abs(Math.sqrt(e * e + g * g) - Math.sqrt(b * b + h * h)) >= 2 * this.tapRadius && (this._pinchActive = !0, this._fire("onPinchStart", this._processTouchEvent(a, [c, d])))))
}
},

I cannot really understand that touch code, but the point is that you should transform the input to follow your rotation:
get a rotation matrix:
var radians = deg / 180 * Math.Pi;
var cos = Math.cos(radians), sin = Math.sin(radians);
var matrix = [cos, sin, -sin, cos];
get a rotated touch point:
var rotatedX = matrix[0] * p.x + matrix[2] * p.y;
var rotatedY = matrix[1] * p.x + matrix[3] * p.y;
At this point if you do not have a knowledge of how your input are handled you can do this:
var originalTouchMove = _touchMove;
_touchMove = function(a) {
// inspect a and find where clientX and clientY are.
// obtain rotatedX and rotatedY and overwrite them
originalTouchMove(a) // a has been mutated with rotated coords
}

Related

Raycasting walls arent drawed equally wide

I am working on a raycaster and followed this tutorial: https://dev.opera.com/articles/3d-games-with-canvas-and-raycasting-part-1/
My questions is about a Bug which draws / calculates Walls in diffrent Width depending on where on the canvas its drawn (so how big the ray angle is to the players point of direction (center view)):
The Walls in drawn in the middle (1.) are small, but those on the left or right (2.) side of the screen are drawn wider. Its easiest to understand when you look at the image. I think I just got the Math wrong, maybe rounded up somewhere I shouldnt but I havent found it yet or could think of any reason this error accures. Its made in a HTML Canvas using JavaScript.
In my first function I am sending out a ray for each x pixel of my canvas:
let resolution = Math.ceil(canvas.width / this.resolution); //canvas width = 1600, resolution = 1
let id = 0;
for (let x = 0; x < resolution; x++) {
let viewDist = (canvas.width / this.resolution) / Math.tan((this.fov / 2)); //fov 90 in rad
let rayx = (-resolution / 2 + x) * this.resolution;
let rayDist = Math.sqrt(rayx * rayx + viewDist * viewDist);
let rayAngle = Math.asin(rayx / rayDist);
let wall = this.castWall(this.pod * Math.PI / 180 + rayAngle);
this.drawWall(x, wall);
}
But I dont think theres anything wrong here. In the second function I am castinbg each ray and giving back the distance to the hit wall. My blocks / walls are 50 wide. My map is stored in and 2D number Array -> this.map.grid, this.map.width holds how many block there are in x direction, this.map.height holds the count in y direction.
castWall(angle) {
const PI2 = Math.PI * 2;
angle %= PI2;
if (angle < 0) {
angle += PI2;
}
let right = angle > PI2 * 0.75 || angle < PI2 * 0.25;
let up = angle < 0 || angle > Math.PI;
let sin = Math.sin(angle);
let cos = Math.cos(angle);
let dist = 0;
let textureX;
let texture;
let slope = sin / cos;
let dXVer = right ? 1 : -1;
let dYVer = dXVer * slope;
let px = this.x / 50;
let py = this.y / 50;
let x = right ? Math.ceil(px) : Math.floor(px);
let y = py + (x - px) * slope;
while (x >= 0 && x < this.map.width && y >= 0 && y < this.map.height) {
let wallX = Math.floor(x + (right ? 0 : -1));
let wallY = Math.floor(y);
if (this.map.grid[wallY][wallX] > 0) {
dist = Math.sqrt(Math.pow(x - px, 2) + Math.pow(y - py, 2));
texture = this.map.grid[wallY][wallX];
textureX = (y * 50) % 50;
if (right) {
textureX = 50 - textureX;
}
break;
}
x += dXVer;
y += dYVer;
}
slope = cos / sin;
let dYHor = up ? -1 : 1;
let dXHor = dYHor * slope;
y = up ? Math.floor(py) : Math.ceil(py);
x = px + (y - py) * slope;
while (x >= 0 && x < this.map.width && y >= 0 && y < this.map.height) {
let wallY = Math.floor(y + (up ? -1 : 0));
let wallX = Math.floor(x);
if (this.map.grid[wallY][wallX] > 0) {
let distHor = Math.sqrt(Math.pow(x - px, 2) + Math.pow(y - py, 2));
if (dist === 0 || distHor < dist) {
dist = distHor;
texture = this.map.grid[wallY][wallX];
textureX = (x * 50) % 50;
if (up) {
textureX = 50 - textureX;
}
}
break;
}
x += dXHor;
y += dYHor;
}
return {
distance: dist,
texture: texture,
textureX: textureX
};`
Ive also tried raycasting with other algorithms (Bresenham & DDA) but I never got them really to work. This is the only one which works for me. If you have any questions about the code feel free to ask.

How do I draw x number of circles around a central circle, starting at the top of the center circle?

I'm trying to create a UI that has a lot of items in circles. Sometimes these circles will have related circles that should be displayed around them.
I was able to cobble together something that works, here.
The problem is that the outer circles start near 0 degrees, and I'd like them to start at an angle supplied by the consumer of the function/library. I was never a star at trigonometry, or geometry, so I could use a little help.
As you can see in the consuming code, there is a setting: startingDegree: 270 that the function getPosition should honor, but I haven't been able to figure out how.
Update 04/02/2014:
as I mentioned in my comment to Salix alba, I wasn't clear above, but what I needed was to be able to specify the radius of the satellite circles, and to have them go only partly all the way around. Salix gave a solution that calculates the size the satellites need to be to fit around the center circle uniformly.
Using some of the hints in Salix's answer, I was able to achieve the desired result... and have an extra "mode," thanks to Salix, in the future.
The working, though still rough, solution is here: http://jsfiddle.net/RD4RZ/11/. Here is the entire code (just so it's all on SO):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.1.js"></script>
<style type="text/css">
.circle
{
position: absolute;
width: 100px;
height: 100px;
background-repeat: no-repeat;background-position: center center;
border: 80px solid #a19084;
border-radius: 50%;
-moz-border-radius: 50%;
}
.sm
{
border: 2px solid #a19084;
}
</style>
<script type="text/javascript">//<![CDATA[
$(function () {
function sind(x) {
return Math.sin(x * Math.PI / 180);
}
/*the law of cosines:
cc = aa + bb - 2ab cos(C), where c is the satellite diameter a and b are the legs
solving for cos C, cos C = ( aa + bb - cc ) / 2ab
Math.acos((a * a + b * b - c * c) / (2 * a * b)) = C
*/
function solveAngle(a, b, c) { // Returns angle C using law of cosines
var temp = (a * a + b * b - c * c) / (2 * a * b);
if (temp >= -1 && temp <= 1)
return radToDeg(Math.acos(temp));
else
throw "No solution";
}
function radToDeg(x) {
return x / Math.PI * 180;
}
function degToRad(x) {
return x * (Math.PI / 180);
}
var satellite = {
//settings must have: collection (array), itemDiameter (number), minCenterDiameter (number), center (json with x, y numbers)
//optional: itemPadding (number), evenDistribution (boolean), centerPadding (boolean), noOverLap (boolean)
getPosition: function (settings) {
//backwards compat
settings.centerPadding = settings.centerPadding || settings.itemPadding;
settings.noOverLap = typeof settings.noOverLap == 'undefined' ? true : settings.noOverLap;
settings.startingDegree = settings.startingDegree || 270;
settings.startSatellitesOnEdge = typeof settings.startSatellitesOnEdge == 'undefined' ? true : settings.startSatellitesOnEdge;
var itemIndex = $.inArray(settings.item, settings.collection);
var itemCnt = settings.collection.length;
var satelliteSide = settings.itemDiameter + (settings.itemSeparation || 0) + (settings.itemPadding || 0);
var evenDistribution = typeof settings.evenDistribution == 'undefined' ? true : settings.evenDistribution;
var degreeOfSeparation = (360 / itemCnt);
/*
we know all three sides:
one side is the diameter of the satellite itself (plus any padding). the other two
are the parent radius + the radius of the satellite itself (plus any padding).
given that, we need to find the angle of separation using the law of cosines (solveAngle)
*/
//if (!evenDistribution) {
var side1 = ((satelliteSide / 2)) + ((settings.minCenterDiameter + (2 * settings.centerPadding)) / 2);
var side2 = satelliteSide;;
var degreeOfSeparationBasedOnSatellite = solveAngle(side1, side1, side2); //Math.acos(((((side1 * side1) + (side2 * side2)) - (side2 * side2)) / (side2 * side2 * 2)) / 180 * Math.PI) * Math.PI;
degreeOfSeparation = evenDistribution? degreeOfSeparation: settings.noOverLap ? Math.min(degreeOfSeparation, degreeOfSeparationBasedOnSatellite) : degreeOfSeparationBasedOnSatellite;
//}
//angle-angle-side
//a-A-B
var a = satelliteSide;
var A = degreeOfSeparation;
/*
the three angles of any triangle add up to 180. We know one angle (degreeOfSeparation)
and we know the other two are equivalent to each other, so...
*/
var B = (180 - A) / 2;
//b is length necessary to fit all satellites, might be too short to be outside of base circle
var b = a * sind(B) / sind(A);
var offset = (settings.itemDiameter / 2) + (settings.itemPadding || 0); // 1; //
var onBaseCircleLegLength = ((settings.minCenterDiameter / 2) + settings.centerPadding) + offset;
var offBase = false;
if (b > onBaseCircleLegLength) {
offBase = true;
}
b = settings.noOverLap ? Math.max(b, onBaseCircleLegLength) : onBaseCircleLegLength;
var radianDegree = degToRad(degreeOfSeparation);
//log('b=' + b);
//log('settings.center.x=' + settings.center.x);
//log('settings.center.y=' + settings.center.y);
var degreeOffset = settings.startingDegree;
if (settings.startSatellitesOnEdge) {
degreeOffset += ((offBase ? degreeOfSeparation : degreeOfSeparationBasedOnSatellite) / 2);
}
var i = ((Math.PI * degreeOffset) / 180) + (radianDegree * itemIndex);// + (degToRad(degreeOfSeparationBasedOnSatellite) / 2); //(radianDegree) * (itemIndex);
var x = (Math.cos(i) * b) + (settings.center.x - offset);
var y = (Math.sin(i) * b) + (settings.center.y - offset);
return { 'x': Math.round(x), 'y': Math.round(y) };
}
,
/* if we ever want to size satellite by how many need to fit tight around the base circle:
x: function calcCircles(n) {
circles.splice(0); // clear out old circles
var angle = Math.PI / n;
var s = Math.sin(angle);
var r = baseRadius * s / (1 - s);
console.log(angle);
console.log(s);
console.log(r);
console.log(startAngle);
console.log(startAngle / (Math.PI * 2));
for (var i = 0; i < n; ++i) {
var phi = ((Math.PI * startAngle) / 180) + (angle * i * 2);
var cx = 150 + (baseRadius + r) * Math.cos(phi);
var cy = 150 + (baseRadius + r) * Math.sin(phi);
circles.push(new Circle(cx, cy, r));
}
},
*/
//settings must have: collection (array), itemDiameter (number), minCenterDiameter (number), center (json with x, y numbers)
//optional: itemPadding (number), evenDistribution (boolean), centerPadding (boolean), noOverLap (boolean)
getAllPositions: function (settings) {
var point;
var points = [];
var collection = settings.collection;
for (var i = 0; i < collection.length; i++) {
settings.item = collection[i]
points.push(satellite.getPosition(settings));
}
return points;
}
};
var el = $("#center"), cnt = 10, arr = [], itemDiameter= 100;
for (var c = 0; c < cnt; c++) {
arr.push(c);
}
var settings = {
collection: arr,
itemDiameter: itemDiameter,
minCenterDiameter: el.width(),
center: { x: el.width() / 2, y: el.width() / 2 },
itemPadding: 2,
evenDistribution: false,
centerPadding: parseInt(el.css("border-width")),
noOverLap: false,
startingDegree: 270
};
var points = satellite.getAllPositions(settings);
for (var i = 0; i < points.length; i++) {
var $newdiv1 = $("<div></div>");
var div = el.append($newdiv1);
$newdiv1.addClass("circle").addClass("sm");
$newdiv1.text(i);
$newdiv1.css({ left: points[i].x, top: points[i].y, width: itemDiameter +'px', height: itemDiameter +'px' });
}
});//]]>
</script>
</head>
<body>
<div id="center" class="circle" style="left:250px;top:250px" >
</div>
</body>
</html>
The central bit you need to work out is radius of the small circles. If you have R for radius of the central circle and you want to fit n smaller circles around it. Let the as yet unknown radius of the small circle be r. We can construct a right angle triangle with one corner in the center of the big circle one in the center of the small circle and one which is where a line from the center is tangent to the small circle. This will be a right angle. The angle at the center is a the hypotenuse has length R+r the opposite is r and we don't need the adjacent. Using trig
sin(a) = op / hyp = r / (R + r)
rearrange
(R+r) sin(a) = r
R sin(a) + r sin(a) = r
R sin(a) = r - r sin(a)
R sin(a) = (1 - sin(a)) r
r = R sin(a) / ( 1 - sin(a))
once we have r we are pretty much done.
You can see this as a fiddle http://jsfiddle.net/SalixAlba/7mAAS/
// canvas and mousedown related variables
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// save canvas size to vars b/ they're used often
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var baseRadius = 50;
var baseCircle = new Circle(150,150,50);
var nCircles = 7;
var startAngle = 15.0;
function Circle(x,y,r) {
this.x = x;
this.y = y;
this.r = r;
}
Circle.prototype.draw = function() {
ctx.beginPath();
ctx.arc(this.x,this.y,this.r, 0, 2 * Math.PI, false);
ctx.stroke();
}
var circles = new Array();
function calcCircles(n) {
circles.splice(0); // clear out old circles
var angle = Math.PI / n;
var s = Math.sin(angle);
var r = baseRadius * s / (1-s);
console.log(angle);
console.log(s);
console.log(r);
for(var i=0;i<n;++i) {
var phi = startAngle + angle * i * 2;
var cx = 150+(baseRadius + r) * Math.cos(phi);
var cy = 150+(baseRadius + r) * Math.sin(phi);
circles.push(new Circle(cx,cy,r));
}
}
function draw() {
baseCircle.draw();
circles.forEach(function(ele){ele.draw()});
}
calcCircles(7);
draw();

How to get CSS transform rotation value in degrees with JavaScript

I'm using the code found at CSS-Tricks to get the current rotation transform (in CSS) with JavaScript.
JavaScript function:
function getCurrentRotation( elid ) {
var el = document.getElementById(elid);
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"fail...";
if( tr !== "none") {
console.log('Matrix: ' + tr);
var values = tr.split('(')[1];
values = values.split(')')[0];
values = values.split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
var scale = Math.sqrt(a*a + b*b);
// arc sin, convert from radians to degrees, round
/** /
var sin = b/scale;
var angle = Math.round(Math.asin(sin) * (180/Math.PI));
/*/
var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
/**/
} else {
var angle = 0;
}
// works!
console.log('Rotate: ' + angle + 'deg');
$('#results').append('<p>Rotate: ' + angle + 'deg</p>');
}
According to the post, this works, however, for values over 180 degrees, I get negative numbers, and 360deg returns zero. I need to be able to correctly return the degree value from 180-360 degrees.
What am I doing wrong with this code that won't let it return the correct degree turn over 180 degrees?
It will make a lot more sense if you view the demo: See the pen for a demo of this in action.
I came in need of something like this too and decided to start from the initial code, doing a little clean up and some little improvement; then I modified as for the OP needing, so I wanted to share it here now:
function getCurrentRotation(el){
var st = window.getComputedStyle(el, null);
var tm = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"none";
if (tm != "none") {
var values = tm.split('(')[1].split(')')[0].split(',');
/*
a = values[0];
b = values[1];
angle = Math.round(Math.atan2(b,a) * (180/Math.PI));
*/
//return Math.round(Math.atan2(values[1],values[0]) * (180/Math.PI)); //this would return negative values the OP doesn't wants so it got commented and the next lines of code added
var angle = Math.round(Math.atan2(values[1],values[0]) * (180/Math.PI));
return (angle < 0 ? angle + 360 : angle); //adding 360 degrees here when angle < 0 is equivalent to adding (2 * Math.PI) radians before
}
return 0;
}
Use it like this:
getCurrentRotation(document.getElementById("el_id"));
Found the answer in another SO question, you have to add (2 * PI) if the result in radians is less than zero.
This line:
var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
Needs to be replaced with this:
var radians = Math.atan2(b, a);
if ( radians < 0 ) {
radians += (2 * Math.PI);
}
var angle = Math.round( radians * (180/Math.PI));

Does a getColorBoundsRect() equivalent exist in javascript?

just wanted to know if anybody has already done this in Javascript or if I have to do it myself - if latter: How would I do it? (not asking for a piece of code, only curious which approach you would use)
I have a better solution. It is not necessary to iterate through all the pixels, only through the ones outside the bounding box. Think of it this way, if you wanted to do the same thing in 1D: finding the first and last position of a value in an array, would you walk through the entire array? It would be better to walk through from the start till you find the first value, then walk from the end till you find the last value. The following code does the same for 2D. I haven't tested it thoroughly (either for correctness or speed), but it seems to work, and common sense say it's faster.
BitmapData.prototype.getColorBoundsRect = function(mask, color, findColor, rect){
findColor = typeof findColor !== 'undefined' ? findColor : true;
rect = typeof rect !== 'undefined' ? rect : new module.Rect(0, 0, this.width, this.height);
var l = rect.w - 1;
var r = 0;
var t = rect.h - 1;
var b = 0;
var data = this.context.getImageData(rect.x, rect.y, rect.w, rect.h).data;
// Scan from top to first pixel.
for (var i = 0; i < data.length; i += 4){
var val = module.RGBToHex({r:data[i], g:data[i+1], b:data[i+2], a:data[i+3]});
// console.log(val, mask, color, (val & mask) >>> 0)
if ((findColor && ((val & mask) >>> 0 == color)) || (!findColor && ((val & mask) >>> 0 != color))){
l = r = ((i / 4) % rect.w);
t = b = Math.floor(i / 4 / rect.w);
break;
}
}
// We found nothing.
if (i >= data.length) {
return null;
}
// Scan from bottom to first pixel
for (var j = data.length - 4; j > i; j -= 4){
var val = module.RGBToHex({r:data[j], g:data[j+1], b:data[j+2], a:data[j+3]});
if ((findColor && ((val & mask) >>> 0 == color)) || (!findColor && ((val & mask) >>> 0 != color))){
l = Math.min(l, ((j / 4) % rect.w))
r = Math.max(r, ((j / 4) % rect.w))
b = Math.floor(j / 4 / rect.w);
break;
}
}
console.log(l, r, t, b);
// Scan from left
for (var x = 0; x < l; x ++){
for (var y = t + 1; y <= b; y ++){
i = (y * rect.w + x) * 4
var val = module.RGBToHex({r:data[i], g:data[i+1], b:data[i+2], a:data[i+3]});
if ((findColor && ((val & mask) >>> 0 == color)) || (!findColor && ((val & mask) >>> 0 != color))){
l = Math.min(l, x);
break;
}
}
}
console.log(l, r, t, b);
// Scan from right
for (var x = rect.w - 1; x > r; x --){
for (var y = t; y < b; y ++){
i = (y * rect.w + x) * 4
var val = module.RGBToHex({r:data[i], g:data[i+1], b:data[i+2], a:data[i+3]});
if ((findColor && ((val & mask) >>> 0 == color)) || (!findColor && ((val & mask) >>> 0 != color))){
r = Math.max(r, x);
break;
}
}
}
console.log(l, r, t, b)
return new module.Rect(l + rect.x, t + rect.y, (r - l), (b - t));
}
In this code BitmapData just wraps a canvas object and its context2d, and Rect is an {x: , y: , w: , h: } object. I had to do some screwing around with RGBToHex to make sure I was getting positive numbers (uint's) too:
module.RGBToHex = function(rgb) {
return (rgb.a << 24 | rgb.r<<16 | rgb.g<<8 | rgb.b) >>> 0;
};
here's my quick'n'dirty solution, maybe somebody'll find it useful ;)
/**
* get a rectangle around color
* #param {...} ctx 2dCanvasObject to be scanned
* #return {Object} object storing the rectangle's data (x, y, w(idth), h(eight))
*/
function getColorBoundsRect(ctx) {
/**
* the canvas' context's data property (shorthand)
* #type {...}
*/
var data = ctx.data,
/**
* counter variable
* #type {Number}
*/
i = 0,
/**
* the "leftest" pixel that is not black (starts right, as we check if currently looped pixel (that is not black) is "lefter" than the current outerLeftPixel)
* #type {Number}
*/
outerLeftPixel = w-1,
/**
* the "rightest" pixel that is not black (starts left, as we check if currently looped pixel (that is not black) is "righter" than the current outerRightPixel)
* #type {Number}
*/
outerRightPixel = 0,
/**
* the "toppest" pixel that is not black (starts at bottom, as we check if currently looped pixel (that is not black) is "topper" than the current outerTopPixel)
* #type {Number}
*/
outerTopPixel = h-1,
/**
* the "bottomest" pixel that is not black (starts at top, as we check if currently looped pixel (that is not black) is "bottomer" than the current outerBottomPixel)
* #type {Number}
*/
outerBottomPixel = 0,
/**
* x coordinate of currently looped pixel
* #type {Number}
*/
x,
/**
* y coordinate of currently looped pixel
* #type {Number}
*/
y;
// loop through all pixels
// i equals the i'th pixel (0 is the upper left pixel, w*h is the bottom right pixel)
while (i < (data.length / 4)) {
// check if currently looped pixel is anything else than black --> color
if ((data[i*4] + data[i*4+1] + data[i*4+2]) > 0) {
// set coordinates for the currently looped pixel
x = i % w; // if one row has 10px and i = 35, the x coordinate of the current pixel is 35 % 10 = 5
y = Math.floor(i / w); // if one row has 10px and i=35, the y coordinate of the current pixel is 35/10 = 3.5 (--> rounded off = 3)
// if the x coordinate of the current (colored) pixel is smaller than the current "leftest" pixel, set the x coordinate as new "leftest pixel"
// same procedure for the other values
if (x < outerLeftPixel) {
outerLeftPixel = x;
}
if (x > outerRightPixel) {
outerRightPixel = x;
}
if (y < outerTopPixel) {
outerTopPixel = y;
}
if (y > outerBottomPixel) {
outerBottomPixel = y;
}
}
++i;
}
// if there is color on the canvas, the outer[Right|Left|Bottom|Top]Pixel properties should have been updated accordingly and the following condition should be true
if (outerRightPixel > outerLeftPixel && outerBottomPixel > outerTopPixel) {
return {
x: outerLeftPixel,
y: outerTopPixel,
w: outerRightPixel - outerLeftPixel,
h: outerBottomPixel - outerTopPixel
};
}
// if there is no color on the canvas, return false, as there is no rectangle
else {
return false;
}
}

Use CMYK on web page

I need to use CMYK colors on my web page. Is there any way to use CMYK in CSS or may be convert CMYK to RGB using JavaScript?
EDIT:
I mean I have colors creating algorithm in CMYK notation and I need to use it on web page.
There is no perfect algorithmic way to convert CMYK to RGB. CYMK is a subtractive color system, RGB is an additive color system. Each have different gamuts, which means there are colors that just cannot be represented in the other color system and vice versa. Both are device dependent color spaces, which really means that what color you really get is dependent on which device you use to reproduce that color, which is why you have color profiles for each device that adjust how it produces color into something more "absolute".
The best that you can do is approximate a simulation of one space onto the other. There is an entire field of computer science that is dedicated to this kind of work, and its non-trivial.
If you are looking for a heuristic for doing this, then the link that Cyrille provided is pretty simple math, and easily invertible to accept a CYMK color and produce a reasonable RGB facsimile.
A very simple heuristic is to map cyan to 0x00FFFF, magenta to 0xFF00FF, and yellow to 0xFFFF00, and black (key) to 0x000000. Then do something like this:
function cmykToRGB(c,m,y,k) {
function padZero(str) {
return "000000".substr(str.length)+str
}
var cyan = (c * 255 * (1-k)) << 16;
var magenta = (m * 255 * (1-k)) << 8;
var yellow = (y * 255 * (1-k)) >> 0;
var black = 255 * (1-k);
var white = black | black << 8 | black << 16;
var color = white - (cyan | magenta | yellow );
return ("#"+padZero(color.toString(16)));
}
invoking cmykToRGB with cmyk ranges from 0.0 to 1.0. That should give you back an RGB color code. But again this is just a heuristic, an actual conversation between these color spaces is much more complicated and takes into account a lot more variables then are represented here. You mileage may vary, and the colors you get out of this might not "look right"
jsFiddle here
There's no way to use CMYK in CSS. You can either use RGB or HSL (CSS3 only). Here's a JavaScript algorithm to convert CMYK to RGB (and the other way around).
Edit: the link seems dead now, here's the code from a cached version:
/**
*
* Javascript color conversion
* http://www.webtoolkit.info/
*
**/
function HSV(h, s, v) {
if (h <= 0) { h = 0; }
if (s <= 0) { s = 0; }
if (v <= 0) { v = 0; }
if (h > 360) { h = 360; }
if (s > 100) { s = 100; }
if (v > 100) { v = 100; }
this.h = h;
this.s = s;
this.v = v;
}
function RGB(r, g, b) {
if (r <= 0) { r = 0; }
if (g <= 0) { g = 0; }
if (b <= 0) { b = 0; }
if (r > 255) { r = 255; }
if (g > 255) { g = 255; }
if (b > 255) { b = 255; }
this.r = r;
this.g = g;
this.b = b;
}
function CMYK(c, m, y, k) {
if (c <= 0) { c = 0; }
if (m <= 0) { m = 0; }
if (y <= 0) { y = 0; }
if (k <= 0) { k = 0; }
if (c > 100) { c = 100; }
if (m > 100) { m = 100; }
if (y > 100) { y = 100; }
if (k > 100) { k = 100; }
this.c = c;
this.m = m;
this.y = y;
this.k = k;
}
var ColorConverter = {
_RGBtoHSV : function (RGB) {
var result = new HSV(0, 0, 0);
r = RGB.r / 255;
g = RGB.g / 255;
b = RGB.b / 255;
var minVal = Math.min(r, g, b);
var maxVal = Math.max(r, g, b);
var delta = maxVal - minVal;
result.v = maxVal;
if (delta == 0) {
result.h = 0;
result.s = 0;
} else {
result.s = delta / maxVal;
var del_R = (((maxVal - r) / 6) + (delta / 2)) / delta;
var del_G = (((maxVal - g) / 6) + (delta / 2)) / delta;
var del_B = (((maxVal - b) / 6) + (delta / 2)) / delta;
if (r == maxVal) { result.h = del_B - del_G; }
else if (g == maxVal) { result.h = (1 / 3) + del_R - del_B; }
else if (b == maxVal) { result.h = (2 / 3) + del_G - del_R; }
if (result.h < 0) { result.h += 1; }
if (result.h > 1) { result.h -= 1; }
}
result.h = Math.round(result.h * 360);
result.s = Math.round(result.s * 100);
result.v = Math.round(result.v * 100);
return result;
},
_HSVtoRGB : function (HSV) {
var result = new RGB(0, 0, 0);
var h = HSV.h / 360;
var s = HSV.s / 100;
var v = HSV.v / 100;
if (s == 0) {
result.r = v * 255;
result.g = v * 255;
result.v = v * 255;
} else {
var_h = h * 6;
var_i = Math.floor(var_h);
var_1 = v * (1 - s);
var_2 = v * (1 - s * (var_h - var_i));
var_3 = v * (1 - s * (1 - (var_h - var_i)));
if (var_i == 0) {var_r = v; var_g = var_3; var_b = var_1}
else if (var_i == 1) {var_r = var_2; var_g = v; var_b = var_1}
else if (var_i == 2) {var_r = var_1; var_g = v; var_b = var_3}
else if (var_i == 3) {var_r = var_1; var_g = var_2; var_b = v}
else if (var_i == 4) {var_r = var_3; var_g = var_1; var_b = v}
else {var_r = v; var_g = var_1; var_b = var_2};
result.r = var_r * 255;
result.g = var_g * 255;
result.b = var_b * 255;
result.r = Math.round(result.r);
result.g = Math.round(result.g);
result.b = Math.round(result.b);
}
return result;
},
_CMYKtoRGB : function (CMYK){
var result = new RGB(0, 0, 0);
c = CMYK.c / 100;
m = CMYK.m / 100;
y = CMYK.y / 100;
k = CMYK.k / 100;
result.r = 1 - Math.min( 1, c * ( 1 - k ) + k );
result.g = 1 - Math.min( 1, m * ( 1 - k ) + k );
result.b = 1 - Math.min( 1, y * ( 1 - k ) + k );
result.r = Math.round( result.r * 255 );
result.g = Math.round( result.g * 255 );
result.b = Math.round( result.b * 255 );
return result;
},
_RGBtoCMYK : function (RGB){
var result = new CMYK(0, 0, 0, 0);
r = RGB.r / 255;
g = RGB.g / 255;
b = RGB.b / 255;
result.k = Math.min( 1 - r, 1 - g, 1 - b );
result.c = ( 1 - r - result.k ) / ( 1 - result.k );
result.m = ( 1 - g - result.k ) / ( 1 - result.k );
result.y = ( 1 - b - result.k ) / ( 1 - result.k );
result.c = Math.round( result.c * 100 );
result.m = Math.round( result.m * 100 );
result.y = Math.round( result.y * 100 );
result.k = Math.round( result.k * 100 );
return result;
},
toRGB : function (o) {
if (o instanceof RGB) { return o; }
if (o instanceof HSV) { return this._HSVtoRGB(o); }
if (o instanceof CMYK) { return this._CMYKtoRGB(o); }
},
toHSV : function (o) {
if (o instanceof HSV) { return o; }
if (o instanceof RGB) { return this._RGBtoHSV(o); }
if (o instanceof CMYK) { return this._RGBtoHSV(this._CMYKtoRGB(o)); }
},
toCMYK : function (o) {
if (o instanceof CMYK) { return o; }
if (o instanceof RGB) { return this._RGBtoCMYK(o); }
if (o instanceof HSV) { return this._RGBtoCMYK(this._HSVtoRGB(o)); }
}
}
Usage:
To convert from HSV to RGB use library like this:
var result = ColorConverter.toRGB(new HSV(10, 20, 30));
alert("RGB:" + result.r + ":" + result.g + ":" + result.b);
To convert from RGB to HSV use library like this:
var result = ColorConverter.toHSV(new RGB(10, 20, 30));
alert("HSV:" + result.h + ":" + result.s + ":" + result.v);
The same goes for CMYK.
CMYK support in CSS is currently considered by W3 for CSS3. But it’s mainly meant for printers and “it is not expected that screen-centric user agents support CMYK colors”. I think you can safely bet that none of the current browsers support CMYK for the screen and therefore you have to convert the colors to RGB somehow.
In the CSS Color Module Level 4 of the W3C as of 5 November 2019, there is a function called device-cmyk that can be used to define a device dependent CMYK color value.
Example:
color: device-cmyk(0 81% 81% 30%);
The function returns an RGB value that the device calculates by trying to convert the CMYK color to an RGB value that matches the CMYK color as close as possible.
Note: I can't find anything regarding the browser support. I guess that no browser is currently supporting this.
You can create your own SCSS/SASS function.
SCSS:
#function cmyk($c, $m, $y, $k) {
$c: $c / 100;
$m: $m / 100;
$y: $y / 100;
$k: $k / 100;
$r: 255 * (1 - $c) * (1 - $k);
$g: 255 * (1 - $m) * (1 - $k);
$b: 255 * (1 - $y) * (1 - $k);
#return rgb($r, $g, $b);
}
SASS:
#function cmyk($c, $m, $y, $k)
$c: $c / 100
$m: $m / 100
$y: $y / 100
$k: $k / 100
$r: 255 * (1 - $c) * (1 - $k)
$g: 255 * (1 - $m) * (1 - $k)
$b: 255 * (1 - $y) * (1 - $k)
#return rgb($r, $g, $b)

Categories

Resources