I am trying to draw a circle by clicking and dragging the mouse pointer. The way you would do in PowerPoint or something. The center of the circle is showing up in weird places and I can't explain it.
Here is the jsfiddle: https://jsfiddle.net/h8t3hfa2/2/
This is how I get the start and end position;
$('#c').mousedown(function(event) {
var parentOffset = $(this).offset();
circle = new Circle();
circle.start['x'] = event.pageX - parentOffset.left;
circle.start['y'] = event.pageY - parentOffset.top;
});
$('#c').mouseup(function(event) {
var parentOffset = $(this).offset();
circle.end['x'] = event.pageX - parentOffset.left;
circle.end['y'] = event.pageY - parentOffset.top;
circle.draw(canvas[0]);
});
When I console log the midpoint it looks correct but the circle shows up somewhere else. Any ideas?
It happens because you're scaling your canvas using CSS. Remember, canvas dimensions are different from canvas CSS (style) dimensions.
A quick fix is to equalize them:
canvas.get(0).width = canvas.width();
canvas.get(0).height = canvas.height();
https://jsfiddle.net/h8t3hfa2/3/
var Circle = function() {
this.start = [];
this.end = [];
}
Circle.prototype.draw = function(canvas) {
var me = this;
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
var mid = me.getcenter();
var rad = me.getradius();
console.log(mid, rad);
ctx.beginPath();
console.log(mid['x'], mid['y']);
ctx.arc(mid['x'], mid['y'], rad, 0, 360);
ctx.stroke();
}
};
Circle.prototype.getcenter = function() {
var me = this;
//Check the start and end are set
var centerX = (me.start['x'] + me.end['x']) / 2;
var centerY = (me.start['y'] + me.end['y']) / 2;
return {
'x': centerX,
'y': centerY
};
};
Circle.prototype.getradius = function() {
var me = this;
var distX = Math.abs(me.start['x'] - me.end['x']);
var distY = Math.abs(me.start['y'] - me.end['y']);
return distX / 2;
};
var circle;
var canvas = $('#c');
// added only these two lines
canvas.get(0).width = canvas.width();
canvas.get(0).height = canvas.height();
$('#c').mousedown(function(event) {
var parentOffset = $(this).offset();
circle = new Circle();
circle.start['x'] = event.pageX - parentOffset.left;
circle.start['y'] = event.pageY - parentOffset.top;
});
$('#c').mouseup(function(event) {
var parentOffset = $(this).offset();
circle.end['x'] = event.pageX - parentOffset.left;
circle.end['y'] = event.pageY - parentOffset.top;
circle.draw(canvas[0]);
});
canvas {background-color: white;height: 100%;width: 100%;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id='c'></canvas>
You should probably also add an onresize event handler to reset the canvas dimensions when the window size changes because you're using a fluid layout. Be careful, though, modifying either of the canvas dimensions (even to the original size) will cause the canvas to clear.
Removing height: 100%; width: 100%; from styles fixes avoids the problem.
Off-topic, but I suggest editing getradius to use the min (or max) of distX and distY (instead of hardcoding distX) like the applications you mentioned.
Circle.prototype.getradius = function() {
var me = this;
var distX = Math.abs(me.start['x'] - me.end['x'])/2;
var distY = Math.abs(me.start['y'] - me.end['y'])/2;
return Math.min(distX, distY);
};
Related
I am trying to create a paint like application using canvas. I am able to draw with free hand tool but when i try to using shapes we need to use clearrect method which i am not able to use properly. I need to draw multiple shapes on canvas with mouse but unable to do so.
Here is a link to what i am trying
http://jsfiddle.net/6vq64sdh/
HTML:
<canvas width="800px" height="600px" id="drawing"></canvas>
JS
var isDown;
var start;
var end;
var canvasEl = document.getElementById("drawing");
var draw = canvasEl.getContext("2d");
draw.lineWidth = "2";
draw.strokeStyle = "blue";
var lastWidth = 0;
var lastHeight = 0;
$("#drawing").mousedown(function(e) {
isDown = true;
start = getMousePos(canvasEl, e);
end = getMousePos(canvasEl, e);
lastWidth = 0;
lastHeight = 0;
e.preventDefault();
});
$("#drawing").mouseup(function() {
isDown = false;
});
$("#drawing").mousemove(function(e) {
if (!isDown) return;
var end = getMousePos(canvasEl, e);
var h = end.y - start.y;
var w = end.x - start.x;
draw.clearRect(start.x-5, start.y-5, lastWidth + 6, lastHeight + 6);
draw.beginPath();
draw.rect(start.x, start.y, w, h);
lastWidth = w;
lastHeight = h;
draw.stroke();
draw.closePath();
});
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: Math.floor(evt.clientX - rect.left),
y: Math.floor(evt.clientY - rect.top)
};
}
in above link able to draw multiple rectangles when i tried to drag mouse right side but unable to do when i drag it to left side. Could you please
I have just a little bit improved your code. It looks like you having some trouble when you trying to draw new rectangle over an already exists one.
I make a variable that will store the drawed rectangles. (Its like browser's brain) And the bottom of code, i created a setInterval() function that re-draw your rectangles that in the brain variable in 60fps.
var drawed_objects = []; //our brain variable
var isDown;
var start;
var end;
var canvasEl = document.getElementById("drawing");
var draw = canvasEl.getContext("2d");
draw.lineWidth = "2";
draw.strokeStyle = "blue";
var lastWidth = 0;
var lastHeight = 0;
$("#drawing").mousedown(function(e) {
isDown = true;
start = getMousePos(canvasEl, e);
end = getMousePos(canvasEl, e);
lastWidth = 0;
lastHeight = 0;
e.preventDefault();
});
$("#drawing").mouseup(function() {
drawed_objects.push({start:start,width:w,height:h});
isDown = false;
});
$("#drawing").mousemove(function(e) {
if (!isDown) return;
end = getMousePos(canvasEl, e);
h = end.y - start.y;
w = end.x - start.x;
draw.clearRect(start.x-5, start.y-5, lastWidth + 6, lastHeight + 6);
draw.beginPath();
draw.rect(start.x, start.y, w, h);
lastWidth = w;
lastHeight = h;
draw.stroke();
draw.closePath();
});
//in here we drawing old rectangles again again again..
//and all time clearing the canvas
setInterval(function(){
draw.clearRect(0,0,draw.canvas.width,draw.canvas.height);
for( let i=0; i <drawed_objects.length; i++ )
{
var obj = drawed_objects[i];
draw.beginPath();
draw.rect(obj.start.x, obj.start.y, obj.width, obj.height);
draw.stroke();
draw.closePath();
}
},1000/60);
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: Math.floor(evt.clientX - rect.left),
y: Math.floor(evt.clientY - rect.top)
};
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas width="800px" height="600px" id="drawing"></canvas>
I am trying to use Hammer.js to perform a pinch zoom with my PDF.js, however, it doesn't work, despite much tries. I didn't get any errors when I debug my codes on my mobile as well, but I just still can't pinch to zoom in and out. Please help if you know what went wrong, my codes are as shown below:
HTML:
<body>
<script type="text/javascript" src="reportviewer/hammer.js"></script>
<script>
var MIN_SCALE = 1; // 1=scaling when first loaded
var MAX_SCALE = 64;
// HammerJS fires "pinch" and "pan" events that are cumulative in nature and not
// deltas. Therefore, we need to store the "last" values of scale, x and y so that we can
// adjust the UI accordingly. It isn't until the "pinchend" and "panend" events are received
// that we can set the "last" values.
// Our "raw" coordinates are not scaled. This allows us to only have to modify our stored
// coordinates when the UI is updated. It also simplifies our calculations as these
// coordinates are without respect to the current scale.
var imgWidth = null;
var imgHeight = null;
var viewportWidth = null;
var viewportHeight = null;
var scale = null;
var lastScale = null;
var container = null;
var img = null;
var x = 0;
var lastX = 0;
var y = 0;
var lastY = 0;
var pinchCenter = null;
// We need to disable the following event handlers so that the browser doesn't try to
// automatically handle our image drag gestures.
var disableImgEventHandlers = function () {
var events = ['onclick', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover',
'onmouseup', 'ondblclick', 'onfocus', 'onblur'];
events.forEach(function (event) {
img[event] = function () {
return false;
};
});
};
// Traverse the DOM to calculate the absolute position of an element
var absolutePosition = function (el) {
var x = 0,
y = 0;
while (el !== null) {
x += el.offsetLeft;
y += el.offsetTop;
el = el.offsetParent;
}
return { x: x, y: y };
};
var restrictScale = function (scale) {
if (scale < MIN_SCALE) {
scale = MIN_SCALE;
} else if (scale > MAX_SCALE) {
scale = MAX_SCALE;
}
return scale;
};
var restrictRawPos = function (pos, viewportDim, imgDim) {
if (pos < viewportDim / scale - imgDim) { // too far left/up?
pos = viewportDim / scale - imgDim;
} else if (pos > 0) { // too far right/down?
pos = 0;
}
return pos;
};
var updateLastPos = function (deltaX, deltaY) {
lastX = x;
lastY = y;
};
var translate = function (deltaX, deltaY) {
// We restrict to the min of the viewport width/height or current width/height as the
// current width/height may be smaller than the viewport width/height
var newX = restrictRawPos(lastX + deltaX / scale,
Math.min(viewportWidth, curWidth), imgWidth);
x = newX;
img.style.marginLeft = Math.ceil(newX * scale) + 'px';
var newY = restrictRawPos(lastY + deltaY / scale,
Math.min(viewportHeight, curHeight), imgHeight);
y = newY;
img.style.marginTop = Math.ceil(newY * scale) + 'px';
};
var zoom = function (scaleBy) {
scale = restrictScale(lastScale * scaleBy);
curWidth = imgWidth * scale;
curHeight = imgHeight * scale;
img.style.width = Math.ceil(curWidth) + 'px';
img.style.height = Math.ceil(curHeight) + 'px';
// Adjust margins to make sure that we aren't out of bounds
translate(0, 0);
};
var rawCenter = function (e) {
var pos = absolutePosition(container);
// We need to account for the scroll position
var scrollLeft = window.pageXOffset ? window.pageXOffset : document.body.scrollLeft;
var scrollTop = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;
var zoomX = -x + (e.center.x - pos.x + scrollLeft) / scale;
var zoomY = -y + (e.center.y - pos.y + scrollTop) / scale;
return { x: zoomX, y: zoomY };
};
var updateLastScale = function () {
lastScale = scale;
};
var zoomAround = function (scaleBy, rawZoomX, rawZoomY, doNotUpdateLast) {
// Zoom
zoom(scaleBy);
// New raw center of viewport
var rawCenterX = -x + Math.min(viewportWidth, curWidth) / 2 / scale;
var rawCenterY = -y + Math.min(viewportHeight, curHeight) / 2 / scale;
// Delta
var deltaX = (rawCenterX - rawZoomX) * scale;
var deltaY = (rawCenterY - rawZoomY) * scale;
// Translate back to zoom center
translate(deltaX, deltaY);
if (!doNotUpdateLast) {
updateLastScale();
updateLastPos();
}
};
var zoomCenter = function (scaleBy) {
// Center of viewport
var zoomX = -x + Math.min(viewportWidth, curWidth) / 2 / scale;
var zoomY = -y + Math.min(viewportHeight, curHeight) / 2 / scale;
zoomAround(scaleBy, zoomX, zoomY);
};
var zoomIn = function () {
zoomCenter(2);
};
var zoomOut = function () {
zoomCenter(1 / 2);
};
var onLoad = function () {
img = document.getElementById('pinchzoom');
container = img.parentElement;
disableImgEventHandlers();
imgWidth = img.width;
imgHeight = img.height;
viewportWidth = img.parentElement.offsetWidth;
scale = viewportWidth / imgWidth;
lastScale = scale;
viewportHeight = img.parentElement.offsetHeight;
curWidth = imgWidth * scale;
curHeight = imgHeight * scale;
var hammer = new Hammer(container, {
domEvents: true
});
hammer.get('pinch').set({
enable: true
});
hammer.on('pan', function (e) {
translate(e.deltaX, e.deltaY);
});
hammer.on('panend', function (e) {
updateLastPos();
});
hammer.on('pinch', function (e) {
// We only calculate the pinch center on the first pinch event as we want the center to
// stay consistent during the entire pinch
if (pinchCenter === null) {
pinchCenter = rawCenter(e);
var offsetX = pinchCenter.x * scale - (-x * scale + Math.min(viewportWidth, curWidth) / 2);
var offsetY = pinchCenter.y * scale - (-y * scale + Math.min(viewportHeight, curHeight) / 2);
pinchCenterOffset = { x: offsetX, y: offsetY };
}
// When the user pinch zooms, she/he expects the pinch center to remain in the same
// relative location of the screen. To achieve this, the raw zoom center is calculated by
// first storing the pinch center and the scaled offset to the current center of the
// image. The new scale is then used to calculate the zoom center. This has the effect of
// actually translating the zoom center on each pinch zoom event.
var newScale = restrictScale(scale * e.scale);
var zoomX = pinchCenter.x * newScale - pinchCenterOffset.x;
var zoomY = pinchCenter.y * newScale - pinchCenterOffset.y;
var zoomCenter = { x: zoomX / newScale, y: zoomY / newScale };
zoomAround(e.scale, zoomCenter.x, zoomCenter.y, true);
});
hammer.on('pinchend', function (e) {
updateLastScale();
updateLastPos();
pinchCenter = null;
});
hammer.on('doubletap', function (e) {
var c = rawCenter(e);
zoomAround(2, c.x, c.y);
});
};
</script>
</body>
View:
<div data-options="dxView : { name: 'myViewer', title: 'My Viewer', disableCache: true } ">
<div data-options="dxContent : { targetPlaceholder: 'content' } " class="dx-content-background">
<div>
<iframe onload="onLoad()" data-bind="attr: { src: EmbedPDFLink }" class="viewReport"/>
</div>
</div>
not for hammer.js... but you can use (not-ready-for-production) SVG backend to render stuff into DOM so you don't need to deal with repainting canvas all the time yourself. Based on https://github.com/mozilla/pdf.js/blob/master/examples/components/pageviewer.html :
var url = "https://cdn.mozilla.net/pdfjs/tracemonkey.pdf";
var PAGE_TO_VIEW = 1;
var SCALE = 1.0;
var container = document.getElementById('container');
// Load document
PDFJS.getDocument(url).then(function (doc) {
return doc.getPage(PAGE_TO_VIEW).then(function (pdfPage) {
// Add div with page view.
var pdfPageView = new PDFJS.PDFPageView({
container: container,
renderer: 'svg',
id: PAGE_TO_VIEW,
scale: SCALE,
defaultViewport: pdfPage.getViewport(SCALE),
// We can enable text/annotations layers, if needed
textLayerFactory: new PDFJS.DefaultTextLayerFactory(),
annotationLayerFactory: new PDFJS.DefaultAnnotationLayerFactory()
});
// Associates the actual page with the view, and drawing it
pdfPageView.setPdfPage(pdfPage);
return pdfPageView.draw();
});
});
<link href="https://npmcdn.com/pdfjs-dist/web/pdf_viewer.css" rel="stylesheet"/>
<script src="https://npmcdn.com/pdfjs-dist/web/compatibility.js"></script>
<script src="https://npmcdn.com/pdfjs-dist/build/pdf.js"></script>
<script src="https://npmcdn.com/pdfjs-dist/web/pdf_viewer.js"></script>
<div id="container" class="pdfViewer singlePageView"></div>
The title might be misleading but that is the best I could come up with for a summary of my question.
Anyways, I need to figure out how to make a list, or a container, in this case a plain rectangle that contains a list of items, which can be dragged up and down in order to reveal other items in the container. In a way it would resemble a constrained div with a slider bar, but without the slider.
Now, I have an idea on using KonvaJS, former KineticJS to put all the items in the container in a group, and make the group draggable in certain directions, etc.
However the catch is that the sliding of the elements top or down should not only be on drag, but on flick also. So if you kind of flick your finger/mouse upwards the list would keep sliding by, until the end, where the speed would vary based on the flick intensity. If determining the flick intensity or speed is too complicated, then just any type of flick would need to slide the whole list to the bottom, or top.
So this should kind of resemble the standard vertical slide widgets you have on your android or ios. Now do you have any ideas on how I can proceed with this, or how would you go about this. Any ideas are welcome.
Working demo: http://jsbin.com/gefuvu/edit?js,output
Usual drag and drop is already supported by draggable property. For limit drag&drop to vertical scrolling I am using this simple dragBound:
const group = new Konva.Group({
draggable: true,
dragBoundFunc: (pos) => {
const minY = -group.getClientRect().height + stage.height();
const maxY = 0;
const y = Math.max(Math.min(pos.y, maxY), minY);
return {y, x: 0}
}
});
"Flick" implementation:
// setup flick
let lastY = null;
let dY = 0;
group.on('dragstart', () => {
lastY = group.y();
dy = 0;
});
group.on('dragmove', () => {
dy = lastY - group.y();
lastY = group.y();
});
group.on('dragend', () => {
// if last move is far way it means user move pointer very fast
// for this case we need to automatically "scroll" group
if (dy > 5) {
group.to({
y: -group.getClientRect().height + stage.height()
});
}
if (dy < -5) {
group.to({
y: 0
});
}
});
I guess that when you talk about "flick" you actually mean "scroll".
Edit : Missed the point of the question, also missed the [konvajs] tag. But here is a way to do it without any library, hoping it may help someone coming this way.
The simplest idea is to make two objects, a container and a content, each one with a canvas.
On mouse's wheel event, update the content position, then redraw its canvas to the container's one or if you need to handle drag, listen to the mousemove event, set a dragging flag to true, that you remove on mouseup. On mousemove update the position after you calculated the moving speed by checking the last event's timestamp and the new one's. Then on mouseup, start an animation that will decrease the speed of your movement :
// our container object
var container = {
width: window.innerWidth - 2,
height: window.innerHeight - 2,
top: 0,
left: 0,
canvas: document.getElementById('container'),
isOver: function(x, y) {
return (x >= this.left && x <= this.left + this.width &&
y >= this.top && y <= this.top + this.height);
},
};
// our content object
var content = {
width: container.width * 2,
height: container.height * 2,
top: 0,
left: 0,
background: 'rgba(0,255,0,.5)',
canvas: document.createElement('canvas'),
// set an init function to draw the texts
init: function() {
var ctx = this.ctx;
ctx.font = '20px sans-serif';
ctx.textBaseline = 'top';
ctx.fillText('Hello World', 0, 0);
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.fillText('Middle world', this.width / 2, this.height / 2);
ctx.textBaseline = 'bottom';
ctx.textAlign = 'left';
var textLength = ctx.measureText('Bye World').width;
ctx.fillText('Bye World', this.canvas.width - textLength, this.canvas.height);
ctx.fillStyle = this.background;
ctx.fillRect(0, 0, this.width, this.height);
},
};
// init the objects
var init = function(obj) {
var c = obj.canvas;
obj.ctx = c.getContext('2d');
c.width = obj.width;
c.height = obj.height;
if (obj.init) {
obj.init();
}
}
// our drawing function
var draw = function() {
container.ctx.clearRect(0, 0, container.width, container.height);
container.ctx.drawImage(content.canvas, content.left, content.top);
};
// update the content position
container.update = function(x, y) {
// if the content is smaller, we don't need to scroll
if (content.width > container.width) {
var maxX = Math.max(container.width, content.width);
var minX = Math.min(container.width, content.width);
content.left -= x;
// if we are at one end
if (content.left < minX - maxX) {
content.left = minX - maxX;
} // or another
else if (content.left > 0) {
content.left = 0;
}
}
if (content.height > container.height) {
var maxY = Math.max(container.height, content.height);
var minY = Math.min(container.height, content.height);
content.top -= y;
if (content.top < minY - maxY) {
content.top = minY - maxY;
} else if (content.top > 0) {
content.top = 0;
}
}
};
var drag = {
friction: .1,
sensibility: 18,
minSpeed: .01,
};
var mouseMove_Handler = function(e) {
// we're not dragging anything, stop here
if (!drag.dragged) {
return;
}
var rect = this.getBoundingClientRect();
var posX = e.clientX - rect.left;
var posY = e.clientY - rect.top;
// how long did it take since last event
var deltaTime = (e.timeStamp - drag.lastDragTime) / drag.sensibility;
// our moving speed
var deltaX = (drag.lastDragX - posX) / deltaTime;
var deltaY = (drag.lastDragY - posY) / deltaTime;
// update the drag object
drag.lastDragX = posX;
drag.lastDragY = posY;
drag.lastDeltaX = deltaX;
drag.lastDeltaY = deltaY;
drag.lastDragTime = e.timeStamp;
// update the container obj
drag.dragged.update(deltaX, deltaY);
// redraw
draw();
};
var mouseDown_Handler = function(e) {
// if we are sliding, stop it
if (drag.sliding) {
cancelAnimationFrame(drag.sliding);
drag.sliding = null;
}
var rect = this.getBoundingClientRect();
var posX = e.clientX - rect.left;
var posY = e.clientY - rect.top;
// first check that the event occurred on top of our container object
// we could loop through multiple ones
if (container.isOver(posX, posY)) {
// init our drag object
drag.dragged = container;
drag.lastDragX = posX;
drag.lastDragY = posY;
drag.lastDragTime = e.timeStamp;
}
};
var mouseUp_Handler = function(e) {
// store a ref of which object we were moving
var container = drag.dragged;
// we're not dragging anymore
drag.dragged = false;
var slide = function() {
// decrease the speed
drag.lastDeltaX /= 1 + drag.friction;
drag.lastDeltaY /= 1 + drag.friction;
// check that we are still out of our minimum speed
if (drag.lastDeltaX > drag.minSpeed || drag.lastDeltaY > drag.minSpeed ||
drag.lastDeltaX < -drag.minSpeed || drag.lastDeltaY < -drag.minSpeed) {
// store a reference of the animation
drag.sliding = requestAnimationFrame(slide);
} else {
drag.sliding = null;
drag.lastDeltaX = drag.lastDeltaY = 0;
}
container.update(drag.lastDeltaX, drag.lastDeltaY);
draw();
};
slide();
};
// add the wheel listener, for a polyfill check the MDN page :
// https://developer.mozilla.org/en-US/docs/Web/Events/wheel#Listening_to_this_event_across_browser
var mouseWheel_Handler = function(e) {
// get the position of our canvas element
var rect = this.getBoundingClientRect();
var posX = e.clientX - rect.left;
var posY = e.clientY - rect.top;
// first check that the event occurred on top of our container object
if (container.isOver(posX, posY)) {
// tell the browser we handle it
e.preventDefault();
e.stopPropagation();
// send the event's deltas
container.update(e.deltaX, e.deltaY);
// redraw
draw();
}
};
container.canvas.addEventListener('mousedown', mouseDown_Handler);
container.canvas.addEventListener('mousemove', mouseMove_Handler);
container.canvas.addEventListener('mouseup', mouseUp_Handler);
container.canvas.addEventListener('mouseleave', mouseUp_Handler);
container.canvas.addEventListener('wheel', mouseWheel_Handler);
// init the objects
init(container);
init(content);
// make a first draw
draw();
// Snippet only preventions \\
// avoid the outer window to scroll
window.onscroll = function(e) {
e.preventDefault();
e.stopPropagation()
};
// if you go in full page view
window.onresize = function() {
container.width = window.innerWidth;
container.height = window.innerHeight;
content.width = container.width * 2;
content.height = container.height * 2;
init(container);
init(content);
draw();
};
body,html,canvas {
margin: 0;
display: block
}
canvas {
border: 1px solid;
}
<canvas id="container"></canvas>
Let's say I have a canvas that is split into a 15x10 32-pixel checkboard. Thus, I have this:
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var tileSize = 32;
var xCoord
var yCoord
var tilesX = 15; // tiles across
var tilesY = 10; // tiles up and down
var counted = 1; // for drawing purpose for checkerboard for visual guidance
var mouseSel = new Image()
mouseSel.src = 'http://i.imgur.com/vAA03NB.png' // mouse selection
mouseSel.width = 32
mouseSel.height = 32
function isOdd(num) {
return num % 2;
}
function getMousePos(canvas, evt) {
// super simple stuff here
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
drawCanvas(); // upon intilization... draw
function drawCanvas() {
for (var y = 0; y <= 10; y++) {
for (var x = 0; x <= 15; x++) {
if (isOdd(counted)) {
context.fillStyle = '#dedede'
context.fillRect(x * 32, y * 32, 32, 32);
// checkboard drawn complete.
}
counted++;
} // end first foor loop
counted++;
} // end last for loop
if (counted >= 176) counted = 1 // once all tiles (16x11) are drawn... reset counter for next instance
}
canvas.addEventListener('mousemove', function (evt) {
context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas so mouse isn't stuck
drawCanvas(); // draw checkboard
// get the actual x,y position of 15x10 32-pixel checkboard
var mousePos = getMousePos(canvas, evt);
mousePos.xCoord = Math.floor(mousePos.x / tileSize)
mousePos.yCoord = Math.floor(mousePos.y / tileSize)
// draw the mouse selection
context.drawImage(mouseSel, (mousePos.xCoord * 32), (mousePos.yCoord * 32), 32, 32) // draw mouse selection
// debug
var message = ' (' + mousePos.xCoord + ',' + mousePos.yCoord + ') | (' + mousePos.x + ',' + mousePos.y + ')';
var textarea = document.getElementById('debug');
textarea.scrollTop = textarea.scrollHeight;
$('#debug').append(message + '\n');
}, false);
canvas#canvas {
background: #ABABAB;
position: relative;
z-index: 1;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" height="352" width="512" tabindex="0"></canvas>
<textarea name="" id="debug" cols="30" rows="35"></textarea>
**NOTE: ** Make sure to scroll down in that preview pane so you can see the debug textarea.
As you can see, the event of "drawing" fires EVERY single time it moves. That means every pixel.
I am trying to figure out how to make the drawing fire ONLY when a new x,y coord has changed. Because it'd be useless to redraw the mouse selection when it's only moved 5 pixels across and it's still going to be drawn at the same place.
My suggestion
Upon entering, have a temporary value and when that is passed, to redraw again?
Make a temporary value and update that if it was different from before. Then put the code in an if statement where either have changed.
var tempX, tempY;
var newX = 100;
var newY = 100;
tempX = mousePos.xCoord;
tempY = mousePos.yCoord;
if (newX !== tempX || newY !== tempY) {
// code here
}
if (tempX !== newX) newX = mousePos.xCoord;
if (tempY !== newY) newY = mousePos.yCoord;
JSFiddle: http://jsfiddle.net/weka/bvnma354/8/
i have a problem i'm implementing a crop custom rectangle on a canvas i made a function in Javascript that when the crop function is called the create on existing canvas a child and then with the JQuery listener i draw the rectangle.The childNode is create correctly while the listener don't work they don't get the events. This is my code:
var dragging = false;
var xstart = 0;
var ystart = 0;
var width = 0;
var height = 0;
var ctxotmp = null;
var ctxtmp = null;
var canvastmp = null;
var mycanvas = null;
function draw() {
ctxtmp.fillRect(xstart, ystart, width, height);
}
function init() {
mycanvas = $('#mycanvas')[0];
// create temp canvas
canvastmp = document.createElement('canvas');
canvastmp.id = "mycanvastmp";
canvastmp.width = mycanvas.width;
canvastmp.height = mycanvas.height;
mycanvas.parentNode.appendChild(canvastmp);
$("#mycanvastmp").css({position:"absolute",top:$("#mycanvas").css("top"),left:$("#mycanvas").css("left")});
canvastmp = $('#mycanvastmp')[0];
ctxtmp = canvastmp.getContext('2d');
ctxtmp.lineWidth = 1;
ctxtmp.fillStyle = "rgba(0, 0, 0, 0.5)";
}
//listener
$('#mycanvastmp').mousedown(function(e) {
var xoffs = $(this).offset().left;
var yoffs = $(this).offset().top;
xstart = e.pageX - xoffs;
ystart = e.pageY - yoffs;
dragging = true;
});
$('#mycanvastmp').mousemove(function(e) {
if(dragging) {
var xoffs = $(this).offset().left;
var yoffs = $(this).offset().top;
width = e.pageX - xoffs - xstart;
height = e.pageY - yoffs - ystart;
ctxtmp.clearRect(0, 0, $(this).width(), $(this).height());
draw();
}
});
$('#mycanvastmp').mouseup(function() {
dragging=false;
alert('The rectangle for crop (x, y, width, height): ' + xstart + ', ' + ystart + ', ' + width + ', ' + height);
});
Someone can help me?
Looks like you're attaching the events before the temp canvas is created.
Either attach the events in the init() function after adding the temp canvas to the DOM or use .delegate() or .on()
$("#mycanvas").on("mouseup", "#mycanvastmp", function() {
//...
});
You need to use .on on method in order to bind events to dynamically created objects. When your page initially loads and the dom fires, it doesn't see tempCanvas so it doesn't attach them initially.
//listener
$('body').on('mousedown' ,'#mycanvastmp', function(e) {
var xoffs = $(this).offset().left;
var yoffs = $(this).offset().top;
xstart = e.pageX - xoffs;
ystart = e.pageY - yoffs;
dragging = true;
});
$('body').on('mousemove' ,'#mycanvastmp', function(e) {
if(dragging) {
var xoffs = $(this).offset().left;
var yoffs = $(this).offset().top;
width = e.pageX - xoffs - xstart;
height = e.pageY - yoffs - ystart;
ctxtmp.clearRect(0, 0, $(this).width(), $(this).height());
draw();
}
});
$('body').on('mouseup' ,'#mycanvastmp', function(e) {
dragging=false;
alert('The rectangle for crop (x, y, width, height): ' + xstart + ', ' + ystart + ', ' + width + ', ' + height);
});
init();
Live Demo