HTML canvas (Draw with Mouse) mouse cursor point mismatch - javascript

I want to use Draw with Mouse on the canvas. The code is as follows:
// Stores the initial position of the cursor
let coord = {x:0 , y:0};
// // set a flag
let paint = false;
// Updates the coordianates of the cursor when an event e is triggered
function getPosition(event){
coord.x = event.clientX - canvas.offsetLeft;
coord.y = event.clientY - canvas.offsetTop;
}
// The following functions toggle the flag to start and stop drawing
function startPainting(event){
if (mouseID.checked){
myCanvas.style.cursor = "url('/paint.cur'),auto"
paint = true;
getPosition(event);
}
}
function stopPainting(){
paint = false;
}
function sketch(event){
if (!paint) return;
ctx.beginPath();
ctx.lineWidth = mouseline.value;
// set to a round shape.
ctx.lineCap = "round";
ctx.strokeStyle = bcolor.value;
// The cursor to start drawing
ctx.moveTo(coord.x, coord.y);
// The position of the cursor gets updated when move the mouse around.
getPosition(event);
ctx.lineTo(coord.x , coord.y);
// Draws the line.
ctx.stroke();
}
The problem is when I run it on my desktop the mouse is fine but on another computer, the mouse gets a mismatched position with the drawing. Can anyone help me with the issue?

Related

How often to redraw a javascript canvas to have a smooth animation effect

I am using React and javascript, I have a canvas with a 'player' square and an 'enemy' square. The player position is determined by the mouse coordinates, while I want the 'enemy' to have a pre determined path.
const Canvas = props => {
useEffect( () => {
setEnemy1X(enemy1X+1);
setEnemy1Y(enemy1Y+1);
},[tempGame])
useEffect(() =>{
//canvas variables
const canvas = canvasRef.current
const context = canvas.getContext('2d')
canvas.width = canvas.getBoundingClientRect().width
canvas.height = canvas.getBoundingClientRect().height
setCanvasWidth(canvas.width);
setCanvasHeight(canvas.height);
var player1 = { x: playerX, y: playerY, draggable: gameStatus }
var enemy1 = { x: enemy1X, y: enemy1Y, width: enemy3Width, height: enemy4Height}
// this function is running forever
const drawFun = () => {
context.clearRect(0,0, canvasWidth, canvasHeight)
context.beginPath();
context.fillStyle = '#00f4cc'
context.fillRect(player1.x, player1.y, playerWidth, playerHeight)
context.beginPath();
context.fillStyle = 'red'
context.fillRect(enemy1.x, enemy1.y, enemy1Width, enemy1Height)
requestAnimationFrame(drawFun);
}
drawFun();
//on mouse move
canvas.addEventListener('mousemove', e => {
e.preventDefault();
var rect = e.target.getBoundingClientRect();
var x = e.clientX - rect.left; //x position within the element.
var y = e.clientY - rect.top; //y position within the element.
//if game is running
if( gameStatus ){
setPlayerX(x - playerWidth/2);
setPlayerY(y - playerHeight/2);
The issue I am having right now is I am trying to update the X and Y coordinates of the red square by incrementing the values by 1 using setEnemy1X(enemy1X+1) and setEnemy1Y(enemy1Y+1) if I place this under mousemove event, the animation looks smooth and slow. If i try updating the coordinates within drawFun or the UseEffect at the beginning, the whole canvas flickers as if it has to be redrawn to fast.
To be more clear, I am trying to have that red square move diagonally across the screen by updating the X and Y coordinates. If I do this via a mousemove event, the animation looks nice and smooth, anywhere else I try to update the animation the whole canvas starts flickering because of what I'm assuming is the canvas being updated to many times a second. I tried to include everything I think is relevant to the question, any help greatly appreciated!

How to pan the canvas?

I have these event listeners in my code
canvas.addEventListener('mousemove', onMouseMove, false);
canvas.addEventListener('mousedown', onMouseDown,false);
canvas.addEventListener('mouseup', onMouseUp, false);
These functions will help me to pan the canvas. I have declared a variable in the onLoad called pan, isDown, mousePostion and previous mouse positions. Then in the initialise function is set the pan,mousePos and premousepos to vectors containing 0,0
function draw() {
context.translate(pan.getX(), pan.getY());
topPerson.draw(context);
console.log(pan);
}
function onMouseDown(event) {
var x = event.offsetX;
var y = event.offsetY;
var mousePosition = new vector(event.offsetX, event.offsetY);
previousMousePosition = mousePosition;
isDown = true;
console.log(previousMousePosition);
console.log("onmousedown" + "X coords: " + x + ", Y coords: " + y);
}
function onMouseUp(event) {
isDown = false;
}
function onMouseMove(event) {
if (isDown) {
console.log(event.offsetX);
mousePosition = new vector(event.offsetX, event.offsetY);
newMousePosition = mousePosition;
console.log('mouseMove' + newMousePosition);
var panX = newMousePosition.getX() - previousMousePosition.getX();
var panY = newMousePosition.getY() - previousMousePosition.getY();
console.log('onMouseMove: ' + panX);
pan = new vector(panX, panY);
console.log('mouseMove' + pan);
}
}
But it is not registering the new pan Values so you could attempt to drag the canvas. I know my mouse dragging events work but is just doesnt pan.
Here's a simple (annotated) example of panning code
It works by accumulating the net amount the mouse has been dragged horizontally (and vertically) Then it redraws everything, but offset by those accumulated horizontal & vertical distances.
Example code and a Demo:
// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// account for scrolling
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
// mouse drag related variables
var isDown=false;
var startX,startY;
// the accumulated horizontal(X) & vertical(Y) panning the user has done in total
var netPanningX=0;
var netPanningY=0;
// just for demo: display the accumulated panning
var $results=$('#results');
// draw the numbered horizontal & vertical reference lines
for(var x=0;x<100;x++){ ctx.fillText(x,x*20,ch/2); }
for(var y=-50;y<50;y++){ ctx.fillText(y,cw/2,y*20); }
// listen for mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calc the starting mouse X,Y for the drag
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// set the isDragging flag
isDown=true;
}
function handleMouseUp(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// clear the isDragging flag
isDown=false;
}
function handleMouseOut(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// clear the isDragging flag
isDown=false;
}
function handleMouseMove(e){
// only do this code if the mouse is being dragged
if(!isDown){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// dx & dy are the distance the mouse has moved since
// the last mousemove event
var dx=mouseX-startX;
var dy=mouseY-startY;
// reset the vars for next mousemove
startX=mouseX;
startY=mouseY;
// accumulate the net panning done
netPanningX+=dx;
netPanningY+=dy;
$results.text('Net change in panning: x:'+netPanningX+'px, y:'+netPanningY+'px');
// display the horizontal & vertical reference lines
// The horizontal line is offset leftward or rightward by netPanningX
// The vertical line is offset upward or downward by netPanningY
ctx.clearRect(0,0,cw,ch);
for(var x=-50;x<50;x++){ ctx.fillText(x,x*20+netPanningX,ch/2); }
for(var y=-50;y<50;y++){ ctx.fillText(y,cw/2,y*20+netPanningY); }
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4 id=results>Drag the mouse to see net panning in x,y directions</h4>
<canvas id="canvas" width=300 height=150></canvas>
To answer question
You have not provided some of the code. Specifically the vector object you are creating each event, could be there. (really you should not be creating a new object each time. Create once and update the values)
What I do see is that mouseMove events do not update the previous mouse position object so you will only get panning from the last mouse down. But you may want that. So without the code I don't know what is wrong as the code given is OK.
Below is how I do the whole shabang..
How to pan (and zoom).
Below is an example of panning and zooming with the mouse. Its a little more complex than standard pan and zooms, that is because I have added some smoothing to the pan and zoom to give it a better interactive feel.
How it works.
The canvas uses a transformation matrix to transform points. What this does is maintain that matrix. I call the transformed space, real space. I also maintain an inverse matrix, that is used to convert from screen space into real space.
The core of the demo is around the object displayTransform it holds the matrix, all the individual values needed, and the functions update() call once a frame, setHome() get the screen space transform and applies it to the canvas. Used to clear the screen. And setTransform() this set the canvas to real space (the zoomed panned space)
To smooth out movements I have a mirror of the values x, y, ox, oy, scale, and rotate. ((ox,oy) are origin x and y) (and yes rotate works) each of these variable has a delta prefixed with d and a chaser prefixed with c. The chaser values chase the required values. You should not touch the chaser values. There are two values called drag and accel (short for acceleration) drag (not real simulated drag) is how quickly the deltas decay. Values for drag > 0.5 will result in a bouncy response. As you get toward one it will get more and more bouncy. At 1 the bound will not stop, above one and it's unusable. 'accel' is how quickly the transform responds to mouse movement. Low values are slow response, 0 is no response at all, and one is instant response. Play with the values to find what you like.
Example of the logic for chaser values
var x = 100; // the value to be chased
var dx = 0; // the delta x or the change in x per frame
var cx = 0; // the chaser value. This value chases x;
var drag = 0.1; // quick decay
var accel = 0.9; // quick follpw
// logic
dx += (x-cx)*accel; // get acceleration towards x
dx *= drag; // apply the drag
cx += dx; // change chaser by delta x.
Convert coords
No point having a zoom panned rotated canvas if you don't know where things are. To do this I keep an inverse matrix. It converts screen x and y into realspace x and y. For convenience I convert the mouse to real space every update. If you want the reverse realSpace to screen space. then its just
var x; // real x coord (position in the zoom panned rotate space)
var y; // real y coord
// "this" is displayTransform
x -= this.cx;
y -= this.cy;
// screenX and screen Y are the screen coordinates.
screenX = (x * this.matrix[0] + y * this.matrix[2])+this.cox;
screenY = (x * this.matrix[1] + y * this.matrix[3])+this.coy;
You can see it at the end of the mouse displayTransform.update where I use the inverse transform to convert the mouse screen coords to real coords. Then in the main update loop I use the mouse real coords to display the help text. I leave it up to the user of the code to create a function that will convert any screen coord. (easy just pinch the bit where the mouse is being converted).
Zoom
The zoom is done with the mouse wheel. This presents a bit of a problem and naturally you expect the zoom to be centered on the mouse. But the transform is actually relative to the top left of the screen. To fix this I also keep an origin x and y. This basically floats about until the wheel zoom is needed then it is set to the mouse real position, and the mouse distance from the top left is placed in the transform x and y position. Then just increase or decrease the scale to zoom in and out. I have left the origin and offset to float (not set the chase values) this works for the current drag and acceleration setting but if you notice that it's not working that well with other setting set the the cx, cy, cox, coy values as well. ( I have added a note in the code)
Pan
Pan is done with the left mouse button. Click and drag to pan. This is straight forward. I get the difference between the last mouse position and the new one screen space (the coords given by the mouse events) This gives me a mouse delta vector. I transform the delta mouse vector into real space and subtract that from the top left coords displayTransform.x and displayTransform.y. Thats it I let the chaser x and y smooth it all out.
The snippet just displays a large image that can be panned and zoomed. I check for the complete flag rather than use onload. While the image is loading the snippet will just display loading. The main loop is refreshed with requestAnimationFrame, first I update the displayTransform then the canvas is cleared in home space (screen space) and then the image is displayed in real space. As always I a fighting time so will return as time permits to add more comments, and maybe a function or two.
If you find the chase variables a little to much, you can just remove them and replace all the c prefixed vars with the unprefixed ones.
OK hope this helps. Not done yet as need to clean up but need to do some real work for a bit.
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var mouse = {
x : 0,
y : 0,
w : 0,
alt : false,
shift : false,
ctrl : false,
buttonLastRaw : 0, // user modified value
buttonRaw : 0,
over : false,
buttons : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
};
function mouseMove(event) {
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if (mouse.x === undefined) {
mouse.x = event.clientX;
mouse.y = event.clientY;
}
mouse.alt = event.altKey;
mouse.shift = event.shiftKey;
mouse.ctrl = event.ctrlKey;
if (event.type === "mousedown") {
event.preventDefault()
mouse.buttonRaw |= mouse.buttons[event.which-1];
} else if (event.type === "mouseup") {
mouse.buttonRaw &= mouse.buttons[event.which + 2];
} else if (event.type === "mouseout") {
mouse.buttonRaw = 0;
mouse.over = false;
} else if (event.type === "mouseover") {
mouse.over = true;
} else if (event.type === "mousewheel") {
event.preventDefault()
mouse.w = event.wheelDelta;
} else if (event.type === "DOMMouseScroll") { // FF you pedantic doffus
mouse.w = -event.detail;
}
}
function setupMouse(e) {
e.addEventListener('mousemove', mouseMove);
e.addEventListener('mousedown', mouseMove);
e.addEventListener('mouseup', mouseMove);
e.addEventListener('mouseout', mouseMove);
e.addEventListener('mouseover', mouseMove);
e.addEventListener('mousewheel', mouseMove);
e.addEventListener('DOMMouseScroll', mouseMove); // fire fox
e.addEventListener("contextmenu", function (e) {
e.preventDefault();
}, false);
}
setupMouse(canvas);
// terms.
// Real space, real, r (prefix) refers to the transformed canvas space.
// c (prefix), chase is the value that chases a requiered value
var displayTransform = {
x:0,
y:0,
ox:0,
oy:0,
scale:1,
rotate:0,
cx:0, // chase values Hold the actual display
cy:0,
cox:0,
coy:0,
cscale:1,
crotate:0,
dx:0, // deltat values
dy:0,
dox:0,
doy:0,
dscale:1,
drotate:0,
drag:0.1, // drag for movements
accel:0.7, // acceleration
matrix:[0,0,0,0,0,0], // main matrix
invMatrix:[0,0,0,0,0,0], // invers matrix;
mouseX:0,
mouseY:0,
ctx:ctx,
setTransform:function(){
var m = this.matrix;
var i = 0;
this.ctx.setTransform(m[i++],m[i++],m[i++],m[i++],m[i++],m[i++]);
},
setHome:function(){
this.ctx.setTransform(1,0,0,1,0,0);
},
update:function(){
// smooth all movement out. drag and accel control how this moves
// acceleration
this.dx += (this.x-this.cx)*this.accel;
this.dy += (this.y-this.cy)*this.accel;
this.dox += (this.ox-this.cox)*this.accel;
this.doy += (this.oy-this.coy)*this.accel;
this.dscale += (this.scale-this.cscale)*this.accel;
this.drotate += (this.rotate-this.crotate)*this.accel;
// drag
this.dx *= this.drag;
this.dy *= this.drag;
this.dox *= this.drag;
this.doy *= this.drag;
this.dscale *= this.drag;
this.drotate *= this.drag;
// set the chase values. Chase chases the requiered values
this.cx += this.dx;
this.cy += this.dy;
this.cox += this.dox;
this.coy += this.doy;
this.cscale += this.dscale;
this.crotate += this.drotate;
// create the display matrix
this.matrix[0] = Math.cos(this.crotate)*this.cscale;
this.matrix[1] = Math.sin(this.crotate)*this.cscale;
this.matrix[2] = - this.matrix[1];
this.matrix[3] = this.matrix[0];
// set the coords relative to the origin
this.matrix[4] = -(this.cx * this.matrix[0] + this.cy * this.matrix[2])+this.cox;
this.matrix[5] = -(this.cx * this.matrix[1] + this.cy * this.matrix[3])+this.coy;
// create invers matrix
var det = (this.matrix[0] * this.matrix[3] - this.matrix[1] * this.matrix[2]);
this.invMatrix[0] = this.matrix[3] / det;
this.invMatrix[1] = - this.matrix[1] / det;
this.invMatrix[2] = - this.matrix[2] / det;
this.invMatrix[3] = this.matrix[0] / det;
// check for mouse. Do controls and get real position of mouse.
if(mouse !== undefined){ // if there is a mouse get the real cavas coordinates of the mouse
if(mouse.oldX !== undefined && (mouse.buttonRaw & 1)===1){ // check if panning (middle button)
var mdx = mouse.x-mouse.oldX; // get the mouse movement
var mdy = mouse.y-mouse.oldY;
// get the movement in real space
var mrx = (mdx * this.invMatrix[0] + mdy * this.invMatrix[2]);
var mry = (mdx * this.invMatrix[1] + mdy * this.invMatrix[3]);
this.x -= mrx;
this.y -= mry;
}
// do the zoom with mouse wheel
if(mouse.w !== undefined && mouse.w !== 0){
this.ox = mouse.x;
this.oy = mouse.y;
this.x = this.mouseX;
this.y = this.mouseY;
/* Special note from answer */
// comment out the following is you change drag and accel
// and the zoom does not feel right (lagging and not
// zooming around the mouse
/*
this.cox = mouse.x;
this.coy = mouse.y;
this.cx = this.mouseX;
this.cy = this.mouseY;
*/
if(mouse.w > 0){ // zoom in
this.scale *= 1.1;
mouse.w -= 20;
if(mouse.w < 0){
mouse.w = 0;
}
}
if(mouse.w < 0){ // zoom out
this.scale *= 1/1.1;
mouse.w += 20;
if(mouse.w > 0){
mouse.w = 0;
}
}
}
// get the real mouse position
var screenX = (mouse.x - this.cox);
var screenY = (mouse.y - this.coy);
this.mouseX = this.cx + (screenX * this.invMatrix[0] + screenY * this.invMatrix[2]);
this.mouseY = this.cy + (screenX * this.invMatrix[1] + screenY * this.invMatrix[3]);
mouse.rx = this.mouseX; // add the coordinates to the mouse. r is for real
mouse.ry = this.mouseY;
// save old mouse position
mouse.oldX = mouse.x;
mouse.oldY = mouse.y;
}
}
}
// image to show
var img = new Image();
img.src = "https://upload.wikimedia.org/wikipedia/commons/e/e5/Fiat_500_in_Emilia-Romagna.jpg"
// set up font
ctx.font = "14px verdana";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// timer for stuff
var timer =0;
function update(){
timer += 1; // update timere
// update the transform
displayTransform.update();
// set home transform to clear the screem
displayTransform.setHome();
ctx.clearRect(0,0,canvas.width,canvas.height);
// if the image loaded show it
if(img.complete){
displayTransform.setTransform();
ctx.drawImage(img,0,0);
ctx.fillStyle = "white";
if(Math.floor(timer/100)%2 === 0){
ctx.fillText("Left but to pan",mouse.rx,mouse.ry);
}else{
ctx.fillText("Wheel to zoom",mouse.rx,mouse.ry);
}
}else{
// waiting for image to load
displayTransform.setTransform();
ctx.fillText("Loading image...",100,100);
}
if(mouse.buttonRaw === 4){ // right click to return to homw
displayTransform.x = 0;
displayTransform.y = 0;
displayTransform.scale = 1;
displayTransform.rotate = 0;
displayTransform.ox = 0;
displayTransform.oy = 0;
}
// reaquest next frame
requestAnimationFrame(update);
}
update(); // start it happening
.canC { width:400px; height:400px;}
div {
font-size:x-small;
}
<div>Wait for image to load and use <b>left click</b> drag to pan, and <b>mouse wheel</b> to zoom in and out. <b>Right click</b> to return to home scale and pan. Image is 4000 by 2000 plus so give it time if you have a slow conection. Not the tha help text follows the mouse in real space. Image from wiki commons</div>
<canvas class="canC" id="canV" width=400 height=400></canvas>

how to change the origin position to rotate a drawed line in the HTML5 Canvas

I've a little problem with canvas
I'm trying to make a paddle game , I want to rotate the upper paddle according to the mouse X-position My problem is that the drawing position is the top left of the paddle as normal and I want to change it after Drawing to be in the center of the paddle for rotation.
so the origin position of the paddle will be the center and every time the mouse moved the paddle will be rotated from the center not the top left.
here is updated function which invoked to updated the canvas.
function update() {
// Update scores
updateScore();
// Move the paddles on mouse move
// Here we will add another condition to move the upper paddle in Y-Axis
if(mouse.x && mouse.y) {
for(var i = 1; i < paddles.length; i++) {
p = paddles[i];
// the botoom paddle
if (i ==1){
p.x = mouse.x - p.w/2;
}else{
// the top paddle
ctx.save(); // saves the coordinate system
ctx.translate(W/4,H/2); // now the position (0,0) is found at (250,50)
ctx.rotate(0.30 * mouse.x); // rotate around the start point of your line
ctx.moveTo(0,0) // this will actually be (250,50) in relation to the upper left corner
ctx.lineTo(W/4,H/2) // (250,250)
ctx.stroke();
ctx.restore(); // restores the coordinate system back to (0,0)
}// end else
}//end for
}
Your translations are a little off, but it's easy to fix. Consider this alternative -- translate the context to the center of the paddle. After all, this is where you will be doing the rotation. Rotate the canvas, and then draw a horizontal line centered around the origin. I've codified my suggestion, and I've stored a few things in local variables to make it clearer.
var W = 200;
var H = 200;
var x = W / 2;
var y = H / 2;
var lineLength = 80;
ctx.save();
ctx.translate(x, y);
ctx.rotate(0.3 * mouse.X);
ctx.moveTo(-lineLength / 2,0, 0);
ctx.lineTo(lineLength / 2.0, 0);
ctx.stroke();
ctx.restore();

Move HTML5 Canvas with a background image

I want to visualize a huge diagram that is drawn in a HTML5 canvas. As depicted below, let’s imagine the world map, it’s impossible to visualize it all at the same time with a “decent” detail. Therefore, in my canvas I would like to be able to pan over it using the mouse to see the other countries that are not visible.
Does anyone know how to implement this sort of panning in a HTML5 canvas? Another feature would be the zoom in and out.
I've seen a few examples but I couldn't get them working nor they seam to address my question.
Thanks in advance!
To achieve a panning functionality with a peep-hole it's simply a matter of two draw operations, one full and one clipped.
To get this result you can do the following (see full code here):
Setup variables:
var ctx = canvas.getContext('2d'),
ix = 0, iy = 0, /// image position
offsetX = 0, offsetY = 0, /// current offsets
deltaX, deltaY, /// deltas from mouse down
mouseDown = false, /// in mouse drag
img = null, /// background
rect, /// rect position
rectW = 200, rectH = 150; /// size of highlight area
Set up the main functions that you use to set size according to window size (including on resize):
/// calc canvas w/h in relation to window as well as
/// setting rectangle in center with the pre-defined
/// width and height
function setSize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
rect = [canvas.width * 0.5 - rectW * 0.5,
canvas.height * 0.5 - rectH * 0.5,
rectW, rectH]
update();
}
/// window resize so recalc canvas and rect
window.onresize = setSize;
The main function in this is the draw function. Here we draw the image on the position calculated by mouse moving (see next section).
First step to get that washed-out look is to set alpha down to about 0.2 (you could also draw a transparent rectangle on top but this is more efficient).
Then draw the complete image.
Reset alpha
Draw the peep-hole using clipping with corrected offsets for the source.
-
/// main draw
function update() {
if (img === null) return;
/// limit x/y as drawImage cannot draw with negative
/// offsets for clipping
if (ix + offsetX > rect[0]) ix = rect[0] - offsetX;
if (iy + offsetY > rect[1]) iy = rect[1] - offsetY;
/// clear background to clear off garbage
ctx.clearRect(0, 0, canvas.width, canvas.height);
/// make everything transparent
ctx.globalAlpha = 0.2;
/// draw complete background
ctx.drawImage(img, ix + offsetX, iy + offsetY);
/// reset alpha as we need opacity for next draw
ctx.globalAlpha = 1;
/// draw a clipped version of the background and
/// adjust for offset and image position
ctx.drawImage(img, -ix - offsetX + rect[0], /// sx
-iy - offsetY + rect[1], /// sy
rect[2], rect[3], /// sw/h
/// destination
rect[0], rect[1], rect[2], rect[3]);
/// make a nice sharp border by offsetting it half pixel
ctx.strokeRect(rect[0] + 0.5, rect[1] + 0.5, rect[2], rect[3]);
}
Now it's a matter of handling mouse down, move and up and calculate the offsets -
In the mouse down we store current mouse positions that we'll use for calculating deltas on mouse move:
canvas.onmousedown = function(e) {
/// don't do anything until we have an image
if (img === null) return;
/// correct mouse pos
var coords = getPos(e),
x = coords[0],
y = coords[1];
/// store current position to calc deltas
deltaX = x;
deltaY = y;
/// here we go..
mouseDown = true;
}
Here we use the deltas to avoid image jumping setting the corner to mouse position. The deltas are transferred as offsets to the update function:
canvas.onmousemove = function(e) {
/// in a drag?
if (mouseDown === true) {
var coords = getPos(e),
x = coords[0],
y = coords[1];
/// offset = current - original position
offsetX = x - deltaX;
offsetY = y - deltaY;
/// redraw what we have so far
update();
}
}
And finally on mouse up we make the offsets a permanent part of the image position:
document.onmouseup = function(e) {
/// was in a drag?
if (mouseDown === true) {
/// not any more!!!
mouseDown = false;
/// make image pos. permanent
ix += offsetX;
iy += offsetY;
/// so we need to reset offsets as well
offsetX = offsetY = 0;
}
}
For zooming the canvas I believe this is already answered in this post - you should be able to merge this with the answer given here:
Zoom Canvas to Mouse Cursor
To do something like you have requested, it is just a case of having 2 canvases, each with different z-index. one canvas smaller than the other and position set to the x and y of the mouse.
Then you just display on the small canvas the correct image based on the position of the x and y on the small canvas in relation to the larger canvas.
However your question is asking for a specific solution, which unless someone has done and they are willing to just dump their code, you're going to find it hard to get a complete answer. I hope it goes well though.

How can I make a html canvas "scroll" indefinitely?

I have a canvas element that automatically fills the entire browser window of the client when loaded. On it you can draw with the mouse, like in the result of any "make a drawing board"-tutorial out there. What I want to do however is to make it so that if you move the mouse to any extreme of the canvas (or maybe select a certain "move"-tool, you can drag the canvas in any direction you'd like), it scrolls. In particular, I want it to be possible to in theory scroll forever, so I can't really pre-generate, I have to generate "more canvas" on the fly. Does any one have any idea on how to do this?
If it helps, this is the client-side javascript right now: (the html is just a canvas-tag)
$(document).ready(function() {
init();
});
function init() {
var canvas = document.getElementById('canvas')
, ctx = canvas.getContext('2d')
, width = window.innerWidth
, height = window.innerHeight;
// Sets the canvas size to be the same as the browser size
canvas.width = width;
canvas.height = height;
// Binds mouse and touch events to functions
$(canvas).bind({
'mousedown': startDraw,
'mousemove': draw,
'mouseup': stopDraw,
});
};
// Triggered on mousedown, sets draw to true and updates X, Y values.
function startDraw(e) {
this.draw = true;
this.X = e.pageX;
this.Y = e.pageY;
};
// Triggered on mousemove, strokes a line between this.X/Y and e.pageX/Y
function draw(e) {
if(this.draw) {
with(ctx) {
beginPath();
lineWidth = 4;
lineCap = 'round';
moveTo(this.X, this.Y);
lineTo(e.pageX, e.pageY);
stroke();
}
this.X = e.pageX;
this.Y = e.pageY;
}
};
// Triggered on mouseup, sets draw to false
function stopDraw() {
this.draw = false;
};
The canvas element uses real memory of your computer, so there is no infinite canvas which scrolls forever. But, you may simulate this behavior using a virtual canvas. Just record the xy coords captured by draw() into an array and calculate a new center of the virtual canvas if the mouse touches the border. Then filter out the xy coords which fit into center +- screen size and draw them.
However, the array recording the xy coords can not grow infinitely and the filter code will get slower over the size of the array. Are 10,000 points enough?
More optimized code will turn the mouse coords into splines and saves only points needed to redraw the (smoothed) path of the mouse.

Categories

Resources