How do you fill specific countries using d3.geo with canvas? - javascript

I created my own pre-projected topojson file, and I am trying to change the fillstyle of a specific country in my canvas. http://bl.ocks.org/anonymous/fea6cfde1184e2f10de8 is my code/example. I tried to catch the specific country code and set a fill for that country (Germany in this case). However, it fills a bunch of other countries in for some reason as well. I suspected it might be a shared borders issue, but some countries that do not share a border get filled as well.
Also interesting is that if I do not render the borders for the other countries as in http://bl.ocks.org/anonymous/3256fe09efaa8d2ebc96 the fill works, but obviously all the borders disappear. Am I simply not understanding how canvas fills work? Is there a different way to highlight a specific country? It has occurred to me that I can use a mask to fill in a specific country, but that seems like an awfully roundabout way to do it.
Note, I am aware that it is way simpler to do using SVG/CSS, but I would really like to get it working in canvas.

As in this example, you need to draw all the borders first, then just fill the region you want:
var isDEU = null;
for (var i = 0; i < geoJson.features.length; i++) {
if (geoJson.features[i].id == "DEU") {
isDEU = geoJson.features[i];
}
context.save();
canvasPath(geoJson.features[i]);
context.stroke();
context.restore();
}
context.fillStyle = "#f00"
context.beginPath();
canvasPath(isDEU);
context.fill();
Example here.

Related

Clip by mask defined by pixel image or prevent drawing outside of that mask

I'm two days into js,html and css programming. So very newbie!
Following and building upon this TUTORIAL
Q1: How can I add this male into the background (see figuere 1.) and prohibit any strokes outside of the borders?
Adding image to background was no biggy!
function make_base()
{
base_image = new Image();
base_image.src = 'img/bmapFront.gif';
base_image.onload = function(){
context.drawImage(base_image, 0,0);
}
}
There is a context.clip function, not sure if I can use pixel form as clipping path. Making tons of "image substractions" isn't the best way.
Any suggestions
Edit:
Did the Job for me: VeryHelpful
var frontPath = new Path2D ("M 133.41,17.00 C 141.37,2.41 160.66, !VERY LONG! ")
context.clip(frontPath);
Messy strokes!
He should look like this. Then I want to save him.
Although there is such a thing as ctx.clip(), this is sometimes not what's wanted as it's impractical to use a path.
The solution that I like involves creating a virtual empty canvas onto which you draw your pixel image. Through various manipulations, like using ctx.getImageData and similar to make sure you only get one kind of color or apply other filters only once, you can obtain an image that seems to be empty (alpha of 0, mostly) in the places where you want to clip other images or paths out.
At that point, you'd use ctx.globalCompositeOperation = 'source-atop', or pick another one you might want to use from mdn's list of globalCompositeOperations.
At this point, you can just draw this virtual canvas image onto the main canvas

Weird HTML 5 Canvas Antialiasing

I've been playing with canvas element and discovered that when I attempt to draw NxN uniform solid-colored cells next to each other, in some width/height configurations, there are blurry white-ish lines between them.
For instance, this canvas is supposed to look black but contains some sort of grid which I conjecture to be a result of faulty antialiasing in the browser.
Suffice to say, this bug appears only in some configurations but I would like to get rid of it for good. Is there any way to circumvent this? Have you ever had problems with antialiasing in canvas?
I have made this fiddle which demonstrates the issue and allows you to play with the dimensions of the canvas and number of cells. It also contains the code I use to draw the cells, so that you can inspect it and tell me if I'm doing anything wrong.
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
for (var i = 0; i < numberOfCells; ++i) {
for (var j = 0; j < numberOfCells; ++j) {
ctx.fillStyle = '#000';
ctx.fillRect(j * cellWidth, i * cellHeight, cellWidth, cellHeight);
}
}
Thanks in advance,
Petr.
jsFiddle : https://jsfiddle.net/ngxjnywz/2/
snippet of javascript
var cellWidth = Math.ceil(canvasWidth / numberOfCells);
var cellHeight = Math.ceil(canvasHeight / numberOfCells);
Depending on the width, height and your numberOfCells you are sometimes getting a... lets say 4.2 which is 4, however this would be displayed wrong and will allow a 1 pixel blank line to appear. So all you need to do is use the Math.ceil function and this will cause your cellWidth and cellHeight to always be the higher number and you won't get blank lines anymore
The best solution is to add a 0.5 pixel wide stroke around all the fills, using the same style as the fill and offsetting all drawing so that you render at the center of pixels rather than the top left.
If you add scaling or translation you will have to adjust the coordinates so that you still give the centers for your drawing coordinates.
In the end you can only reduce the artifacts but for many situations you will not be able to completely remove them.
This answer shows you how to remove the artifacts for an untransformed canvas.
How to fill the gaps
After reading through and trying several approaches, I've decided to come up with my own. I've created another (virtual) canvas which had integer dimensions corresponding to the number of cells in the grid.
After drawing all the cells in there, I call context.drawImage() on the main canvas and pass the virtual canvas as an argument along with offset and scale parameters to make it fit rest of my drawing. Assuming that the browser would scale the virtual canvas's image as a whole (and not as individual cells), I was hoping to get rid of the unwanted separator lines.
In spite of my efforts, the lines are still there. Any suggestions?
Here's the fiddle demonstrating my technique: https://jsfiddle.net/ngxjnywz/5/

KineticJS - Patterns and Fills on Images

I have a canvas that I'm creating with KineticJS and I am adding transparent PNG images to that canvas. When stacked on top of each other, this makes one image of an outfit with all the different parts.
What I then want to do is allow the user to click on a pattern and then change a specific piece of that outfit with that pattern. So I need to fill in the non-transparent parts of one of the images with that pattern. I found a way to do this that didn't use KineticJS and it looks something like this:
ctx.globalCompositeOperation = 'source-in';
var ptrn = ctx.createPattern(fabricA, 'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, 375, 260);
My question is, is there a way to do the same steps outlined above with KineticJS?
Also, I did first try to just do this without using KineticJS, but when I applied the above code to the layer, it filled in all of the images because they were all on the same layer. So I'm guessing that I will need to change my code to either use multiple layers or to add the images to groups in a single layer. Is my thinking right here? And which would be the better option for what I'm trying to accomplish? Multiple Layers? Or Multiple Groups on a single Layer?
Thanks for any help that anyone can provide.
If you want to do custom drawing then use the KineticJS Shape Object
This is a KineticJS object that lets you control exactly how it's drawn.
You create your overlays using your compositing method. Then put that drawing code in a function and give that function to Kinetic Shape's drawFunc.
Here's a skeleton of Kinetic.Shape:
var outfit1 = new Kinetic.Shape({
drawFunc: function(canvas) {
// you are passed a canvas to draw your custom shape on
// so new-up a context and get drawing!
var context = canvas.getContext();
context.beginPath();
// Draw stuff--including your composited overlays
// You can use any canvas.context drawing commands
},
id:"myCustomOutfit"
});
You can get started with an example here: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-shape-tutorial/

Why does lineTo() change interior pixels?

This situation is difficult to explain, so let me illustrate with a picture:
Those pixels inside the first shape created are lightened. The screen is cleared with black, the red and green boxes are drawn, then the path is drawn. The only fix that I've found so far was setting the line width of the boxes to 2 pixels, for the reasons outlined here.
Here's the code being used to draw the squares:
sctx.save();
sctx.strokeStyle = this.color;
sctx.lineWidth = this.width;
sctx.beginPath();
sctx.moveTo(this.points[0].x, this.points[0].y);
for (var i = 1; i < this.points.length; i++)
{
sctx.lineTo(this.points[i].x, this.points[i].y);
}
sctx.closePath();
sctx.stroke();
sctx.restore();
And the lines:
sctx.save();
sctx.strokeStyle = 'orange';
sctx.lineWidth = 5;
console.log(sctx);
sctx.beginPath();
sctx.moveTo(this.points[0].x, this.points[0].y);
for (var i = 1; i < this.points.length; i++)
{
sctx.lineTo(this.points[i].x, this.points[i].y);
}
sctx.closePath();
sctx.stroke();
sctx.restore();
And a picture of the same situation where the boxes are drawn at 2px width:
Is lineTo() perhaps messing with the alpha values? Any help is greatly appreciated.
EDIT: To clarify, the same thing occurs when sctx.closePath(); is omitted from the path being drawn.
It would seem as though this is a currently undocumented rendering bug that for some reason appears on all platforms. There is very little info out there about it, and it will hopefully be attended to before HTML5 is the official standard.
As a workaround, don't use lineTo()'s, use multiple sets of single lines.
Let's say you have: http://jsfiddle.net/g3Kvw/
As you can see it seems to be 2px wide. But if you change that to use fillRect() instead of lineTo(), you have http://jsfiddle.net/g3Kvw/1/ which is looking good.
Problem: you will not be able to draw something that is not a rectangle with this method.
But, if you sum 0.5 to EVERY int coordinate (x,y): http://jsfiddle.net/g3Kvw/3/ you will get the regular line.
I think this is some kind of antialiasing calculation bug on FF & webkit... But I didn't find it in the bug tracker, and this "weird" solution to convert the number to float, in order to get a solid line is quite confusing. Because using integers should be enough.

Click on given element in canvas

Is there any trick to determine if user clicks on given element rendered in canvas? For example I'm displaying rhombus from .png file with transparent background and i want to know if user click inside or outside that figure (like mouse-element collision).
There is no concept of individual elements in a canvas - it is simply just an area that you're drawing pixels onto. SVG on the other hand is made up of elements which you can then bind events to. However there are a few approaches you can take to add click events to canvas:
Position an html element that overlays the area on the canvas you want to be clickable. A for a rectangular area or an image map for something more irregular.
Use separate canvases for each element that you want to be clickable.
CAKE - I haven't used this myself, but it's description is "SVG sans the XML". This may cover your needs. Demos here http://glimr.rubyforge.org/cake/canvas.html#EditableCurve
One idea is to draw the image to a temporary canvas, then use getImageDate() to receive data for the pixel you are interested in, and check if its alpha value is 0 ( = transparent).
The following is a sketch of a solution. It is assumed that...
x and y are the coordinates of the mouse click event
you are looping over gameObjects, the current object being stored in the variable gameObject
the game object has been initialized with an image, x and y coordinates
The following code would then check whether the click was on a transparent area:
var tempCanvas = document.createElement('canvas');
if (tempCanvas.getContext) {
tempContext = tempCanvas.getContext('2d');
}
tempContext.drawImage(gameObject.img, 0, 0);
var imgd = tempContext.getImageData(x - gameObject.x, y - gameObject.y, 1, 1);
var pix = imgd.data;
if (pix[3] == 0) {
// user clicked on transparent part of the image!
}
Note: This is probably quite inefficient. I'm sure someone can come up with a better solution.
I have solve this problem using kintech.js, tutorials and examples can be found: http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-tutorial/

Categories

Resources