I am using obelisk.js, which does not have a native way ( to my knowledge) of intersecting or raycasting for mouse picking, so I am trying to make my own methods, but I am not sure how to proceed.
Here's the code for a basic pen with a mouseover ( I want to detect when the mouse is on top of the cube):
Codepen sample
// Canvas & Scene Init:
var canvas = document.getElementById("canvas01");
var point = new obelisk.Point(210, 180);
var pixelView = new obelisk.PixelView(canvas, point);
// CUBE
var dimensionCube = new obelisk.CubeDimension(40, 40, 40);
var cubeColor = new obelisk.CubeColor();
var cube = new obelisk.Cube(dimensionCube, cubeColor);
var cubePos = new obelisk.Point3D(80, 80, 0);
pixelView.renderObject(cube, cubePos);
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
// Listener
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y ;
console.log(message);
}, false);
Thanks.
So after a bit of scratching my head I managed to add easel.js which has a handy hit area function and mouse event handling , the catch is that one has to crate a bitmap from the obelisk.js canvas and use that for the hit area without re-rendering.
I still need to figure out an efficient way of adding more than one primitive, here's the code so far:
And of course if you have a better,different or more native way that would be great as well:
// Canvas & Scene Init:
var canvas = document.getElementById("canvas01");
var ctx = canvas.getContext("2d");
var point = new obelisk.Point(210, 180);
var pixelView = new obelisk.PixelView(canvas, point);
var dimensionCube = new obelisk.CubeDimension(40, 40, 40);
var cubeColor = new obelisk.CubeColor();
var cube = new obelisk.Cube(dimensionCube, cubeColor);
var cubePos = new obelisk.Point3D(80, 80, 0);
pixelView.renderObject(cube, cubePos);
// Easel.JS glob:
var stage = new createjs.Stage("canvas01");
init();
function init() {
stage.enableMouseOver(10);
// we get the rendered canvas
var dataUrl = canvas.toDataURL();
// make a bitmap
var bitmap = new createjs.Bitmap(dataUrl);
// You could also add a hit area here
stage.addChild(bitmap);
// and the event handling we are after:
bitmap.on("mouseover", handleInteraction);
bitmap.on("mouseout", handleInteraction);
}
function handleInteraction(event) {
if (event.type === "mouseover") {
dimensionCube = new obelisk.CubeDimension(40, 40, 120);
cube = new obelisk.Cube(dimensionCube, cubeColor);
pixelView.clear();
pixelView.renderObject(cube, cubePos);
console.log("over");
} else {
dimensionCube = new obelisk.CubeDimension(40, 40, 40);
cube = new obelisk.Cube(dimensionCube, cubeColor);
pixelView.clear();
pixelView.renderObject(cube, cubePos);
console.log("out");
}
}
Pen: Obelisk.js + Easel.js basic Mouseover
Related
I have a bar drew in Canvas.
When user clicks anywhere in the bar I want to get the start position. Then, the user drags the mouse to any position and releases the mouse and I would get the last position.
I've been doing that for some time but I couldn't get the right events.
Here's my code.
<canvas id="demoCanvas" width="500" height="300"></canvas>
var stage = new createjs.Stage("demoCanvas");
var rect = new createjs.Shape();
rect.graphics.beginFill("#000").drawRect(0, 20, 200, 50);
rect.on('mousedown', function (mousedownEvent) {
var startX = mousedownEvent.rawX;
console.log('mousedown');
});
rect.on('mouseup', function(mouseupEvent) {
var stopX = mouseupEvent.rawX;
console.log('mouseup');
console.log(stopX);
});
stage.addChild(rect);
stage.update();
http://jsfiddle.net/noppanit/x0bdq3aa/
The event you are looking for is 'pressup', you may also need 'mouseleave'and 'mouseout'.
Strangely, this easeljs tutorial explicitly says that you can use the 'mouseup' event as you just did.
However, when you look to the docs about events attached to the Stage class and the ones attached to the DisplayObject class, there is no mention of this 'mouseup'.
About the 'pressup' event :
After a mousedown occurs on a display object, a pressup event will be generated on that object when that mouse press is released. This can be useful for dragging and similar operations.
var stage = new createjs.Stage("demoCanvas");
var rect = new createjs.Shape();
rect.graphics.beginFill("#000").drawRect(0, 20, 200, 50);
rect.on('mousedown', function (mousedownEvent) {
var startX = mousedownEvent.rawX;
snippet.log('mousedown');
});
rect.on('pressup', function(mouseupEvent) {
var stopX = mouseupEvent.rawX;
snippet.log('mouseup');
snippet.log(stopX);
});
stage.addChild(rect);
stage.update();
<script src="https://code.createjs.com/easeljs-0.8.1.min.js"></script>
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
<canvas id="demoCanvas" width="500" height="70"></canvas>
Check this code once, Here you will get x and y position continously.
function writeMessage(canvas, message) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.font = '18pt Calibri';
context.fillStyle = 'white';
context.fillText(message, 10, 25);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var canvas = document.getElementById('demoCanvas');
var context = canvas.getContext('2d');
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
writeMessage(canvas, message);
}, false);
check this fiddle-> http://jsfiddle.net/x0bdq3aa/3/
This is to get the mouse coordinates relative to Canvas,getMousePos() method which returns the mouse coordinates based on the position of the client mouse and the position of the canvas obtained from the getBoundingClientRect() method of the window object.
i have been having trouble with reading a mouse position on a canvas. The code is working (semi) correctly as it reads the position when clicking he canvas in IE but only on one frame, in chrome it is just displaying the value as 0.
Here is the full code:
<script>
var blip = new Audio("blip.mp3");
blip.load();
var levelUp = new Audio("levelUp.mp3");
levelUp.load();
var canvas = document.getElementById('game');
var context = canvas.getContext('2d');
context.font = '18pt Calibri';
context.fillStyle = 'white';
//load and draw background image
var bgReady = false;
var background = new Image();
background.src = 'images/background.jpg';
background.onload = function(){
bgReady = true;
}
var startMessage = 'Click the canvas to start';
//load plane image
var planeReady = false;
var planeImage = new Image();
planeImage.src = 'images/plane.png';
planeImage.onload = function() {
planeReady = true;
}
//load missile image
var missileReady = false;
var missileImage = new Image();
missileImage.src = 'images/missile-flipped.gif';
missileImage.onload = function() {
missileReady = true;
}
//initialise lives and score
var score = 0;
var lives = 3;
var missilesLaunched = 0;
var missileSpeed = 5;
var level = 1;
var missileX = 960;
var missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
function getMousePos(canvas, event) {
return {
x: input.x - rect.left,
y: input.y - rect.top
};
}
function update_images(event) {
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
planeImage.y = pos.y;
missileX = missileX - missileSpeed;
if (missileX < - 70) {
missilesLaunched++;
missileX = 960;
missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
blip.play();
score = missilesLaunched;
if (score % 5 == 0) {
missileSpeed = missileSpeed + 2;
level++;
levelUp.play();
}
}
}
function reload_images() {
if (bgReady = true) {
context.drawImage(background, 0, 0);
}
if (planeReady = true) {
context.drawImage(planeImage, 10, planeImage.y);
}
if (missileReady = true) {
context.drawImage(missileImage, missileX, missileY);
}
context.fillText('Lives: ' + lives, 200, 30);
context.fillText('Score: ' + score, 650, 30);
context.fillText('Level: ' + missileSpeed, 420, 30);
context.fillText('Position: ' + missileImage.y, 420, 70);
}
function main(event) {
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
update_images(event);
reload_images();
if (lives > 0) {
window.requestAnimationFrame(main);
}
else {
}
}
function start() {
context.drawImage(background, 0, 0);
context.fillText('Click the canvas to start', 350, 250);
function startMain(event) {
game.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("mousedown", startMain);
}
start();
</script>
Joe, you should actually be capturing the mouse position every time you click...
...but you're actually also starting a new game (without stopping the old one), every time you click, too.
First problem: starting game engine several times to draw on the same instance of the canvas
Solution:
In your start function, you need to remove the mousedown event listener, after you've triggered it.
function start () {
// ... other setup
function startMain (event) {
canvas.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("click", startMain);
}
Now it will only listen for the first click, before starting, and will only start once.
Second Problem: mouse doesn't update as expected
Solution: two issues here...
...first, you are passing event into main on first call...
...after that, you're passing main into requestAnimationFrame.
requestAnimationFrame won't call it with an event, it will call it with the number of microseconds (or ms or some other unit as a fractional precision of ms) since the page was loaded.
So the first time you got main({ type: "mousedown", ... });.
The next time you get main(4378.002358007);
So lets refactor the startMain we had above, so that main never ever collects an event, just a time.
function startMain ( ) {
canvas.removeEventListener("click", startMain);
requestAnimationFrame(main);
}
The next problem is that even if you were getting just events, you're only ever capturing a click event (which as we mentioned earlier, fires a new copy of the game logic).
Your solution is to separate the code which catches mouse events from the code which reads mouse position.
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
function getMousePos (rect, input) {
return {
x : input.x - rect.left,
y : input.y - rect.top
};
}
// currently in updateImages (should not be there, but... a different story)
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
You've got other problems, too...
You're calling getMousePos and passing in game at the moment. I don't see where game is defined in your JS, so either you're making game somewhere else (begging for bugs), or it's undefined, and your app blows up right there.
You should really be building this with your console / dev-tools open, in a hands-on fashion, and cleaning bugs in each section, as you go.
I am developing a canvas paint but I want to have an eraser there. So I use this lines to erase de content but when I click is clear the whole canvas.
//undo tool
var undo = new createjs.Bitmap(app.loader.getResult('undo'));
undo.name = 'undo';
undo.x = brush.x + 90;
undo.y = brush.y;
undo.addEventListener('click', this.undoHandler);
this.toolsContainer.addChild(undo);
//trash tool
var clear = new createjs.Bitmap(app.loader.getResult('clear'));
clear.name = 'clear';
clear.x = undo.x + 90;
clear.y = undo.y;
clear.addEventListener('click', this.clearHandler);
this.toolsContainer.addChild(clear);
undoHandler:function(){
if(tools.undoArray.length){
var lastItem = tools.undoArray.pop();
app.container.removeChild(lastItem);
var lastItem2 = tools.undoArray2.pop();
app.container.removeChild(lastItem2);
var lastItem3 = tools.undoArray3.pop();
app.container.removeChild(lastItem3);
app.stage.update();
}
},
clearHandler:function(){
app.container.removeAllChildren();
app.container.updateCache(clearhandler?"destination-out":"source-over");;
app.stage.update();
},
I trying to develop something like this erase
http://jsfiddle.net/lannymcnie/ZNYPD/
any idea?
well here http://jsfiddle.net/lannymcnie/ZNYPD/, the key is this:
wrapper.updateCache(erase?"destination-out":"source-over");
so...
var stage, wrapper,erase;
function init() {
var stage = new createjs.Stage("canvas");
createjs.Ticker.addEventListener("tick", stage);
// Add some text to draw on top of (also instructions)
stage.addChild(new createjs.Text("Click and Drag to Draw", "40px Arial", "#000000").set({x:200,y:200}));
// Set up the container. We use it to draw in, and also to get mouse events.
var wrapper = new createjs.Container();
wrapper.hitArea = new createjs.Shape(new createjs.Graphics().f("#000").dr(0,0,800,600));
wrapper.cache(0,0,800,600); // Cache it.
stage.addChild(wrapper);
// Create the shape to draw into
var drawing = new createjs.Shape();
wrapper.addChild(drawing);
var lastPoint = new createjs.Point();
wrapper.addEventListener("mousedown", function(event) {
// Store the position. We have to do this because we clear the graphics later.
lastPoint.x = event.stageX;
lastPoint.y = event.stageY;
erase = Math.floor(Math.random()*2);
wrapper.addEventListener("pressmove", function(event){
// Draw a round line from the last position to the current one.
drawing.graphics.ss(20, "round").s("#ff0000");
drawing.graphics.mt(lastPoint.x, lastPoint.y);
drawing.graphics.lt(event.stageX, event.stageY);
// Update the last position for next move.
lastPoint.x = event.stageX;
lastPoint.y = event.stageY;
// Draw onto the canvas, and then update the container cache.
wrapper.updateCache(erase==1?"destination-out":"source-over");
drawing.graphics.clear();
});
// Listen for mousemove
});
}
$(function(){
init();
})
the only difference is that the drawing is based on a random from 0 to 1 because in my example the erase gets these values from here erase = Math.floor(Math.random()*2);
I have achieved this by maintaining a array of mid points and using the globalCompositeOperation as destination-out and source-over to make a transparent eraser trail .
Following is the code that you need to use with a mouse move function
`var handleMouseMove = function (event) {
midPt = new createjs.Point(oldPt.x + stage.mouseX>>1, oldPt.y+stage.mouseY>>1);
if(curTool.type=="eraser"){
var tempcanvas = document.getElementById('drawcanvas');
var tempctx=tempcanvas.getContext("2d");
tempctx.beginPath();
tempctx.globalCompositeOperation = "destination-out";
tempctx.arc(midPt.x, midPt.y, 20, 0, Math.PI * 2, false);
tempctx.fill();
tempctx.closePath();
tempctx.globalCompositeOperation = "source-over";
drawingCanvas.graphics.clear();
// keep updating the array for points
arrMidPtx.push(midPt.x);
arrMidPty.push(midPt.y);
stage.addChild(drawingCanvas);
stage.update();
}
else if ( curTool.type=="pen"){
drawingCanvas.graphics.clear().setStrokeStyle(stroke, 'round', 'round').beginStroke(color).moveTo(midPt.x, midPt.y).curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
arrMidPtx.push(midPt.x);
arrMidPty.push(midPt.y);
arrOldPtx.push(oldPt.x);
arrOldPty.push(oldPt.y);
arrOldMidPtx.push(oldMidPt.x);
arrOldMidPty.push(oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPt.x;
oldMidPt.y = midPt.y;
stage.addChild(drawingCanvas);
stage.update();
}
};`
I'd like to draw a rectangle with click and drag. How can I do this ? Where do I have to put my click event listener ? On the stage or on the layer ? I have the following code but it doesn't work :
stage = new Kinetic.Stage({...})
layer = new Kinetic.Layer({...})
stage.add(layer)
stage.on('click', function() {
var pos = stage.getMousePosition();
var rect = new Kinetic.Rect({
x: pos.x,
y: pos.y,
width: 10,
height: 10,
});
layer.add(rect);
layer.draw();
})
Thanks.
As far as i know there is no "click" event on stage in kineticjs. You should use something like this:
stage.getContainer().addEventListener('mousedown', function(evt) {});
Link to a fiddle that shows what I've been working on:
http://jsfiddle.net/robtown/SGQq7/22/
It's a set of drawing tools using KineticJS and Sketch.js
You need to select "make sketch" to draw freehand and then "copy sketch to Kinetic" to copy your sketch into the kinetic stage. Select "Make rectangle" make a rectangle.
I need to include code to post this so here's the code for when you select the "Make Rectangle" button:
$('#makeRect').click(function (e) {
followRect = new Kinetic.Rect({
width: 120,
height: 40,
x: -200,
y:-200,
stroke: 'red',
strokeWidth: 3
});
drawLayer.setVisible(true);
drawLayer.add(followRect);
drawLayer.draw();
makeRect = true;
drawLayer.on("mousemove", function (e) {
if (makeRect) {
followRect.setX(e.x+5);
followRect.setY(e.y+5);
drawLayer.draw();
}
});
This creates a rectangle that follows the mouse until you click on the canvas, then it drops the rectangle into the Redlines layer of the stage:
drawLayer.on("mousedown", function (e) {
//for (var f = 0 ; f < 1; f++) {
//alert(e.length);
if (makeRect) {
addToRedlineLayer(e.x, e.y);
}
//}
followRect.setX(-200);
drawLayer.setVisible(false);
return;
});
I had the exact same problem, and indeed the method of Guilherme works greatly.
But there's a simple alternative: create a transparent Rect (Kinetic rectangle) the same size as the canvas:
<script type='text/javascript'>//<![CDATA[
window.onload=function(){
function writeMessage(messageLayer, message) {
var context = messageLayer.getContext();
messageLayer.clear();
context.font = '18pt Calibri';
context.fillStyle = 'black';
context.fillText(message, 10, 25);
}
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var shapesLayer = new Kinetic.Layer();
var messageLayer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x:0,
y:0,
width:stage.getWidth(),
height:stage.getHeight(),
stroke:0
});
rect.on('mousemove', function() {
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
writeMessage(messageLayer, 'x: ' + x + ', y: ' + y);
});
stage.getContainer().addEventListener('mouseout', function(evt) {
writeMessage(messageLayer, '');
});
shapesLayer.add(rect);
stage.add(shapesLayer);
stage.add(messageLayer);
}//]]>
</script>
The above code will print the x and y position of the mouse when you hover it over the canvas (a div with id "container"). You of course need to load the KineticJS library before using this code.
How to make google closure graphics elements draggable and respond to events otherwise?
Here's what I have so far. I have the circle, but can't drag it yet :).
Thanks.
goog.require('goog.dom');
goog.require('goog.graphics');
goog.require('goog.events');
goog.require('goog.fx.Dragger');
goog.provide('graphics_test');
graphics_test = function(){
var canvas = goog.dom.createDom('div', {'id':'canvas'});
goog.dom.appendChild(document.body, canvas);
var g = goog.graphics.createGraphics(600,400);
var fill = new goog.graphics.SolidFill('yellow');
var stroke = new goog.graphics.Stroke(1,'black');
circle = g.drawCircle(300, 200, 50, stroke, fill);
var dragger = new goog.fx.Dragger(circle,circle);
g.render(goog.dom.$('canvas'));
};
I had to use a dragger in my own project and couldn't get goog.fx.Dragger to work. However, I implemented my own draggable. Its actually much smaller and pretty simple. Here is a gist:
var graphic = new goog.graphics.ext.Graphics(1000, 500);
var group = new goog.graphics.ext.Group(graphic);
group.setLeft(20, true);
group.setTop(20, true);
group.setWidth(600, true);
group.setHeight(200);
var fill = new goog.graphics.SolidFill('yellow');
var stroke = new goog.graphics.Stroke(2, 'green');
var bg = new goog.graphics.SolidFill('#eeeeee');
var outline = new goog.graphics.Stroke(1, '#333333');
var path = new goog.graphics.ext.Path().moveTo(0, 0).lineTo(20, 0).
lineTo(10, 20).close();
var shape = new goog.graphics.ext.Shape(group, path);
shape.setLeft(10, true);
shape.setTop(10, true);
shape.setWidth('10%', true);
shape.setHeight('10%');
shape.setStroke(stroke);
shape.setFill(fill);
var ellipse = new goog.graphics.ext.Ellipse(group);
ellipse.setCenter(0, true);
ellipse.setMiddle(0, true);
ellipse.setWidth(120, true);
ellipse.setHeight(60);
ellipse.setStroke(stroke);
ellipse.setFill(fill);
goog.events.listen(group.getWrapper(), 'mousedown', function(e) {
group.startOffsetX = e.offsetX;
group.startOffsetY = e.offsetY;
group.dragging = true;
});
goog.events.listen(group.getWrapper(), 'mouseup', function(e) {
group.dragging = false;
});
goog.events.listen(group.getWrapper(), 'mousemove', function(e) {
if(group.dragging) {
group.setPosition(group.getLeft() + (e.offsetX-group.startOffsetX),
group.getTop() + (e.offsetY-group.startOffsetY));
group.startOffsetX = e.offsetX;
group.startOffsetY = e.offsetY;
};
});
goog.events.listen(group.getWrapper(), 'mouseout', function(e) {
group.dragging = false;
});
graphic.render(document.body);
You can of course listen on any single shape too (rectangle/ellipse or even a path) by listening to it instead of the group itself. I feel this method gives you more flexibility (think restricting the movement of the entire group to the canvas or some custom bounds!)
I purposely omitted the fifth argument(opt_handler) from goog.events.listen so as to make this code more readable.
Hope this helps :)