I've been playing around with creating a hexagonal grid in HTML5 Canvas. I have a basic grid, where if you click a hex it will highlight the hex (demo here: http://cfiresim.com/hex-map-game-reboot/test.html)
In javascript, I am defining the canvas as such (some parts omitted for brevity):
var hex = {};
hex.canvas = document.getElementById("HexCanvas");
hex.ctx = null;
console.log("Initializing new game...");
this.radius = 20;
this.side = Math.round((3 / 2) * this.radius);
this.height = Math.round(Math.sqrt(3) * this.radius);
this.width = Math.round(2 * this.radius);
//Set Size of main div to size of canvas
$('#primary-panel').css('height', (hex.height * hex.rows)+hex.height*2);
hex.canvas.style.width='100%';
hex.canvas.style.height='100%';
hex.canvas.width = hex.canvas.offsetWidth;
hex.canvas.height = hex.canvas.offsetHeight;
//Set click eventlistener for canvas
this.canvas.addEventListener("mousedown", this.clickEvent.bind(this), false);
this.ctx = this.canvas.getContext('2d');
All of the code can be found here: https://github.com/boknows/hex-map-game-reboot
My primary question is: Is there a specific way to prevent the click events from getting all screwy when the canvas is resized via the browser? For example, if you shrink the browser to be just bigger than the grid, clicks dont register in the right place. Am I missing some feature of canvas? Maybe getSelectedTile() is not being defined correctly?
Edit: It seems like this primarily happens when the browser scrolls a little, and thus moves the grid off screen. The clicks then register with a weird offset, which I'm guessing is equal to the distance the screen scrolled. Advice?
You must take into a position of a page scroll.
In HexagonGrid.js, instead of this:
hex.clickEvent = function(e) {
var mouseX = e.pageX;
var mouseY = e.pageY;
Do this:
hex.clickEvent = function(e) {
var mouseX = e.pageX - window.pageXOffset;
var mouseY = e.pageY - window.pageYOffset;
Related
Website: http://minimedit.com/
Currently implementing an eye dropper. It works fine in my normal resolution of 1080p, but when testing on a higher or lower resolution it doesn't work.
This is the basics of the code:
var ctx = canvas.getContext("2d");
canvas.on('mouse:down', function(e) {
var newColor = dropColor(e, ctx);
}
function dropColor(e, ctx) {
var mouse = canvas.getPointer(e.e),
x = parseInt(mouse.x),
y = parseInt(mouse.y),
px = ctx.getImageData(x, y, 1, 1).data;
return rgb2hex('rgba('+px+')');
}
When I first initiate the canvas I have it resize to fit resolution:
setResolution(16/9);
function setResolution(ratio) {
var conWidth = ($(".c-container").css('width')).replace(/\D/g,'');
var conHeight = ($(".c-container").css('height')).replace(/\D/g,'');
var tempWidth = 0;
var tempHeight = 0;
tempHeight = conWidth / ratio;
tempWidth = conHeight * ratio;
if (tempHeight > conHeight) {
canvas.setWidth(tempWidth);
canvas.setHeight(conHeight);
} else {
canvas.setWidth(conWidth);
canvas.setHeight(tempHeight);
}
}
The x and y mouse coordinates work fine when zoomed in, but they don't line up with the returned image data. It seems as though the ctx isn't changing it's width and height and scaling along with the actual canvas size.
The canvas element is showing the correct width and height before using getContext as well.
Any ideas on a solution?
Feel free to check out the full scripts on the live website at: http://minimedit.com/
Try "fabric.devicePixelRatio" for calculating actual position, for example:
x = parseInt(mouse.x) * fabric.devicePixelRatio
I have been using RaphealJS to create a vector drawing tool, I have all the drawing completed and working
my issues comes in when I resize the browser window and try to draw the mouse pointer is off from the location that is being drawn.
I use the mouse move event on the browser and draw lines , Like so
$(document).mousemove(function(e){
if (IE) {
var dh = $("#details").height();
var dw = $("#details").width();
xx = e.offsetX;
yy = e.offsetY;
} else {
var offset = $("#workcanvas").offset();
xx = e.pageX - offset.left;
yy = e.pageY - offset.top;
}
if (lineObject != null) {
lineObject.updateEnd(xx, yy);
} else {
lineObject = Line(xx, yy, xx, yy, MasterCanvas);
}
});
I create my canvas and background image
var MasterCanvas = Raphael($("#workcanvas").attr("id"));
var MasterBGImage = MasterCanvas.image(imgPath, 0, 0, $("#workcanvas").width(),$("#workcanvas").height());
MasterCanvas.setViewBox(0, 0, $("#workcanvas").width(), $("#workcanvas").height(), true);
and in my window resize event I tried this
MasterCanvas.setSize($("#workcanvas").width(), $("#workcanvas").height());
Now I have beat my head against this for a few days to no avail. Please note: I can the drawing function work, and as long as the window does not resize every thing is great but when the page resizes the drawing point is off.
Just in case anyone else has this problem, it turns out to be a viewBox problem, I had to calculate the mouse position based on the viewBox coordinates not the screen so my original code becomes:
$(document).mousemove(function(e){
var uupos = MasterCanvas.canvas.createSVGPoint();
uupos.x = e.clientX;
uupos.y = e.clientY;
var ctm = MasterCanvas.canvas.getScreenCTM();
if (ctm = ctm.inverse())
uupos = uupos.matrixTransform(ctm);
x = uupos.x;
y = uupos.y;
if (lineObject != null) {
lineObject.updateEnd(x, y);
} else {
lineObject = Line(x, y, x, y, MasterCanvas);
}
});
Edit:
Looks like this solution is SVG only though and it does not work in IE8 which is a requirement for me - any ideas.
Is there something like viewBox coordinates in VML
I am using the HTML5 full_screen API to scale up this letter to full screen mode of the browser.
Follow https://bubbleideas.com/letters/html5-full_screen-api for the demo and steps to reproduce.
There seems a problem/bug with the way browser returns (x,y) value of pointer location of the mouse. In full_screen mode when you scroll down an offset is introduced between the mouse pointer and scribbled path.
Here are the steps to reproduce the issue. (go to above demo link)
Click on button on the right hand top of this page.
Click on "Free hand" drawing tool on the right bottom side. It will open up a stationery panel (Choose pen or pencil tool)
Scribble on the drawing area a couple of times
Now scroll down a bit and try to scribble with the same pen. You ll notice that there is gap/offset between mouse pointer position and scribbled path(this is the issue). Ideally, there should be no gap in the full screen mode either
Has someone been here before? Also note this works perfectly fine for other shapes like the square, circle and triangle without any offset.
UPDATE: (As asked by "Iftah" in the commment below)
As per fabric js I use calcOffset() which recalculates offset on every mouse down. As far as other functions are concerned we do some thing like this. Hopefully this gives some idea
$("#rectangle-function").click(function (evt1) {
doCanvasUp();
initObjectDrawing();
//canvas.isDrawingMode = true;
canvas1 = document.createElement("canvas");
canvas1.height = canvas.height;
canvas1.width = canvas.width;
canvas1.id = "dummy-canvas";
canvas1.style.zIndex = 998;
canvas1.style.position = "absolute";
$(".page-body").prepend(canvas1);
$("#dummy-canvas").mousedown(function (evt) {
var context1 = canvas1.getContext("2d");
var offset = $("#dummy-canvas").offset();
startX = evt.pageX - offset.left;
startY = evt.pageY - offset.top;
context1.beginPath();
$("#dummy-canvas").mousemove(function (event) {
context1.clearRect(0, 0, canvas1.width, canvas1.height);
context1.strokeStyle = "#ff0000";
context1.lineWidth = 1;
context1.moveTo(startX, startY);
var offset1 = $("#dummy-canvas").offset();
var x = event.pageX - offset1.left;
var y = event.pageY - offset1.top;
var diffX = x - startX;
var diffY = y - startY;
context1.strokeRect(startX, startY, diffX, diffY);
context1.closePath();
context1.beginPath();
}).mouseup(function (eventf) {
$("#dummy-canvas").unbind('mousemove');
$("#dummy-canvas").unbind('mouseup');
var offset = $("#dummy-canvas").offset();
//$("#dummy-canvas").remove();
context1.clearRect(0, 0, canvas1.width, canvas1.height);
var endX = eventf.pageX - offset.left;
var endY = eventf.pageY - offset.top;
var diffX = endX - startX;
var diffY = endY - startY;
var rect = new fabric.Rect({
left: startX + diffX * 0.5,
top: startY + diffY * 0.5,
width: diffX,
height: diffY,
opacity: 1,
fill: null,
stroke: color
});
canvas.add(rect);
});
});
Without looking at your code it is impossible to tell exactly where the error is...
One problem could be when reading the mouse coordinates, IE. You could be wrongly using event.pageY instead of event.clientY or a similar confusion (see What is the difference between screenX/Y, clientX/Y and pageX/Y?)
Or you can say the problem is how you use those coordinates,
ie. if you take the mouse event coordinates in screen space then before applying it on the canvas you need to subtract the canvas element screen offset and add the scrolling offset...
Since you have one tool that works and one that doesn't you can compare them and see where they differ.
I want to implement dragging of an image within a canvas. I want simplest code for that. So far I have seen a lot of examples but they have used complex ways of implementation. I want an example that is easy to learn and implement.
It's pretty difficult. You'll first need to write a function that can detect when you click a particular element. However, before we can do that, we must define what we mean by "element". Is it the product of a single draw instruction (e.g. a rectangle or arc), or something complex? (Imagine I wanted to draw a cat and make the entire cat draggable as a unit.)
A canvas is nothing but a collection of pixels. If you want your program to have an idea of "shapes" or even "collections of shapes treated as a unit" you'll need to implement them yourself as data structures external to the canvas itself. Once you have that, you can write an onmousedown handler that takes the x/y point clicked and determine what shape (if any) the click falls inside of (and if it falls inside of multiple shapes, check which has the foremost z-index). Then add an onmousemove handler that erases and redraws the shape on the canvas based on the information in the shape data object.
This is a moderately difficult problem with very difficult prerequisite problems (creating data structures that can describe a wide range of shapes as well as collections of shapes). I highly recommend you use a canvas drawing library that has already solved these problems. I use cake.js but there are loads of options available.
If you don't have to use the HTML5 canvas, jQuery UI is a lot simpler:
HTML:
<img class="drag-me" src="http://www.google.com/images/srpr/logo3w.png">
JavaScript:
$(function() {
$('.drag-me').draggable();
});
See it in action:
http://jsfiddle.net/flackend/TQzSe/
The jQuery UI API has a lot of options too to make it act how you want:
http://jqueryui.com/demos/draggable/
Plus, if it doesn't do what you need, it's easy to implement yourself. Post here if you need help with that.
jsfiddle.net/Zevan/QZejF/5 This may help you.
<html>
<head>
<title>Test Page</title>
<script type="text/javascript" src="jquery.js"></script>
</head>
<body>
<canvas id="c" width = "500" height = "500" ></canvas>
<script type="text/javascript">
var canvas = $("#c");
var c = canvas[0].getContext("2d");
//var path = "http://wonderfl.net/images/icon/e/ec/ec3c/ec3c37ba9594a7b47f1126b2561efd35df2251bfm";
var path = "blue.jpg";
var path2 = "purple.jpg";
var image1 = new DragImage(path, 200, 100);
var image2 = new DragImage(path2, 300, 100);
var loop = setInterval(function() {
c.fillStyle = "gray";
c.fillRect(0, 0, 500, 500);
image1.update();
image2.update();
}, 30);
var mouseX = 0,
mouseY = 0;
var mousePressed = false;
var dragging = false;
canvas.mousemove(function(e) {
mouseX = e.offsetX;
mouseY = e.offsetY;
})
$(document).mousedown(function() {
mousePressed = true;
}).mouseup(function() {
mousePressed = false;
dragging = false;
});
function DragImage(src, x, y) {
var that = this;
var startX = 0,
startY = 0;
var drag = false;
this.x = x;
this.y = y;
var img = new Image();
img.src = src;
this.update = function() {
if (mousePressed ) {
var left = that.x;
var right = that.x + img.width;
var top = that.y;
var bottom = that.y + img.height;
if (!drag) {
startX = mouseX - that.x;
startY = mouseY - that.y;
}
if (mouseX < right && mouseX > left && mouseY < bottom && mouseY > top) {
if (!dragging){
dragging = true;
drag = true;
}
}
} else {
drag = false;
}
if (drag) {
that.x = mouseX - startX;
that.y = mouseY - startY;
}
c.drawImage(img, that.x, that.y);
}
}
</script>
</body>
</html>
Note: I'm using Google Chrome
Currently I have this test page
http://www.evecakes.com/doodles/canvas_size_issue.htm
It should draw a rectangle right below the mouse cursor, but there are some really weird scaling issues which is causing it to draw at a varying offset. I think it's because the canvas coordinate space is not increasing along with its html size. Is there a way to increase that coordinate space size?
Here's the javascript function
$('#mapc').mousemove(function (event) {
var canvas = $('#mapc');
var ctx = canvas[0].getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(event.clientX, event.clientY, 55, 50);
document.title = event.clientX + " --- " + event.clientY;
});
Set the width and height HTML attributes of the canvas. Right now, it's assuming its default width and the CSS is just stretching it, and it appears as if it is scaling.
A side-note, you can use this in the mousemove event - it is a reference to #mapc element, so you won't have to query the DOM on every mouse move.
var offset = $('#mapc').offset();
$('#mapc').mousemove(function (event) {
var ctx = this.getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(event.pageX - offset.left, event.pageY - offset.top, 1, 1);
});