Kinetic.js Text gets drawn under shape with fill value set sometimes - javascript

In this fiddle: http://jsfiddle.net/m1erickson/fu5LP/
A group of wedges are drawn with text in the middle to produce the above image on the canvas.
When you set the fill value of the wedge object however, the output is rather odd:
Some text values are being drawn under the wedge's fill, I have no idea why.
The code for the fiddle is here:
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var layer = new Kinetic.Layer();
stage.add(layer);
var cx = 175;
var cy = 175;
var wedgeRadius = 140;
var accumAngle = 0;
var center = new Kinetic.Circle({
x: cx,
y: cy,
radius: 5,
fill: 'red'
});
layer.add(center);
for (var i = 0; i < 12; i++) {
newTextWedge(30, "Element # " + i);
}
function newTextWedge(angle, text) {
var wedge = new Kinetic.Wedge({
fill : 'black',
x: cx,
y: cy,
//If I add this fill the above output of hiding text occurs
//fill: 'black',
radius: wedgeRadius,
angleDeg: angle,
stroke: 'gray',
strokeWidth: 1,
rotationDeg: -accumAngle + angle / 2
});
layer.add(wedge);
if (accumAngle > 90 && accumAngle < 270) {
var offset = {
x: wedgeRadius - 10,
y: 7
};
var textAngle = accumAngle - 180;
} else {
var offset = {
x: -50,
y: 7
};
var textAngle = accumAngle;
}
var text = new Kinetic.Text({
x: cx,
y: cy,
text: text,
fill: 'red',
offset: offset,
rotationDeg: textAngle
});
layer.add(text);
layer.draw();
accumAngle += angle;
}
Anyone able to give any insight as to why this happens?

Debugging and watching the chart created step by step shows what is happening. The wedge layer is rendered and then the text layer is rendered. The next wedge is rendered on top of that last text layer.
Here is a fork of the fiddle:
http://jsfiddle.net/smurphy/UvdWt/
Kinetic Shape has a method moveToBottom() that you can call on the filled wedge that will force it to the bottom of the stack. See documentation
layer.add(wedge);
wedge.moveToBottom();

Related

Draw a line automaticlly tied to circle on Konva JS

i want to draw a line beetween a circle everytime i add a circle on konva js. and the circle is dragable. so i want the line follow the circle when i drag it. How to do it? or maybe i'm doing it wrong. Here my code https://jsfiddle.net/nutsi/2L7v6hy3/13/
var stage = new Konva.Stage({
height: window.innerHeight * 0.85,
width: window.innerWidth * 0.85,
container: "konva"
});
var layer = new Konva.Layer();
stage.on("click", function(){
var points = [];
var pos = this.getRelativePointerPosition();
var dot = new Konva.Circle({
x: pos.x,
y: pos.y,
fill: 'red',
radius: 5,
id: "seljal",
draggable: true
});
layer.add(dot)
var a = layer.find("#seljal")
for(let x = 0; x < a.length; x++){
points.push(a[x].attrs.x, a[x].attrs.y)
}
var line = new Konva.Line({
points: points,
stroke: "red",
strokeWidth: 2,
dash: [5, 5],
opacity: 1,
closed: !0,
id: "line"
})
var b = layer.find("#line")[0]
if(b) b.destroy();
layer.add(line);
})
stage.add(layer);
stage.draw();

Place node centred on rotated note

I have following example stage.
const rectangle = new Konva.Rect({
width: 300,
height: 100,
x: stage.width() / 2,
y: stage.height() / 2,
fill: 'blue',
rotation: 140
});
layer.add(rectangle);
layer.draw();
const tooltip = new Konva.Label({
x: rectangle.x() + (rectangle.width() / 2),
y: rectangle.y() + (rectangle.height() / 2),
opacity: 0.75
});
tooltip.add(
new Konva.Tag({
fill: 'green',
pointerDirection: 'down',
pointerWidth: 6,
pointerHeight: 4,
lineJoin: 'round'
})
);
tooltip.add(new Konva.Text({
text: 'Hello world',
fontSize: 12,
padding: 5,
fill: 'white',
}));
layer.add(tooltip);
layer.draw();
When the rectangle has no rotation I am able to place the tooltip correctly in the centre of the node. When the rectangle has some rotation the tooltip is not centred any more.
Without changing the attributes of the rectangle or rotating the tooltip, how can I place the tooltip in the centre of the rectangle node?
JSBIN example:
https://jsbin.com/wopoxuligi/edit?html,js,output
First, you need to update your declaration for Konva in the html script tag as you are referencing an old version.
<script src="https://unpkg.com/konva#7.0.3/konva.min.js"></script>
Because this shape is a rect you can call rectangle.getClientRect() to get the position on the stage, then use simple center-math to place your label. Slip this code in at the end of your current code where you do the last layer.draw();
const cliRect = rectangle.getClientRect();
let pos = {
x: cliRect.x + (cliRect.width )/2,
y: cliRect.y + (cliRect.height )/2
};
tooltip.x(pos.x)
tooltip.y(pos.y)
layer.draw();
This will leave the pointer of the label pointing at the centre of the rotated rectangle, with the label being non-rotated. Working snippet below - run it full screen.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
draggable: true
});
const layer = new Konva.Layer();
stage.add(layer);
const rectangle = new Konva.Rect({
width: 300,
height: 100,
x: stage.width() / 2,
y: stage.height() / 2,
fill: 'blue',
rotation: 140
});
layer.add(rectangle);
layer.draw();
const tooltip = new Konva.Label({
x: rectangle.x() + (rectangle.width() / 2),
y: rectangle.y() + (rectangle.height() / 2),
opacity: 0.75
});
tooltip.add(
new Konva.Tag({
fill: 'green',
pointerDirection: 'down',
pointerWidth: 6,
pointerHeight: 4,
lineJoin: 'round'
})
);
tooltip.add(new Konva.Text({
text: 'Hello world',
fontSize: 12,
padding: 5,
fill: 'white',
}));
layer.add(tooltip);
const cliRect = rectangle.getClientRect();
// Extra red rect to illustrate client rect. Not needed in solution
const rectangle1 = new Konva.Rect({
width: cliRect.width,
height: cliRect.height,
x: cliRect.x,
y: cliRect.y,
stroke: 'red'
});
layer.add(rectangle1);
let pos = {
x: cliRect.x + (cliRect.width )/2,
y: cliRect.y + (cliRect.height )/2
};
// Set tooltip position taking account of stage draggind
tooltip.x(pos.x - stage.x())
tooltip.y(pos.y - stage.y())
console.log(pos)
layer.draw();
<script src="https://unpkg.com/konva#7.0.3/konva.min.js"></script>
<div id="container"></div>
EDIT: In a comment the OP pointed out that the real use case requires the stage to be draggable before the label is added and that after the drag the calc of the label position fails. This is because getClientRect() gives the position in the client container without adjustment for the stage movement. To make the code work with a dragged stage, change the two lines that set tooltip.x & y to adjust by the stage.x & y, as follows:
tooltip.x(pos.x - stage.x())
tooltip.y(pos.y - stage.y())
I added offset to rectangle, to rotate it by its center point and managed to have the tooltip centered.
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
var group1 = new Konva.Group({
draggable: true
});
const rectWidth = 300;
const rectHeight = 100;
const rectangle = new Konva.Rect({
width: rectWidth,
height: rectHeight,
x: stage.width() / 2,
y: stage.height() / 2,
fill: 'blue',
rotation: 140,
offset: {
x: rectWidth / 2,
y: rectHeight / 2,
},
});
const tooltip = new Konva.Label({
x: rectangle.x() + (rectangle.width() / 2),
y: rectangle.y() + (rectangle.height() / 2),
offset: {
x: rectangle.width() / 2,
y: rectangle.height() / 2,
},
opacity: 0.75
});
tooltip.add(
new Konva.Tag({
fill: 'green',
pointerDirection: 'down',
pointerWidth: 6,
pointerHeight: 4,
lineJoin: 'round'
})
);
tooltip.add(new Konva.Text({
text: 'Hello world',
fontSize: 12,
padding: 5,
fill: 'white',
}));
layer.add(rectangle);
layer.add(tooltip);
layer.draw();
<script src="https://unpkg.com/konva#^2/konva.min.js"></script>
<div id="container"></div>

How can I rotate any shape or point on an HTML5 canvas around an arbitrary point?

I have seen a few cases of questions asking how to rotate a shape around a given point and decided to ask this self-answer question myself. So - in relation to HTML5 canvas, or in fact any two-dimensional surface, how can I rotate a shape around an arbitrary x,y point ?
It turns out the answer is pretty simple but involves a bit of math that may put some folks off. I'm using the Konvajs HTML5 canvas library but the code is easily transportable to your own lib. Also, this example is described as rotating a shape, but it's really rotating a point - the origin of the shape - so you could use it for any point-rotation-around-a-point case.
The rotateAroundPoint() function does the work - the rest of the code in the snippet is there to make it a working example.
Lifting this function out we can see that the inputs are the shape - although this could be any object with x, y and rotation properties, the rotation angle in degrees, and the rotation point - again an object with x & y values.
When we rotate around the point we are carrying out the equivalent of a rotation-in-place, followed by a translation (or move). These must be done in this sequence. Also, because of how 2d-drawing works, we have to work out the new position for the move and this depends on the drawing origin of the shape.
The calculation of the new x & y positions requires the use of sine & cosine functions which require radians - not degrees. So we multiply degrees by PI / 180 to get that.
// Rotate a shape around any point.
// shape is a Konva shape
// angleDegrees is the angle to rotate by, in degrees
// point is an object {x: posX, y: posY}
function rotateAroundPoint(shape, angleDegrees, point) {
let angleRadians = angleDegrees * Math.PI / 180; // sin + cos require radians
const x =
point.x +
(shape.x() - point.x) * Math.cos(angleRadians) -
(shape.y() - point.y) * Math.sin(angleRadians);
const y =
point.y +
(shape.x() - point.x) * Math.sin(angleRadians) +
(shape.y() - point.y) * Math.cos(angleRadians);
shape.rotation(shape.rotation() + angleDegrees); // rotate the shape in place
shape.x(x); // move the rotated shape in relation to the rotation point.
shape.y(y);
}
That's it! Have a play with the snippet - best viewed full-screen. Select a shape to rotate, then click the rotate button a few times to watch it spin around its origin (the natural point of rotation if we just change the rotation angle and nothing else). Then click the reset button, and click the canvas to move the blue target somewhere else on the canvas or shape, and rotate some more to see the effect.
There's also a codepen version here.
// Code to illustrate rotation of a shape around any given point. The important functions here is rotateAroundPoint() which does the rotation and movement math !
let
angle = 0, // display value of angle
startPos = {x: 80, y: 45},
shapes = [], // array of shape ghosts / tails
rotateBy = 20, // per-step angle of rotation
shapeName = $('#shapeName').val(), // what shape are we drawing
shape = null,
ghostLimit = 10,
// Set up a stage
stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
}),
// add a layer to draw on
layer = new Konva.Layer(),
// create the rotation target point cross-hair marker
lineV = new Konva.Line({points: [0, -20, 0, 20], stroke: 'cyan', strokeWidth: 1}),
lineH = new Konva.Line({points: [-20, 0, 20, 0], stroke: 'cyan', strokeWidth: 1}),
circle = new Konva.Circle({x: 0, y: 0, radius: 10, fill: 'transparent', stroke: 'cyan', strokeWidth: 1}),
cross = new Konva.Group({draggable: true, x: startPos.x, y: startPos.y});
// Add the elements to the cross-hair group
cross.add(lineV, lineH, circle);
layer.add(cross);
// Add the layer to the stage
stage.add(layer);
$('#shapeName').on('change', function(){
shapeName = $('#shapeName').val();
shape.destroy();
shape = null;
reset();
})
// Draw whatever shape the user selected
function drawShape(){
// Add a shape to rotate
if (shape !== null){
shape.destroy();
}
switch (shapeName){
case "rectangle":
shape = new Konva.Rect({x: startPos.x, y: startPos.y, width: 120, height: 80, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "hexagon":
shape = new Konva.RegularPolygon({x: startPos.x, y: startPos.y, sides: 6, radius: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "ellipse":
shape = new Konva.Ellipse({x: startPos.x, y: startPos.y, radiusX: 40, radiusY: 20, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "circle":
shape = new Konva.Ellipse({x: startPos.x, y: startPos.y, radiusX: 40, radiusY: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
case "star":
shape = new Konva.Star({x: startPos.x, y: startPos.y, numPoints: 5, innerRadius: 20, outerRadius: 40, fill: 'magenta', stroke: 'black', strokeWidth: 4});
break;
};
layer.add(shape);
cross.moveToTop();
}
// Reset the shape position etc.
function reset(){
drawShape(); // draw the current shape
// Set to starting position, etc.
shape.position(startPos)
cross.position(startPos);
angle = 0;
$('#angle').html(angle);
$('#position').html('(' + shape.x() + ', ' + shape.y() + ')');
clearTails(); // clear the tail shapes
stage.draw(); // refresh / draw the stage.
}
// Click the stage to move the rotation point
stage.on('click', function (e) {
cross.position(stage.getPointerPosition());
stage.draw();
});
// Rotate a shape around any point.
// shape is a Konva shape
// angleRadians is the angle to rotate by, in radians
// point is an object {x: posX, y: posY}
function rotateAroundPoint(shape, angleDegrees, point) {
let angleRadians = angleDegrees * Math.PI / 180; // sin + cos require radians
const x =
point.x +
(shape.x() - point.x) * Math.cos(angleRadians) -
(shape.y() - point.y) * Math.sin(angleRadians);
const y =
point.y +
(shape.x() - point.x) * Math.sin(angleRadians) +
(shape.y() - point.y) * Math.cos(angleRadians);
shape.rotation(shape.rotation() + angleDegrees); // rotate the shape in place
shape.x(x); // move the rotated shape in relation to the rotation point.
shape.y(y);
shape.moveToTop(); //
}
$('#rotate').on('click', function(){
let newShape = shape.clone();
shapes.push(newShape);
layer.add(newShape);
// This ghost / tails stuff is just for fun.
if (shapes.length >= ghostLimit){
shapes[0].destroy();
shapes = shapes.slice(1);
}
for (var i = shapes.length - 1; i >= 0; i--){
shapes[i].opacity((i + 1) * (1/(shapes.length + 2)))
};
// This is the important call ! Cross is the rotation point as illustrated by crosshairs.
rotateAroundPoint(shape, rotateBy, {x: cross.x(), y: cross.y()});
cross.moveToTop();
stage.draw();
angle = angle + 10;
$('#angle').html(angle);
$('#position').html('(' + Math.round(shape.x() * 10) / 10 + ', ' + Math.round(shape.y() * 10) / 10 + ')');
})
// Function to clear the ghost / tail shapes
function clearTails(){
for (var i = shapes.length - 1; i >= 0; i--){
shapes[i].destroy();
};
shapes = [];
}
// User cicks the reset button.
$('#reset').on('click', function(){
reset();
})
// Force first draw!
reset();
body {
margin: 10;
padding: 10;
overflow: hidden;
background-color: #f0f0f0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva#^3/konva.min.js"></script>
<p>1. Click the rotate button to see what happens when rotating around shape origin.</p>
<p>2. Reset then click stage to move rotation point and click rotate button again - rinse & repeat</p>
<p>
<button id = 'rotate'>Rotate</button>
<button id = 'reset'>Reset</button>
<select id='shapeName'>
<option value='rectangle'>Rectangle</option>
<option value='hexagon'>Polygon</option>
<option value='ellipse' >Ellipse</option>
<option value='circle' >Circle</option>
<option value='star' selected='selected'>Star</option>
</select>
Angle : <span id='angle'>0</span>
Position : <span id='position'></span>
</p>
<div id="container"></div>

How to fix FabricJS scaled polygon clipping offset

I'am trying to clip the FabricJS rect shape to the polygon shape. The clipping works okay until the polygon shape which need to be clipped is now scaled. After this there is some weird offset that is caused by the polygon clipping.
Can anyone help me how can i fix the function to prevent the polygon offset issue when clip object is scaled.
This is how it looks before scalling. The clipping works fine
Image => https://i.imgur.com/Eop2YJh.png
And then there is the problem when the polygon is scaled.
2: Image => https://i.imgur.com/ICkP8SG.png
Here is the code on fiddle with the clipping function
https://jsfiddle.net/0xpvc9uq/
So if there is anyone who knows whats the point and how can I fix it I would appriciate it.
Thx
var canvas = new fabric.Canvas('c');
var rect1 = new fabric.Rect({
left: 0, top: 0,
width: 900, height: 900,
fill: 'blue',
selectable: false,
clipTo: clipRegion,
scaleX: 1.5,
scaleY: 1.5
});
var clipPoly = new fabric.Polygon([
{ x: 180, y: 10 },
{ x: 300, y: 50 },
{ x: 300, y: 180 },
{ x: 180, y: 220 }
], {
originX: 'left',
originY: 'top',
left: 180,
top: 10,
fill: 'transparent', /* use transparent for no fill */
strokeWidth: 0,
selectable: false,
strokeWidth: 1,
stroke: "red",
scaleX: 1.3,
scaleY: 1.3
});
canvas.add(rect1, clipPoly);
function clipRegion (ctx) {
rect1.setCoords();
const clipObj = clipPoly;
const scaleXTo1 = (1 / rect1.scaleX);
const scaleYTo1 = (1 / rect1.scaleY);
ctx.save();
const ctxLeft = -( rect1.width / 2 ) - clipObj.strokeWidth - rect1.strokeWidth;
const ctxTop = -( rect1.height / 2 ) - clipObj.strokeWidth - rect1.strokeWidth;
ctx.translate( ctxLeft, ctxTop );
ctx.scale(scaleXTo1, scaleYTo1);
ctx.rotate((rect1.angle * -1) * (Math.PI / 180));
ctx.beginPath();
const matrix = clipPoly.calcTransformMatrix();
let points = [];
clipObj.points.forEach( (point) => {
points.push({
x: ((point.x * matrix[0]) + (clipObj.strokeWidth * clipObj.scaleX)) - rect1.oCoords.tl.x,
y: ((point.y * matrix[3]) + (clipObj.strokeWidth * clipObj.scaleY)) - rect1.oCoords.tl.y
});
});
ctx.moveTo(points[0].x, points[0].y);
points.forEach((point) => {
ctx.lineTo(point.x, point.y);
});
ctx.lineTo(points[0].x, points[0].y);
ctx.closePath();
ctx.restore();
}
I discovered that there is also another approach that can be used and that solves all the problem.
The clipRegion function now looks like:
function clipRegion (ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
clipPoly.render(ctx);
ctx.restore();
}
Which makes the rendering okay. If still anyone have other way to fix the above problem, I would like to see the answer

Draw collided transparent circles with the same color of common area

When i draw circles of the same color one by one using fill() for each circle. It comes that when they are overlapped, common are color is darker (saturated).
See example 1 (left) in JS Bin.
If i create long path and then use fill() for this common path, it has strange artifacts (yep, of course it's confused by complex path and don't know what i'm trying to draw)
See example 2 (right) in JS Bin.
How i can achieve that common area of circles with the same color was not saturated, it shoud be not darker than other circles with the same color. (exactly what i have in JS Bin right side, but without crazy artifacts)
If circles with different colors has common areas, color should be saturated.
Regarding your .getImageData solution ...
It's faster to use compositing rather than .getImageData to blend your colors.
Here's a function to combine overlapping semi-transparent circles without having the overlap darken.
Draw all identically colored circles on a 2nd canvas in an opaque color.
Set context.globalCompositeOperation='source-in' which causes new drawings to replace existing pixels.
Fill the 2nd canvas with the desired semi-transparent color for this set of indentically colored circles.
The result is a set of overlapping circles without the darkening effect.
function uniformColorCircles(circles){
var PI2=Math.PI*2;
tempctx.globalCompositeOperation='source-over';
tempctx.clearRect(0,0,cw,ch);
tempctx.beginPath();
for(var i=0;i<circles.length;i++){
var c=circles[i];
tempctx.arc(c.x,c.y,c.radius,0,PI2);
}
tempctx.fillStyle='black';
tempctx.fill();
tempctx.globalCompositeOperation='source-in';
tempctx.fillStyle=circles[0].rgba;
tempctx.fill();
}
And here is example code and a Demo involving multiple sets of semi-transparent circles:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
ctx.fillRect(0,0,120,220);
var tempCanvas=canvas.cloneNode();
var tempctx=tempCanvas.getContext('2d');
var c1={x:100,y:200,radius:50,rgba:'rgba(255,0,0,0.5)'};
var c2={x:100,y:240,radius:35,rgba:'rgba(255,0,0,0.5)'};
var c3={x:140,y:200,radius:50,rgba:'rgba(0,255,255,0.5)'};
var c4={x:140,y:240,radius:35,rgba:'rgba(0,255,255,0.5)'};
var c5={x:120,y:140,radius:50,rgba:'rgba(255,255,0,0.5)'};
uniformColorCircles([c1,c2]);
ctx.drawImage(tempCanvas,0,0);
uniformColorCircles([c3,c4]);
ctx.drawImage(tempCanvas,0,0);
uniformColorCircles([c5]);
ctx.drawImage(tempCanvas,0,0);
function uniformColorCircles(circles){
var PI2=Math.PI*2;
tempctx.globalCompositeOperation='source-over';
tempctx.clearRect(0,0,cw,ch);
tempctx.beginPath();
for(var i=0;i<circles.length;i++){
var c=circles[i];
tempctx.arc(c.x,c.y,c.radius,0,PI2);
}
tempctx.fillStyle='black';
tempctx.fill();
tempctx.globalCompositeOperation='source-in';
tempctx.fillStyle=circles[0].rgba;
tempctx.fill();
}
body{ background-color:white; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=512 height=512></canvas>
I have solved the problem using helper canvases and canvas image data processing.
There are data array, which contains coordinates and value which is used to determine which color we need to use.
I draw each circle color in it's own layer, then process each layer with preventSaturation function. Then add all layers into original canvas.
Please if anyone know the better way let me know.
If someone didn't understand what i needed to do it was:
1) i had this
2) i tried to have this
var canvas = document.getElementById('circles');
var ctx = canvas.getContext('2d');
var radius = 30;
var opacity = .7;
var data = [
{
x: 200,
y: 200,
v: 10
},
{
x: 230,
y: 230,
v: 20
},
{
x: 250,
y: 210,
v: 30
},
{
x: 270,
y: 190,
v: 40
},
{
x: 300,
y: 220,
v: 100
},
{
x: 300,
y: 260,
v: 200
},
{
x: 320,
y: 210,
v: 300
},
{
x: 300,
y: 160,
v: 200
},
{
x: 380,
y: 160,
v: 3000
},
{
x: 380,
y: 110,
v: 3000
},
{
x: 320,
y: 190,
v: 3000
}
];
var styles = {
blue: {
edgeValue: 0,
color: [0, 0, 255, 0.7]
},
green: {
edgeValue: 100,
color: [0, 255, 0, 0.7]
},
red: {
edgeValue: 1000,
color: [255, 0, 0, 0.7]
}
};
var layers = {};
for (var prop in styles) {
if(styles.hasOwnProperty(prop)) {
var c = document.createElement('canvas');
c.width = canvas.width;
c.height = canvas.height;
var cx = c.getContext('2d');
var cc = document.createElement('canvas');
cc.width = radius * 2;
cc.height = radius * 2;
var ccx = cc.getContext('2d');
var cColor = styles[prop].color;
ccx.fillStyle = 'rgba(' + cColor[0] + ',' + cColor[1] + ',' + cColor[2] + ',' + cColor[3] + ')';
ccx.beginPath();
ccx.arc(radius, radius, radius, 0, Math.PI * 2, true);
ccx.closePath();
ccx.fill();
layers[prop] = {
color: styles[prop].color,
edgeValue: styles[prop].edgeValue,
canvas: c,
ctx: cx,
canvasC: cc,
ctxC: ccx,
objects: []
};
}
}
data.forEach(function(o) {
var layer = o.v < styles.green.edgeValue ? layers.blue : o.v < styles.red.edgeValue ? layers.green : layers.red;
layer.ctx.drawImage(layer.canvasC, o.x, o.y);
layer.objects.push(o);
});
for(prop in layers) {
if(layers.hasOwnProperty(prop)) {
var image = layers[prop].ctx
.getImageData(0, 0, layers[prop].canvas.width, layers[prop].canvas.height);
preventColorSaturation(image.data, layers[prop].color);
layers[prop].ctx.putImageData(image, 0, 0);
ctx.drawImage(layers[prop].canvas, 0, 0);
}
}
function preventSaturation (d, s) {
var rgb255 = raRGBA255(s);
for (var i = 0; i < d.length; i += 4) {
d[i] = Math.min(d[i], rgb255[0]);
d[i + 1] = Math.min(d[i + 1], rgb255[1]);
d[i + 2] = Math.min(d[i + 2], rgb255[2]);
d[i + 3] = Math.min(d[i + 3], rgb255[3]);
}
}
function raRGBA255 (s) {
return [
s[0],
s[1],
s[2],
255 * s[3]
];
}
function raHexToRGB (s) {
var hexREGEXP = /^#([0-9A-Za-z]{3,6})$/;
var parsedHEX = s.match(hexREGEXP);
if (!parsedHEX) {
return [0, 0, 0];
}
return [
parseInt(parsedHEX[1].slice(0, 2), 16),
parseInt(parsedHEX[1].slice(2, 4), 16),
parseInt(parsedHEX[1].slice(4), 16)
];
}

Categories