Easeljs: show pointer cursor when mouse is on shape's bounds - javascript

I am trying to achieve something similar to the code below. When user is on the edge of a rectangle, the cursor points a pointer, otherwise cursor is an arrow.
shape.graphics.beginStroke("#000").beginFill("#daa").drawRect(50, 150, 250, 250);
shape.on("mousemove", function(evt) {
if (isOnEdges(evt)) {
evt.target.cursor = "pointer";
} else {
evt.target.cursor = "arrow";
}
});
Problems with the above code is:
Shape doesn't have a mousemove handler
how do i calculate if mouse is on the edge of a shape (isOnEdges function)

You can simply set the cursor on the shape, and ensure that you enableMouseOver on the stage:
var shape = new Shape();
shape.graphics.beginStroke("#000").beginFill("#daa").drawRect(50, 150, 250, 250);
shape.cursor = "pointer";
stage.enableMouseOver();
EaselJS will automatically determine when you are over the shape's bounds.

I have a similar problem:
container.on("pressmove", function (evt) {
this.x = evt.stageX + this.offset.x;
this.y = evt.stageY + this.offset.y;
if (this.allowDrop(board)) {
this.cursor = "pointer";
}
else {
this.cursor = "no-drop";
}
});
This code doesn't change my cursor in runtime.. I update the stage with a ticker.. so this is not the problem.

Related

Add highlight or marker function to html canvas w/javascript

I'm working on making a nuclear physics webgame. I have created a nuclide chart on a canvas. I manually created every isotope and inputted their corresponding isotope name. A sample is below, as there are over 500 isotopes. I had to do this manually because the "grid" has to be in the form of the normal nuclide chart. The thing I need to do next is create some sort of function that will either highlight an isotope when its clicked on, or put a "marker" on the isotope when clicked on. And unhighlight or move the marker when a different isotope is clicked on. I've been at this for quite some time, but I can't figure it out. Does anyone know how I can achieve this?
// hydrogen
ctx.fillRect(21, 960, 25, 25);
ctx.fillRect(46, 960, 25, 25);
ctx.strokeRect(71, 960, 25, 25);
ctx.strokeRect(96, 960, 25, 25);
ctx.strokeRect(121, 960, 25, 25);
ctx.strokeRect(146, 960, 25, 25);
ctx.strokeRect(171, 960, 25, 25);
//
ctx.fillStyle = 'white';
ctx.fillText("1H", 23, 980, 15, 15);
ctx.fillStyle = 'white';
ctx.fillText("2H", 48, 980, 15, 15);
ctx.fillStyle = 'black';
ctx.fillText("3H", 73, 980, 15, 15);
ctx.fillStyle = 'black';
ctx.fillText("4H", 98, 980, 15, 15);
ctx.fillStyle = 'black';
ctx.fillText("5H", 123, 980, 15, 15);
ctx.fillStyle = 'black';
ctx.fillText("6H", 148, 980, 15, 15);
ctx.fillStyle = 'black';
ctx.fillText("7H", 173, 980, 15, 15);
Down here I've created a demo of how you would do such a thing like creating a hitbox. The hitbox will be represented by a square that has a random position on the canvas when the script runs.
In the script we use the mousedown and mouseup event listeners to determine when the user presses and releases the mouse. On mousedown the x and y coordinates of the mouse are calculated relative to the size of the canvas. By calculating it this way you can get the exact pixel on the canvas that has been clicked upon. It then stores this value in a global variable.
You'll need to know the x, y, width and height of the hitbox because you'll want to determine if the x and y of the click is within the square that is the hitbox.
In the demo below the view is re-rendered whenever you click and release the click. The mouseData state changes en will show that in the new render. The render then evaluates for the hitbox if the mouseData.position coordinates are inside of the box and changes the color based on that evaluation.
Then releasing the mouse triggers another re-render changing the clicked state to false and showing the original state of the hitbox.
So this is basically what it is, a square of coordinates which detects if the clicked pixel is inside that square and does something if it is.
const canvas = document.querySelector('#canvas');
const context = canvas.getContext('2d');
const squareSideSize = 50;
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// Stores data on the mouseclick.
let mouseData = {
clicked: false,
position: [] // Here we will store the x and y of click as [ x, y ]
}
/**
* Generates random number between min and max value.
* Just for the purpose of drawing the hitbox.
*/
const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
/**
* Returns an array with x and y coordinates
* based on the click event.
*/
const getMousePositionOnCanvas = ({ clientX, clientY }) => {
const canvasRect = canvas.getBoundingClientRect();
const x = clientX - canvasRect.left;
const y = clientY - canvasRect.top;
return [ x, y ];
};
/**
* Determines if the x and y from the click is within
* the x, y, width and height of the square.
* Returns true or false.
*/
const isInHitbox = ([ x, y ], square) => (
(x >= square.x && x <= square.x + square.width) &&
(y >= square.y && y <= square.y + square.height)
);
/**
* Create random square.
* This square has an x and y position as well
* as a width and height. The x and y are created
* to enchance the demonstration.
*/
const square = {
x: getRandomInt(0, canvas.width - squareSideSize),
y: getRandomInt(0, canvas.height - squareSideSize),
width: squareSideSize,
height: squareSideSize
};
// Listen for mousedown and update the click position.
canvas.addEventListener('mousedown', event => {
mouseData.clicked = true; // Set clicked state to true.
mouseData.position = getMousePositionOnCanvas(event); // Get the click position relative to the canvas.
render(); // Render with clicked state.
});
// Listen for mousemove and update the position.
canvas.addEventListener('mousemove', event => {
if (mouseData.clicked === true) { // Only act if clicked.
mouseData.position = getMousePositionOnCanvas(event); // Update mouse position.
render(); // Render with updated mouse position.
}
})
// Listen for mouseup and clear the click position.
canvas.addEventListener('mouseup', event => {
mouseData.clicked = false; // Set clicked state to false.
mouseData.position.length = 0; // Reset the positions.
render(); // Render unclicked state.
});
/**
* Render function which clears and draws on the canvas.
* Here we determine what to draw according the data we
* collection from the mouse.
*/
function render() {
requestAnimationFrame(() => {
const { x, y, width, height } = square; // Get all dimensions of the square.
const { clicked, position } = mouseData; // Get data of the mouse.
context.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas for a clean re-render.
// Default white color.
context.fillStyle = 'white';
// Check if the mouse has clicked and if the
// position is inside the hitbox. Change the color
// if a it is true.
if (clicked === true && isInHitbox(position, square)) {
context.fillStyle = 'green';
}
// Draw the hitbox.
context.fillRect(x, y, width, height);
});
}
// Initial render.
render();
html,
body {
width: 100%;
height: 100%;
margin: 0;
box-sizing: border-box;
}
body {
padding: 10px;
}
canvas {
width: 100%;
height: 100%;
background: #000000;
}
<canvas id="canvas"></canvas>
I created these functions to draw a circle onto my canvas and be able to track mouse position, test for if it's on the circle, and then listen for click down and up to be able to drag it. If there are other draw elements in the project, in this instance for me it's all 520 of my isotopes, you just need to put that code inside of your redraw function, or else everything else disappears after moving the cirlce
var cw=canvas.width;
var ch=canvas.height;
document.body.appendChild(canvas);
// used to calc canvas position relative to window
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(); }
canvas.onresize=function(e){ reOffset(); }
// save relevant information about shapes drawn on the canvas
var shapes=[];
// define one circle and save it in the shapes[] array
shapes.push( {x:300, y:275, radius:10} );
var isDragging=false;
var startX,startY;
// hold the index of the shape being dragged (if any)
var selectedShapeIndex;
// draw the shapes on the canvas
drawAll();
// listen for mouse events
canvas.onmousedown=handleMouseDown;
canvas.onmousemove=handleMouseMove;
canvas.onmouseup=handleMouseUp;
canvas.onmouseout=handleMouseOut;
// given mouse X & Y (mx & my) and shape object
// return true/false whether mouse is inside the shape
function isMouseInShape(mx,my,shape){
if(shape.radius){
// this is a circle
var dx=mx-shape.x;
var dy=my-shape.y;
// math test to see if mouse is inside circle
if(dx*dx+dy*dy<shape.radius*shape.radius){
// yes, mouse is inside this circle
return(true);
}
}
// the mouse isn't in any of the shapes
return(false);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// test mouse position against all shapes
// post result if mouse is in a shape
for(var i=0;i<shapes.length;i++){
if(isMouseInShape(startX,startY,shapes[i])){
// the mouse is inside this shape
// select this shape
selectedShapeIndex=i;
// set the isDragging flag
isDragging=true;
// and return (==stop looking for
// further shapes under the mouse)
return;
}
}
}
function handleMouseUp(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseOut(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// the drag is over -- clear the isDragging flag
isDragging=false;
}
function handleMouseMove(e){
// return if we're not dragging
if(!isDragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// how far has the mouse dragged from its previous mousemove position?
var dx=mouseX-startX;
var dy=mouseY-startY;
// move the selected shape by the drag distance
var selectedShape=shapes[selectedShapeIndex];
selectedShape.x+=dx;
selectedShape.y+=dy;
// clear the canvas and redraw all shapes
drawAll();
// update the starting drag position (== the current mouse position)
startX=mouseX;
startY=mouseY;
}
// clear the canvas and
// redraw all shapes in their current positions
function drawAll(){
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<shapes.length;i++){
var shape=shapes[i];
if(shape.radius){
// it's a circle
ctx.beginPath();
ctx.arc(shape.x,shape.y,shape.radius,0,Math.PI*2);
ctx.fillStyle=shape.color;
ctx.fill();
}
}

Paint text on the mouse hover on the graphics in the position of cursor

I have a problem when I go to paint the PIXI.Text in the cursor position.
This is the simple demo to reproduce the problem, when you go over the node with the cursor I paint the text, in this case, "#author vincenzopalazzo" but I want the position on the node, so I think for resolving the problem I have got the solution I must set the position of the mouse.
But I don't have an idea got get this position, so this is an example to reproduce the problem with PIXI
//setup Pixi renderer
var renderer = PIXI.autoDetectRenderer(600, 400, {
backgroundColor: 0x000000
});
document.body.appendChild(renderer.view);
// create new stage
var stage = new PIXI.Container();
// create helpful message
var style = {
font: '18px Courier, monospace',
fill: '#ffffff'
};
// create graphic object called circle then draw a circle on it
var circle = new PIXI.Graphics();
circle.lineStyle(5, 0xFFFFFF, 1);
circle.beginFill(0x0000FF, 1);
circle.drawCircle(150, 150, 100);
circle.endFill();
circle.alpha = 0.5;
stage.addChild(circle);
// designate circle as being interactive so it handles events
circle.interactive = true;
// create hit area, needed for interactivity
circle.hitArea = new PIXI.Circle(150, 150, 100);
// make circle non-transparent when mouse is over it
circle.mouseover = function(events) {
var message = new PIXI.Text('Hover your mouse over the circle to see the effect.', style);
message.x = events.clientX;
message.y = events.clientY;
circle.message = message;
circle.addChild(message);
}
// make circle half-transparent when mouse leaves
circle.mouseout = function(mouseData) {
this.alpha = 0.5;
circle.removeChild(circle.message);
delete circle.message;
}
// start animating
animate();
function animate() {
requestAnimationFrame(animate);
// render the root container
renderer.render(stage);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.4/pixi.js"></script>
This is my real code
module.exports = function (animatedNode, ctx) {
ctx.on('hover', function(animatedNode, ctx){
let x = animatedNode.pos.x;
let y = - animatedNode.pos.y / 2;
if(animatedNode.label === undefined){
animatedNode.label = new PIXI.Text('#author vincenzopalazzo', { fontFamily: "Arial", fontSize: "20px" , fill: 0x000000} );
animatedNode.label.x = x;
animatedNode.label.y = y + animatedNode.width/2;
ctx.addChild(animatedNode.label);
}else{
animatedNode.label.x = x;
animatedNode.label.y = y + animatedNode.width/2;
}
});
ctx.on('unhover', function(animatedNode, ctx){
ctx.removeChild(animatedNode.label);
delete animatedNode.label;
});
ctx.mouseover = function() {
console.debug('I\'call the hover events');
this.fire('hover', animatedNode, ctx);
}
ctx.mouseout = function() {
console.debug('I\'call the unhover events');
this.fire('unhover', animatedNode, ctx);
}
}
I'm using the ngraph.events on the ctx (it is the PIXI graphics) object, the method on and fire is the module nghraph.events
In your example code (first snippet) the "moseover" handler should be changed from:
// make circle non-transparent when mouse is over it
circle.mouseover = function(events) {
var message = new PIXI.Text('Hover your mouse over the circle to see the effect.', style);
message.x = events.clientX;
message.y = events.clientY;
circle.message = message;
circle.addChild(message);
}
to:
// make circle non-transparent when mouse is over it
circle.on('mouseover', function(event) {
// console.log('mouse is over the circle');
// console.log(event); // see in (for example in Chrome Devtools console) what is inside this variable
var message = new PIXI.Text('Hover your mouse over the circle to see the effect.', style);
// By looking at what "console.log(event)" shows we can see that instead of:
// message.x = events.clientX;
// message.y = events.clientY;
// should be:
message.x = event.data.global.x;
message.y = event.data.global.y;
circle.message = message;
circle.addChild(message);
});
To understand it more you can uncomment the "console.log" lines to observe it in your browser devtools console.
Then we also need to handle 'mouseover' event like this:
circle.on('mousemove',function (event) {
if (!circle.message) {
return;
}
var newPosition = event.data.getLocalPosition(this.parent);
circle.message.x = newPosition.x;
circle.message.y = newPosition.y;
});
so whole runnable example will be like this:
//setup Pixi renderer
var renderer = PIXI.autoDetectRenderer(600, 400, {
backgroundColor: 0x000000
});
document.body.appendChild(renderer.view);
// create new stage
var stage = new PIXI.Container();
// create helpful message
var style = {
font: '18px Courier, monospace',
fill: '#ffffff'
};
// create graphic object called circle then draw a circle on it
var circle = new PIXI.Graphics();
circle.lineStyle(5, 0xFFFFFF, 1);
circle.beginFill(0x0000FF, 1);
circle.drawCircle(150, 150, 100);
circle.endFill();
circle.alpha = 0.5;
stage.addChild(circle);
// designate circle as being interactive so it handles events
circle.interactive = true;
// create hit area, needed for interactivity
circle.hitArea = new PIXI.Circle(150, 150, 100);
// make circle non-transparent when mouse is over it
circle.on('mouseover', function(event) {
// console.log('mouse is over the circle');
// console.log(event); // see in (for example in Chrome Devtools console) what is inside this variable
var message = new PIXI.Text('Hover your mouse over the circle to see the effect.', style);
// By looking at what "console.log(event)" shows we can see that instead of:
// message.x = events.clientX;
// message.y = events.clientY;
// should be:
message.x = event.data.global.x;
message.y = event.data.global.y;
circle.message = message;
circle.addChild(message);
});
circle.on('mousemove',function (event) {
if (!circle.message) {
return;
}
var newPosition = event.data.getLocalPosition(this.parent);
circle.message.x = newPosition.x;
circle.message.y = newPosition.y;
});
// make circle half-transparent when mouse leaves
circle.mouseout = function(mouseData) {
this.alpha = 0.5;
circle.removeChild(circle.message);
delete circle.message;
}
// start animating
animate();
function animate() {
requestAnimationFrame(animate);
// render the root container
renderer.render(stage);
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.4/pixi.js"></script>
Please also see:
"Interaction/Dragging" Pixi.js demo (with source code): https://pixijs.io/examples/#/interaction/dragging.js
tutorial "Rotate towards mouse and shoot in that direction" by Igor Neuhold : http://proclive.io/shooting-tutorial/
Pixi.js API reference:
https://pixijs.download/dev/docs/PIXI.DisplayObject.html#event:mousemove
https://pixijs.download/dev/docs/PIXI.interaction.InteractionEvent.html
https://pixijs.download/dev/docs/PIXI.interaction.InteractionData.html

Unable to select objects after drawing in FabricJS

I am trying to free draw rectangle on canvas.
Here's my JSFiddle.
Code:
var canvas1 = new fabric.Canvas("canvas2");
var freeDrawing = true;
var divPos = {};
var offset = $("#canvas2").offset();
$(document).mousemove(function(e) {
divPos = {
left : e.pageX - offset.left,
top : e.pageY - offset.top
};
});
$('#2').click(function() {
console.log("Button 2 cilcked");
// Declaring the variables
var isMouseDown = false;
var refRect;
// Setting the mouse events
canvas1.on('mouse:down', function(event) {
// Defining the procedure
isMouseDown = true;
// Getting yhe mouse Co-ordinates
// Creating the rectangle object
if (freeDrawing) {
var rect = new fabric.Rect({
left : divPos.left,
top : divPos.top,
width : 0,
height : 0,
stroke : 'red',
strokeWidth : 3,
fill : ''
});
canvas1.add(rect);
refRect = rect; // **Reference of rectangle object
}
});
canvas1.on('mouse:move', function(event) {
// Defining the procedure
if (!isMouseDown) {
return;
}
// Getting yhe mouse Co-ordinates
if (freeDrawing) {
var posX = divPos.left;
var posY = divPos.top;
refRect.setWidth(Math.abs((posX - refRect.get('left'))));
refRect.setHeight(Math.abs((posY - refRect.get('top'))));
canvas1.renderAll();
}
});
canvas1.on('mouse:up', function() {
// alert("mouse up!");
isMouseDown = false;
// freeDrawing=false; // **Disables line drawing
});
});
The problem that I am facing is after drawing a rectangle I am unable to move, resize or at least select the drawn rectangle.
Mistake is you are not adding the object finally when mouse is up. Just change the mouse:up event function like this:
canvas1.on('mouse:up', function() {
// alert("mouse up!");
canvas1.add(refRect);
isMouseDown = false;
// freeDrawing=false; // **Disables line drawing
});
It will work fine. :)
I am also facing the same issue, thanks for the solution provided. If you noticed in this fiddle, duplicate object is creating when moving the shape.
How to solve the issue.
$(document).ready(function(){
//Getting the canvas
var canvas1 = new fabric.Canvas("canvas2");
var freeDrawing = true;
var divPos = {};
var offset = $("#canvas2").offset();
$(document).mousemove(function(e){
divPos = {
left: e.pageX - offset.left,
top: e.pageY - offset.top
};
});
$('#2').click(function(){
console.log("Button 2 cilcked");
//Declaring the variables
var isMouseDown=false;
var refRect;
//Setting the mouse events
canvas1.on('mouse:down',function(event){
//Defining the procedure
isMouseDown=true;
//Getting yhe mouse Co-ordinates
//Creating the rectangle object
if(freeDrawing) {
var rect=new fabric.Rect({
left:divPos.left,
top:divPos.top,
width:0,
height:0,
stroke:'red',
strokeWidth:3,
fill:''
});
canvas1.add(rect);
refRect=rect; //**Reference of rectangle object
}
});
canvas1.on('mouse:move', function(event){
// Defining the procedure
if(!isMouseDown)
{
return;
}
//Getting yhe mouse Co-ordinates
if(freeDrawing) {
var posX=divPos.left;
var posY=divPos.top;
refRect.setWidth(Math.abs((posX-refRect.get('left'))));
refRect.setHeight(Math.abs((posY-refRect.get('top'))));
canvas1.renderAll();
}
});
canvas1.on('mouse:up',function(){
//alert("mouse up!");
canvas1.add(refRect);
isMouseDown=false;
//freeDrawing=false; // **Disables line drawing
});
});
});
JS Fiddle Link here.
http://jsfiddle.net/PrakashS/8u1cqasa/75/
None of the other answer's implementations worked for me. All seem to be un compatible with latest fabric.js versions or have important issues.
So I implemented my own (check the JS in the HTML sources)
https://cancerberosgx.github.io/demos/misc/fabricRectangleFreeDrawing.html?d=9
supports two modes: one that will create and update a rectangle onmousedown and another that won't and only create the rectangle onmouseup
doesn't depend on any other library other than fabric.js (no jquery, no react no nothing)
Doesn't use offsets or event.clientX, etc to track the coordinates. It just uses fabric event.pointer API to compute it. This is safe and compatible since it doesn't depend on the DOM and just on a fabric.js public API
carefully manages event listeners
TODO:
I probably will also add throttle support for mousemove
Thanks

kineticjs rotate with mouse angle

I am trying to make an image object rotate in accordance to the mouse being clicked down on that image and the angle of the mouse to the center of the object.
Think of a unit circle and having the image being rotated about the circle based on where the mouse is on that circle.
I currently have
var stage = new Kinetic.Stage({
container: "container",
width: 800,
height: 500,
id: "myCanvas",
name: "myCanvas"
});
var layer = new Kinetic.Layer();
//gets the canvas context
var canvas = stage.getContainer();
var mousePosX;
var mousePosY;
var mouseStartAngle;
var selectedImage;
var mouseAngle;
var mouseStartAngle;
console.log(canvas)
var shiftPressed
window.addEventListener('keydown', function (e) {
if (e.keyCode == "16") {
shiftPressed = true;
}
console.log(shiftPressed)
}, true);
window.addEventListener('keyup', function (e) {
if (e.keyCode == "16") {
shiftPressed = false;
}
console.log(shiftPressed)
}, true);
function drawImage(imageObj) {
var dynamicImg = new Kinetic.Image({
image: imageObj,
x: stage.getWidth() / 2 - 200 / 2,
y: stage.getHeight() / 2 - 137 / 2,
width: 100, //imageObj.width,
height: 100, // imageObj.height,
draggable: true,
offset: [50,50] //[(imageObj.width/2), (imageObj.height/2)]
});
dynamicImg.on('mousedown', function () {
selectedImage = this;
console.log("x: " + this.getX())
console.log("y: " + this.getY())
var mouseStartXFromCenter = mousePosX - (this.getX() + (this.getWidth() / 2));
var mouseStartYFromCenter = mousePosY - (this.getY() + (this.getHeight() / 2));
mouseStartAngle = Math.atan2(mouseStartYFromCenter, mouseStartXFromCenter);
if (shiftPressed) {
//console.log("trying to switch draggable to false");
//console.log(this)
this.setDraggable(false);
}
});
dynamicImg.on('mouseup mouseout', function () {
//console.log('mouseup mouseout')
this.setDraggable(true);
});
dynamicImg.on('mouseover', function () {
document.body.style.cursor = 'pointer';
});
dynamicImg.on('mouseout', function () {
document.body.style.cursor = 'default';
});
imageArray.push(dynamicImg);
layer.add(dynamicImg);
stage.add(layer);
}
var imageObj = new Image();
imageObj.onload = function () {
drawImage(this);
};
imageObj.src = 'http://localhost:60145/Images/orderedList8.png';
function getMousePos(evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canvas.addEventListener('mouseup', function () {
selectedImage = undefined;
});
canvas.addEventListener('mousemove', function (evt) {
mousePos = getMousePos(evt);
//console.log('Mouse position: ' + mousePos.x + ',' + mousePos.y)
mousePosX = mousePos.x;
mousePosY = mousePos.y;
if (selectedImage != undefined) {
mouseXFromCenter = mousePosX - selectedImage.getX();
mouseYFromCenter = mousePosY - selectedImage.getY();
mouseAngle = Math.atan2(mouseYFromCenter, mouseXFromCenter);
var rotateAngle = mouseAngle - mouseStartAngle;
selectedImage.setRotation(rotateAngle);
}
}, false);
the code has a couple things to it.
-It should only allow a rotate if 'shift' is pressed and mousedown event happens on an image.
-it needs to maintain the dynamic image drawing as they will be populating the canvas dynamically over the life of the page.
here is a good example of something similar i want to happen, but just simply cannot get it to work in canvas/kineticjs.
http://jsfiddle.net/22Feh/5/
I would go simpler way using dragBoundFunc. http://jsfiddle.net/bighostkim/vqGmL/
dragBoundFunc: function (pos, evt) {
if (evt.shiftKey) {
var x = this.getX() - pos.x;
var y = this.getY() - pos.y;
var radian = Math.PI + Math.atan(y/x);
this.setRotation(radian);
return {
x: this.getX(),
y: this.getY()
}
} else {
return pos;
}
}
My mistake, I misread your question. You were essentially missing one line:
layer.draw();
Hold shift and move the mouse, you'll see it rotates nicely.
http://jsfiddle.net/WXHe6/2/
canvas.addEventListener('mousemove', function (evt) {
mousePos = getMousePos(evt);
//console.log('Mouse position: ' + mousePos.x + ',' + mousePos.y)
mousePosX = mousePos.x;
mousePosY = mousePos.y;
if (selectedImage != undefined) {
mouseXFromCenter = mousePosX - selectedImage.getX();
mouseYFromCenter = mousePosY - selectedImage.getY();
mouseAngle = Math.atan2(mouseYFromCenter, mouseXFromCenter);
var rotateAngle = mouseAngle - mouseStartAngle;
selectedImage.setRotation(rotateAngle);
layer.draw(); // <--------------- right here
}
}, false);
You should be more clear what your .on() events do. In your code, the mousedown on the shape doesn't do anything other than calculate an mouseStartAngle, but doesn't do anything. And your mouseUp on the shape event doesn't do much either. It's your browser/client mousedown/mouseup that do all the work, and that's why it rotates properly on some clicks and not others.
For setting the rotation you have many options, here are two:
Option one: set the radians manually on mousedown
dynamicImg.on('mousedown', function () {
dynamicImg.setRotation(mouseStartAngle); //set the rotation degree
layer.draw(); // redraw the layer
}
Option two: let kineticJS animate the rotation for you
dynamicImg.on('mousedown', function () {
dynamicImg.transitionTo({
duration: 1, // length of animation in seconds
rotation: mouseStartAngle // your angle, in radians
});
}
your updated jsfiddle: http://jsfiddle.net/WXHe6/1/
Here are some additional notes:
The reason you are having trouble is because your code is structured is a bit of a messy manner, sorry to say. You are mixing browser events with and adding listeners to the canvas rather than using built-in kineticJS functionality, for example, you could use stage.getUserPosition() to get the mouse coordinates. Unless you have some dire need to structure your project this way, try to avoid it.
What I do like is that you have created functions to break up your code, but on the down-side you have multiple functions doing the same thing, like calculating and updating the rotation angle. Why not just make one function to get the angle?
Some Tips:
Try using mainly KineticJS for functionality (I know you'll need event listeners for buttons like SHIFT). What I mean is, use things like stage.getUserPosition(); to get the mouse/touch coordinates, rather than evt.clientX, it's cleaner, and the work has been done for you already, no need to re-invent the wheel.
Make one function for setting/getting the rotation for a shape. Then you can call it whenever you want. (use less repetitive code)
If you don't want the shape to be draggable when shift is clicked, you should set that before an item is clicked. You have button listeners so you can either disable rotation for all shapes when shift is pressed down, or just for a shape on that is under the cursor.
imageArray.push(dynamicImg); not sure if you really need this, if you need to access all the elements as an array, you could alway do layer.getChildren(); to get all the images in the layer, or access them numerically like layer.getChildren()[0]; (in creation order)

Drawing at cursor position on canvas with JavaScript

I am trying to draw over a canvas by clicking and dragging the mouse. My problem is that, apart from the fact that the line has a very poor quality (I want a more pronounced border) it only respects the mouse position when this is at 0,0. As I move the mouse to the lower corner, the line increments its distance from it as much as when I am in the middle of the canvas, the line is already out of it.
I have my code at: http://jsfiddle.net/ajTkP/12/
I will also post it here:
var MDown = false;
var Color = 'blue';
var Canvas = document.getElementById('canvas');
var Context = Canvas.getContext('2d');
Canvas.onselectstart = function() { return false; };
Canvas.unselectable = "on";
Canvas.style.MozUserSelect = "none";
Canvas.onmousedown = function(e) {
MDown = true;
Context.strokeStyle = Color;
Context.lineWidth = 3;
Context.lineCap = 'round';
Context.beginPath();
Context.moveTo(e.pageX - Position(Canvas).left, e.pageY - 5);
}
Canvas.onmouseup = function() { MDown = false; };
Canvas.onmousemove = function(e) {
if (MDown) {
Context.lineTo(e.pageX - Position(Canvas).left, e.pageY - 5);
Context.stroke();
}
}
function Position(el) {
var position = {left: 0, top: 0};
if (el) {
if (!isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
position.left += el.offsetLeft;
position.top += el.offsetTop;
}
}
return position;
}
Thanks for your help!
You need to set an explicit width and height on the canvas. The default dimensions of a canvas are a width of 300 and a height of 150 (see the spec here). By setting the width and height via CSS you are just stretching the canvas.
Either do:
<canvas id="canvas" width="300" height="200"></canvas>
or set the width/height via JavaScript:
canvas.width = 300;
canvas.height = 200;
See the updated jsfiddle: http://jsfiddle.net/ajTkP/13/
It looks like jimr beat me to the punch about the canvas height and width.
The poor quality of the line though is due to how you're drawing the line. You'll notice that you're calling stroke() on every onmousemove event. Keep in mind that it's keeping track of the path of the line from when you beginPath() to when you closePath(), so you're basically stroking the same line multiple times (every time your mouse moves). This is what's giving you the aliased (blocky-looking) lines, instead of the smooth anti-aliased lines you're expecting.

Categories