Background scale, keep position of drawn element in canvas - javascript

I use a system that combine a div background image and a canvas. This system let me appear the background like there is a light like this :
I permit to the user to zoom in/out the background. But i need to keep every drawn element positions. Today i have this :
After zoom in :
The wall height and width are correctly resize but its position move with the scale.
For the code i have this for the background :
document.getElementById("background").style.transform = "translate("+backgroundTransform.x+"px,"+backgroundTransform.y+"px) scale("+backgroundTransform.zoom+","+backgroundTransform.zoom+")";
And this for the drawn element :
this.x = function(x){
if(x != undefined)
this.position.x = x;
return this.position.x + backgroundTransform.x;
},
this.y = function(y){
if(y != undefined)
this.position.y = y;
return this.position.y + backgroundTransform.y;
},
this.width = function() {return this.baseWidth * this.globalZoom()};
this.height = function() {return this.baseHeight * this.globalZoom()};
If you need any informations please let me know.
Thanks.

I switch the div background with a canvas background. Now they can scale at the same time.

Related

How can I maintain accurate rectangle hover detection after scaling a canvas?

I am writing an application that draws rectangles on a HTML canvas using the fillRect function. I currently track the movement of the mouse and detect when the mouse pointer hovers over a rectangle to highlight it:
This is how I am currently detecting collision which works great.
//boxes2 is my array of rectangles
var l = boxes2.length;
for (var i = l - 1; i >= 0; i--) {
if (mouseX >= boxes2[i].x && mouseX <= (boxes2[i].x + boxes2[i].w ) &&
mouseY >= boxes2[i].y && mouseY <= (boxes2[i].y + boxes2[i].h )) {
selectedBoxNum = i;
}
}
My problem is that this hover detection no longer works well after zooming in/out as the actual bounds of the rectangles desync from their values in my rectangle array.
var currentZoomValue = 1;
function myOnMouseWheel(event) {
event.preventDefault();
// Normalize wheel to +1 or -1.
var wheel = event.wheelDelta / 120;
if (wheel == 1) {
zoom = 1.1;
}
else {
zoom = .9;
}
currentZoomValue = currentZoomValue * zoom
canvas.style.transform = "scale(" + currentZoomValue + ")";
}
What I have tried:
Scaling the values in the array as I zoom in/out so that the rectangle bounds will stay in sync
This will not work for me because the scale function is stretching my canvas to make the rectangles look bigger. If I also actually make them bigger, they will be doubly enlarged and outpace the zoom of my canvas background.
Compensating my hover detection based upon my current zoom level
I have tried something like:
if (mouseX >= boxes2[i].x && mouseX <= (boxes2[i].x + (boxes2[i].w * currentZoomValue) ) &&
mouseY >= boxes2[i].y && mouseY <= (boxes2[i].y + (boxes2[i].h * currentZoomValue) )) {
selectedBoxNum = i;
}
My attempts at this do not work because while the rectangle height and width do scale in an easily predictable way, the x,y coordinates do not. When zooming in, the rectangles will radiate out from the center so some rectangles will gain x value and other lose based upon their position. I also considered maintaining a second rectangle array that I could use just for hover detection but decided against it for this reason.
A good solution would be to actually scale the rectangle's sizes to give the illusion of zooming, but the rectangles positions on the background image is important, and this technique will not affect the background.
Since there is no standard way of knowing page zoom level, I would suggest catching the click event with an absolutely-positioned div.
You can get the offset of your canvas element with the getBoundingClientRect() method.
Then, the code would look something like this:
boxes2.forEach(function(box, i) {
cx.fillRect(box.x, box.y, box.w, box.h);
/* We create an empty div */
var div = document.createElement("div");
/* We get the position of the canvas */
var rect = canvas.getBoundingClientRect();
div.style.position = "absolute";
div.style.left = (rect.left + box.x) + "px"; //Don't forget the pixels!!!
div.style.top = (rect.top + box.y) + "px";
div.style.width = box.w + "px";
div.style.height = box.h + "px";
/* For demonstration purposes we display a border */
div.style.border = "1px dashed black"
div.onclick = function() {/* Your event handler */}
document.body.appendChild(div);
});
Here's a live demonstration. At least in my browser, regions stay consistent even if I zoom in and out the page.

How do I get the center of an Image in Javascript?

I'm making a guy in which I have objects to move, each objects has an image attached to it, but I have no idea how I could move them from the center and not the left upper corner.
This is the player:
function Player() {
this.height = 167.5;
this.width = 100;
this.pos_x = 350;
this.pos_y = 425;
this.player_image = new Image();
this.player_image.src = 'img/player_car_img.png';
};
And its method "Move":
Player.prototype.move = function(){
if (37 in keysDown) {
this.pos_x -= 10;
} else if (39 in keysDown) {
this.pos_x += 10;
}
};
I don't know where you're drawing this image, but I would use what you already have and simply draw the image in a different position.
You can draw the image around your position (with Player.pos_x and Player.pos_y as a central point, rather than top left) by taking off half the image dimensions from the initial position like so:
Y = Y location - (image height / 2)
X = X location - (image width / 2)
In actual code this would look something like:
var x = Player.pos_x - Player.player_image.width/2;
var y = Player.pos_y - Player.player_image.height/2;
ctx.drawImage(Player.player_image, x, y);
This way your Player.pos_x and Player.pos_y remain in the same position, but the image will be drawn "around" that center.

Why are the rectangles I am creating on this canvas not getting put in the right spot?

I am trying to create a simple page where you click and can create rectangles on a canvas. It takes the user's mouse clicks as input, and then creates a rectangle from the x and y of the click. However, it places the rectangle off to the side by some amount, and I am not sure why.
Fiddle: https://jsfiddle.net/2717s53h/
HTML
<canvas id="cnv"></canvas>
CSS
#cnv{
width:99vw;
height:98vh;
background-color:#faefbd;
}
JAVASCRIPT
$(function () {
var canvas = $('#cnv');
var canvObj = document.getElementById('cnv');
var ctx = canvObj.getContext('2d');
var point1 = {};
var point2 = {};
canvas.click(function (e) {
console.log(e);
var x = e.pageX;
var y = e.pageY;
console.log(x);
console.log(y);
if (Object.keys(point1).length == 0)
{
point1.x = x;
point1.y = y;
}
else if (Object.keys(point2).length == 0)
{
point2.x = x;
point2.y = y;
console.log(point1);
console.log(point2);
var width = point2.x - point1.x;
var height = point2.y - point1.y;
width = width < 0 ? width * -1 : width;
height = height < 0 ? height * -1 : height;
ctx.fillRect(x, y, 10, 10);
point1 = {};
point2 = {};
}
});
});
There is a difference between the CSS height/ width and the HTML canvas attributes height and width: the former defines the space the canvas occupies in the page; the latter defines the rendering surface. In concreto, suppose you have the following canvas:
<canvas height="400" width="600"></canvas>
with a viewport of a 1200x800 size and the canvas' CSS is set to width: 100%; height: 100%;, then your canvas will be rendered as stretched out twice as big and blurry in both height and width (like in your fiddle; clearly those rectangles are bigger than 10px). As a consequence, the page coordinates are not in sync with the canvas' coordinates.
As per the specification, your fiddle's canvas rendering surface is 300x150 because you didn't specify the width/height attributes:
The width attribute defaults to 300, and the height attribute defaults to 150.
See a slightly 'corrected' version of your fiddle.
As a result my advice (as a non-expert on HTML-canvas) would be to always specify those 2 attributes and not to mess with different rendering surface vs. display dimensions (certainly not relative ones like vw, vh, %, em, ...) if you don't want unpredictable results; although some SO users have been looking for a solution.

RaphaelJS : Stop drag when image out of frame

i have a RaphaelJS paper which has a set that contains an image and other elements. The Image is larger than the paper, and the user can drag it around.
However i want the drag to stop so that the image border is never visible, meaning that the user will not the the white margins. I have tried several ways but i keep getting weird results and i cant wrap my head around it.
I can get the top left corner of the image and the remaining corners of the image with calculation. Thank you for your time.
UPDATE : i have added a JSFiddle example
In the example i have added a condition that's supposed to keep the top left cornet outside the canvas, but as you can see there are a lot of glitches and bugs with this.
here is an image illustrating the desired result
Code :
Raphael.st.draggable = function(index) {
var me = this,
lx = 0,
ly = 0,
ox = 0,
oy = 0,
moveFnc = function(dx, dy) {
x = set.getBBox().x;
y = set.getBBox().y;
console.log(x+":"+y);
lx = dx + ox;
ly = dy + oy;
if(x+dx < 0 && y+dy < 0)//REMOVE THIS CONDITION FOR FREE DRAG
me.transform(',,320,240,'+'t' + lx + ',' + ly);
},
startFnc = function() {},
endFnc = function() {
ox = lx;
oy = ly;
};
this.drag(moveFnc, startFnc, endFnc);
};
width = 640;
height = 480;
paper = Raphael(cur_id, width, height),
image = paper.image('http://edwardtufte.com.s3.amazonaws.com/Thinking%20Eye/ParisMap.gif', 0, 0,950,805)
set = paper.set();
set.push(image);
paper.canvas.style.backgroundColor = "#FF0000"
set.draggable();
If I understand what you're looking for correctly, it looks like you're only missing the limiting part in your moveFnc drag event.
Something like this in place of your lx and ly assignments should limit the image to be scrollable only to the edges:
if (dx + ox <= 0){
if (dx + ox > width - image.attrs.width){
lx = dx + ox;
}
else{
lx = width - image.attrs.width;
}
}
if (dy + oy <= 0){
if (dy + oy > height - image.attrs.height){
ly = dy + oy;
}
else{
ly = height - image.attrs.height;
}
}
Positive translation will allow the image to drag to the left and down. If we limit the translation to negative values, it will stop at the left and top edges. For the right and bottom edges, we limit the negative translation to values bigger than the width of the paper minus the width of the image.

RaphealJS Resize and mouse shift

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

Categories

Resources