I'm trying to write a little game using KineticJS. The cursor is a gun sight, and I want to use some keyboard keys as the triggers to fire different "bullets" for different effects, but I didn't find a way to do it using JS events.
stage = new Kinetic.Stage
container: 'container'
width: window.innerWidth
height: window.innerHeight
shape_layer = new Kinetic.Layer()
circle = new Kinetic.Circle
x: stage.getWidth() / 2
y: stage.getHeight() / 2
radius: 100
fill: 'red'
stroke: 'white'
strokeWidth: 20
circle.on 'click', (e) ->
console.log 'You get one point!'
shape_layer.add circle
stage.add shape_layer
$(document).on 'keydown', (e) ->
if e.keyCode == 90
mouse_p = stage.getMousePosition()
x = mouse_p.x
y = mouse_p.y
evt = document.createEvent 'MouseEvents'
evt.initMouseEvent 'mouseup'
, true
, true
, window
, null
, 0
, 0
, x
, y
shape_layer.fire 'click', evt, true
The event is actually fired, but it's on the layer, not the circle. So I start to think is it possible to get the shape using the mouse position and directly fire a click event on it?
Why not handle mouse events through KineticJS instead?
layer.on('click', function(evt) {
// get the shape that was clicked on
var shape = evt.targetNode;
alert('you clicked on \"' + shape.getName() + '\"');
});
Event Delegation: http://www.html5canvastutorials.com/kineticjs/html5-canvas-get-event-shape-with-kineticjs/
Events: http://www.html5canvastutorials.com/kineticjs/html5-canvas-path-mouseover/
UPDATE:
I misunderstood your question so my apologies.
You were looking to access the targetNode when pressing a key (keydown event). Here's how I would do it:
First you need to set a transparent rectangle background with the width and height of the stage, so that the layer can detect mouse events (in this case we need mousemove).
var bg = new Kinetic.Rect({
x: 0,
y: 0,
width: stage.getWidth(),
height: stage.getHeight(),
id: 'bg'
});
Then, I set an empty Kinetic.Shape for whenever the mouse is moving inside the stage but isn't on a target. So, target is always equal to empty unless mouse is over a node other than our transparent background. scoreText simply prints your score on the stage.
var empty = new Kinetic.Shape({
id: 'empty'
});
var target = empty;
var score = 0;
var scoreText = new Kinetic.Text({
text: 'Score: '+score,
x: 10,
y: 10,
fill: '#000',
fontSize: 20,
id: 'score'
});
Use the mousemove event in KineticJS:
layer.on('mousemove', function (e) {
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
var node = e.targetNode;
var nodeID = node.getId();
if (nodeID !== 'bg') {
target = node;
} else {
target = empty;
}
});
Then use the jQuery keydown event:
$(document).keydown(function(e) {
if (e.keyCode == 90) {
var id = target.getId();
if (id == 'empty' || id == 'score') {
alert('MISS');
} else {
var targetID = target.getId();
var targetName = target.getName();
alert('ID: ' + targetID + ' NAME: ' + targetName + ' You get one point!');
target.destroy();
target = empty;
score++;
updateScore(scoreText, score);
randomCircle();
}
}
});
And finally, for the sake of making a game.. the randomCircle() and updateScore() functions:
function updateScore(text, score) {
text.setText('Score: ' + score);
//layer.draw(); //normally we would have to layer.draw() here, but I called it in randomCircle() instead to save the amount of draw() calls necessary
}
function randomCircle() {
var circle = new Kinetic.Circle({
x: Math.floor((Math.random() * 578)),
y: Math.floor((Math.random() * 200)),
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
id: 'someTarget',
name: 'targets'
});
layer.add(circle);
layer.draw();
}
jsfiddle (Don't forget to click on the javascript pane to be able to use the keydown event!)
Related
I am quite new to javascript, and I have been following tutorials at https://konvajs.org/ to help me learn their library.
I am currently trying to make it so you can load and select local images to move, resize, and rotate them.
This is the code I have so far:
<head>
<!-- USE DEVELOPMENT VERSION -->
<script src="https://unpkg.com/konva#7.0.0/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Select and Transform Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/3.2.5/konva.min.js"></script>
<div>Render a local image without upload</div>
<div>
<input type="file" id="file_input">
</div>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
//add images
// listen for the file input change event and load the image.
$("#file_input").change(function(e){
var URL = window.webkitURL || window.URL;
var url = URL.createObjectURL(e.target.files[0]);
var img = new Image();
img.src = url;
img.onload = function() {
var img_width = img.width;
var img_height = img.height;
// calculate dimensions to get max 300px
var max = 300;
var ratio = (img_width > img_height ? (img_width / max) : (img_height / max))
// now load the Konva image
var theImg = new Konva.Image({
image: img,
x: 50,
y: 30,
width: img_width/ratio,
height: img_height/ratio,
draggable: true,
rotation: 0
});
layer.add(theImg);
layer.draw();
}
});
var tr = new Konva.Transformer();
layer.add(tr);
// by default select all shapes
// at this point basic demo is finished!!
// we just have several transforming nodes
layer.draw();
// add a new feature, lets add ability to draw selection rectangle
var selectionRectangle = new Konva.Rect({
fill: 'rgba(0,0,255,0.5)',
});
layer.add(selectionRectangle);
var x1, y1, x2, y2;
stage.on('mousedown touchstart', (e) => {
// do nothing if we mousedown on eny shape
if (e.target !== stage) {
return;
}
x1 = stage.getPointerPosition().x;
y1 = stage.getPointerPosition().y;
x2 = stage.getPointerPosition().x;
y2 = stage.getPointerPosition().y;
selectionRectangle.visible(true);
selectionRectangle.width(0);
selectionRectangle.height(0);
layer.draw();
});
stage.on('mousemove touchmove', () => {
// no nothing if we didn't start selection
if (!selectionRectangle.visible()) {
return;
}
x2 = stage.getPointerPosition().x;
y2 = stage.getPointerPosition().y;
selectionRectangle.setAttrs({
x: Math.min(x1, x2),
y: Math.min(y1, y2),
width: Math.abs(x2 - x1),
height: Math.abs(y2 - y1),
});
layer.batchDraw();
});
stage.on('mouseup touchend', () => {
// no nothing if we didn't start selection
if (!selectionRectangle.visible()) {
return;
}
// update visibility in timeout, so we can check it in click event
setTimeout(() => {
selectionRectangle.visible(false);
layer.batchDraw();
});
var shapes = stage.find('.rect').toArray();
var box = selectionRectangle.getClientRect();
var selected = shapes.filter((shape) =>
Konva.Util.haveIntersection(box, shape.getClientRect())
);
tr.nodes(selected);
layer.batchDraw();
});
// clicks should select/deselect shapes
stage.on('click tap', function (e) {
// if we are selecting with rect, do nothing
if (selectionRectangle.visible()) {
return;
}
// if click on empty area - remove all selections
if (e.target === stage) {
tr.nodes([]);
layer.draw();
return;
}
// do nothing if clicked NOT on our rectangles
if (!e.target.hasName('rect')) {
return;
}
// do we pressed shift or ctrl?
const metaPressed = e.evt.shiftKey || e.evt.ctrlKey || e.evt.metaKey;
const isSelected = tr.nodes().indexOf(e.target) >= 0;
if (!metaPressed && !isSelected) {
// if no key pressed and the node is not selected
// select just one
tr.nodes([e.target]);
} else if (metaPressed && isSelected) {
// if we pressed keys and node was selected
// we need to remove it from selection:
const nodes = tr.nodes().slice(); // use slice to have new copy of array
// remove node from array
nodes.splice(nodes.indexOf(e.target), 1);
tr.nodes(nodes);
} else if (metaPressed && !isSelected) {
// add the node into selection
const nodes = tr.nodes().concat([e.target]);
tr.nodes(nodes);
}
layer.draw();
});
</script>
Demo: https://www.w3schools.com/code/tryit.asp?filename=GG5QCXFMLFXJ
I am unsure how to make the selecting work on images, rather than rectangles.
Thank you
In the demo, you will see that "rect" names is used to detect selectable shapes. That is a useful approach, because you may have non-selectable shapes on the stage (like a background).
So to check a shape if it is selectable, we use:
e.target.hasName('rect')
So just remember to add "rect" name to new images on the stage:
// now load the Konva image
var theImg = new Konva.Image({
name: 'rect',
image: img,
x: 50,
y: 30,
width: img_width/ratio,
height: img_height/ratio,
draggable: true,
rotation: 0
});
The name itself is not very important. You can find a different way to detect "selectable" nodes.
https://www.w3schools.com/code/tryit.asp?filename=GG6XVLB39IKW
I understand Fabric.js have built-in support on drag-n-drop within the same canvas.
How can we make it work for multiple canvas?
Or from an non-canvas html element e.g. an image from a table?
Drag and drop between canvases is possible in Fabric.js, but involves some manipulation of private properties. For this reason it is not guaranteed to be functional with future versions of Fabric.js.
Working demo: https://jsfiddle.net/mmalex/kdbu9f3y/
Video capture: https://youtu.be/nXZgCmIrpqQ
Key features:
✓ can drag and drop between any number of canvases (not only two),
✓ can drag back and forth between canvases without interruption,
✓ can transform (mirror) dropped image without interruption of manipulation.
Step 1 – prepare canvases, load images, arrange everything for demo:
//create two canvases
var canvas0El = document.getElementById("c0");
canvas0El.width = canvas0El.offsetWidth;
canvas0El.height = canvas0El.parentElement.offsetHeight;
var canvas1El = document.getElementById("c1");
canvas1El.width = canvas1El.offsetWidth;
canvas1El.height = canvas1El.parentElement.offsetHeight;
var canvas0 = new fabric.Canvas('c0');
canvas0.setBackgroundColor('rgba(19, 19, 19, 0.25)');
canvas0.renderAll();
var canvas1 = new fabric.Canvas('c1');
canvas1.setBackgroundColor('rgba(92, 18, 18, 0.25)');
canvas1.renderAll();
// add loaded image on left canvas
var onImageLoaded = function(oImg) {
oImg.originX = "center";
oImg.originY = "center";
oImg.left = this.x;
oImg.top = this.y;
canvas0.add(oImg);
oImg.canvas = canvas0;
imgArrow = oImg;
};
var config = { crossOrigin: 'anonymous' };
var baseUrl = "http://mbnsay.com/rayys/images";
var url0 = baseUrl + "/arrow-right-green.png";
var url1 = baseUrl + "/arrow-right-icon.png";
var url2 = baseUrl + "/arrow-right-blue.png";
// load some images
fabric.Image.fromURL(url0, onImageLoaded.bind({ x: 56, y: 96 }), config);
fabric.Image.fromURL(url0, onImageLoaded.bind({ x: 156, y: 96 }), config);
fabric.Image.fromURL(url1, onImageLoaded.bind({ x: 56, y: 2*96 }), config);
fabric.Image.fromURL(url1, onImageLoaded.bind({ x: 156, y: 2*96 }), config);
fabric.Image.fromURL(url2, onImageLoaded.bind({ x: 56, y: 3*96 }), config);
fabric.Image.fromURL(url2, onImageLoaded.bind({ x: 156, y: 3*96 }), config);
Step 2 – subscribe object:moving events on both canvases, and watch when object center crossing the canvas border. When object crosses the border, it has to be
remove from source canvas,
paste into destination canvas,
migrate internal canvas transformations (will be explained separately)
var onObjectMoving = function(p) {
var viewport = p.target.canvas.calcViewportBoundaries();
if (p.target.canvas === canvas0) {
if (p.target.left > viewport.br.x) {
console.log("Migrate: left -> center");
migrateItem(canvas0, canvas1, p.target);
return;
}
}
if (p.target.canvas === canvas1) {
if (p.target.left < viewport.tl.x) {
console.log("Migrate: center -> left");
migrateItem(canvas1, canvas0, p.target);
return;
}
}
};
canvas0.on("object:moving", onObjectMoving);
canvas1.on("object:moving", onObjectMoving);
Step 3 – The core of the solution, migrate object between canvases not interrupting mouse manipulations. Hard to explain, just follow the comments in code.
var migrateItem = function(fromCanvas, toCanvas, pendingImage) {
// Just drop image from old canvas
fromCanvas.remove(pendingImage);
// We're going to trick fabric.js,
// so we keep internal transforms of the source canvas,
// in order to inject it into destination canvas.
var pendingTransform = fromCanvas._currentTransform;
fromCanvas._currentTransform = null;
// Make shortcuts for fabric.util.removeListener and fabric.util.addListener
var removeListener = fabric.util.removeListener;
var addListener = fabric.util.addListener;
// Re-arrange subscriptions for source canvas
{
removeListener(fabric.document, 'mouseup', fromCanvas._onMouseUp);
removeListener(fabric.document, 'touchend', fromCanvas._onMouseUp);
removeListener(fabric.document, 'mousemove', fromCanvas._onMouseMove);
removeListener(fabric.document, 'touchmove', fromCanvas._onMouseMove);
addListener(fromCanvas.upperCanvasEl, 'mousemove', fromCanvas._onMouseMove);
addListener(fromCanvas.upperCanvasEl, 'touchmove', fromCanvas._onMouseMove, {
passive: false
});
if (isTouchDevice) {
// Wait 500ms before rebinding mousedown to prevent double triggers
// from touch devices
var _this = fromCanvas;
setTimeout(function() {
addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
}, 500);
}
}
// Re-arrange subscriptions for destination canvas
{
addListener(fabric.document, 'touchend', toCanvas._onMouseUp, {
passive: false
});
addListener(fabric.document, 'touchmove', toCanvas._onMouseMove, {
passive: false
});
removeListener(toCanvas.upperCanvasEl, 'mousemove', toCanvas._onMouseMove);
removeListener(toCanvas.upperCanvasEl, 'touchmove', toCanvas._onMouseMove);
if (isTouchDevice) {
// Unbind mousedown to prevent double triggers from touch devices
removeListener(toCanvas.upperCanvasEl, 'mousedown', toCanvas._onMouseDown);
} else {
addListener(fabric.document, 'mouseup', toCanvas._onMouseUp);
addListener(fabric.document, 'mousemove', toCanvas._onMouseMove);
}
}
// We need this timer, because we want Fabric.js to complete pending render
// before we inject, because it causes some unpleasant image jumping.
setTimeout(function() {
// Add image to destination canvas,
pendingImage.scaleX *= -1;
pendingImage.canvas = toCanvas;
pendingImage.migrated = true;
toCanvas.add(pendingImage);
// and inject transforms from source canvas
toCanvas._currentTransform = pendingTransform;
// as we have mirrored the image, we mirror transforms too
toCanvas._currentTransform.scaleX *= -1;
toCanvas._currentTransform.original.scaleX *= -1;
// finally don't forget to make pasted object selected
toCanvas.setActiveObject(pendingImage);
}, 10);
};
Have fun!
Use
canvas.observe("object:moving", function (event) {});
If event.e.clientY and event.e.clientX are outside the canvas, then:
var activeObject = canvas.getActiveObject();
Store in global dragImage var
activeObject.clone(function (c) { dragImage = c; });
canvas.remove(activeObject);
Then in window mouse move event you can place an img with src = dragImage.src and follow the cursor.
function mousemove(e){
if (dragImage != null) {
$("#dragimage").show();
$("#dragimage").css("left", e.clientX);
$("#dragimage").css("top", e.clientY);
return;
}else{
$("#dragimage").hide();
}
}
On a window event mouseup, if dragImage != null and new coordinates are inside a fabric.js canvas, just newcanvas.add(dragImage).
mouseup event:
if (dragImage != null) {
$([canvas, canvas2]).each(function (i, v) {
if (Intersect([event.clientX, event.clientY],$(v.wrapperEl))) {
dragImage.left = event.clientX - $(v.wrapperEl).offset().left;
dragImage.top = event.clientY - $(v.wrapperEl).offset().top;
v.add(dragImage);
}
});
dragImage = null;
}
Helper Intersect function:
function Intersect(point, element) {
return ( point[0] > element.offset().left
&& point[0] < element.offset().left + element.width()
&& point[1] < element.offset().top + element.height()
&& point[1] > element.offset().top
);
}
css for #dragimage:
#dragimage
{
opacity:0.5;
max-width:100px;
max-height:200px;
position:fixed;
top:0px;
left:0px;
z-index:90000;
}
I can't do a fiddle but i implemented this on our mega huge photo album editor in less than 30 minutes. Works for text too but for text you must use dragImage=getActiveObject().clone()
Any questions feel free to ask.
I need help only having the anchors for rotating. Right now there is five anchors and I don't know how to get rid of all of them except the rotate one. I would also only like the anchors to show when the user hovers over the image
Here is my code
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<body onmousedown="return false;">
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.4.min.js">
</script>
<script>
function update(activeAnchor) {
var group = activeAnchor.getParent();
var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var rotateAnchor = group.get('.rotateAnchor')[0];
var image = group.get('Image')[0];
var anchorX = activeAnchor.getX();
var anchorY = activeAnchor.getY();
var imageWidth = image.getWidth();
var imageHeight = image.getHeight();
var offsetX = Math.abs((topLeft.getX() + bottomRight.getX() + 10) / 2);
var offsetY = Math.abs((topLeft.getY() + bottomRight.getY() + 10) / 2);
// update anchor positions
switch (activeAnchor.getName()) {
case 'rotateAnchor':
group.setOffset(offsetX, offsetY);
break;
case 'topLeft':
topRight.setY(anchorY);
bottomLeft.setX(anchorX);
break;
case 'topRight':
topLeft.setY(anchorY);
bottomRight.setX(anchorX);
break;
case 'bottomRight':
topRight.setX(anchorX);
bottomLeft.setY(anchorY);
break;
case 'bottomLeft':
topLeft.setX(anchorX);
bottomRight.setY(anchorY);
break;
}
rotateAnchor.setX(topRight.getX() + 5);
rotateAnchor.setY(topRight.getY() + 20);
image.setPosition((topLeft.getPosition().x + 20), (topLeft.getPosition().y + 20));
var width = topRight.getX() - topLeft.getX() - 30;
var height = bottomLeft.getY() - topLeft.getY() - 30;
if (width && height) {
image.setSize(width, height);
}
}
function addAnchor(group, x, y, name, dragBound) {
var stage = group.getStage();
var layer = group.getLayer();
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
radius: 8,
name: name,
draggable: true,
dragOnTop: false
});
if (dragBound == 'rotate') {
anchor.setAttrs({
dragBoundFunc: function (pos) {
return getRotatingAnchorBounds(pos, group);
}
});
}
anchor.on('dragmove', function() {
update(this);
layer.draw();
});
anchor.on('mousedown touchstart', function() {
group.setDraggable(false);
this.moveToTop();
});
anchor.on('dragend', function() {
group.setDraggable(true);
layer.draw();
});
// add hover styling
anchor.on('mouseover', function() {
var layer = this.getLayer();
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
layer.draw();
});
anchor.on('mouseout', function() {
var layer = this.getLayer();
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
layer.draw();
});
group.add(anchor);
}
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
function getRotatingAnchorBounds(pos, group) {
var topLeft = group.get('.topLeft')[0];
var bottomRight = group.get('.bottomRight')[0];
var topRight = group.get('.topRight')[0];
var absCenterX = Math.abs((topLeft.getAbsolutePosition().x + 5 + bottomRight.getAbsolutePosition().x + 5) / 2);
var absCenterY = Math.abs((topLeft.getAbsolutePosition().y + 5 + bottomRight.getAbsolutePosition().y + 5) / 2);
var relCenterX = Math.abs((topLeft.getX() + bottomRight.getX()) / 2);
var relCenterY = Math.abs((topLeft.getY() + bottomRight.getY()) / 2);
var radius = distance(relCenterX, relCenterY, topRight.getX() + 5, topRight.getY() + 20);
var scale = radius / distance(pos.x, pos.y, absCenterX, absCenterY);
var realRotation = Math.round(degrees(angle(relCenterX, relCenterY, topRight.getX() + 5, topRight.getY() + 20)));
var rotation = Math.round(degrees(angle(absCenterX, absCenterY, pos.x, pos.y)));
rotation -= realRotation;
group.setRotationDeg(rotation);
return {
y: Math.round((pos.y - absCenterY) * scale + absCenterY),
x: Math.round((pos.x - absCenterX) * scale + absCenterX)
};
}
function radians(degrees) { return degrees * (Math.PI / 180); }
function degrees(radians) { return radians * (180 / Math.PI); }
// Calculate the angle between two points.
function angle(cx, cy, px, py) {
var x = cx - px;
var y = cy - py;
return Math.atan2(-y, -x);
}
// Calculate the distance between two points.
function distance(p1x, p1y, p2x, p2y) {
return Math.sqrt(Math.pow((p2x - p1x), 2) + Math.pow((p2y - p1y), 2));
}
function initStage(images) {
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 400
});
var darthVaderGroup = new Kinetic.Group({
x: 270,
y: 100,
draggable: true
});
var yodaGroup = new Kinetic.Group({
x: 100,
y: 110,
draggable: true
});
var layer = new Kinetic.Layer();
/*
* go ahead and add the groups
* to the layer and the layer to the
* stage so that the groups have knowledge
* of its layer and stage
*/
layer.add(darthVaderGroup);
layer.add(yodaGroup);
stage.add(layer);
// darth vader
var darthVaderImg = new Kinetic.Image({
x: 0,
y: 0,
image: images.darthVader,
width: 200,
height: 138,
name: 'image'
});
darthVaderGroup.add(darthVaderImg);
addAnchor(darthVaderGroup, -20, -20, 'topLeft', 'none');
addAnchor(darthVaderGroup, 220, -20, 'topRight', 'none');
addAnchor(darthVaderGroup, 220, 158, 'bottomRight', 'none');
addAnchor(darthVaderGroup, -20, 158, 'bottomLeft','none');
addAnchor(darthVaderGroup, 225, 0, 'rotateAnchor','rotate');
darthVaderGroup.on('dragstart', function() {
this.moveToTop();
});
stage.draw();
}
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg'
};
loadImages(sources, initStage);
</script>
</body>
</html>
You can use each anchors show/hide methods inside the images mouseenter/mouseleave events to display the anchors when the mouse enters the image:
image.on("mouseleave",function(){ anchor1.hide(); }
image.on("mouseenter",function(){ anchor1.show(); layer.draw(); }
Problem is that since your anchors are partly outside your image, so hiding the anchors when the mouse leaves the image might make the anchors "disappear" when the user intends to use them.
The ideal solution would be to listen for mouseenter/mouseleave events on the group which contains the image but also extends to include the outside part of the anchors. Unfortunately, a Kinetic.Group will not respond to mouseenter/mouseleave events.
A workaround is to create a Kinetic.Rect background to the group which includes the images plus the anchors. The rect will listen for mouseenter/mouseleave events and will show/hide the anchors. If you don't want the background rect to be visible, just set it's opacity to .001. The rect will still listen for events, but will be invisible.
groupBackgroundRect.on("mouseleave",function(){ anchor1.hide(); }
groupBackgroundRect.on("mouseenter",function(){ anchor1.show(); layer.draw(); }
A related note:
With KineticJS, combining rotation with resizing is made more difficult than it needs to be because KineticJS uses offsetX/offsetY as both an object's rotation point and as an offset to its position. Your key to making it work will be to re-center the offset point after resizing so that your rotation takes place around the new centerpoint--not the previous centerpoint. (or reset the offset reference point to any other point that you want to rotate around).
I am trying to paint in canvas using kinetic-v4.7.2.min.js. It successfully draw lines when user touch that layer. I am using jsFiddle for this feature.
How can i clear this ractangle layer & how can i save that layer of container as an image?
JSFiddle
// create a stage and a layer
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var layer = new Kinetic.Layer();
stage.add(layer);
// an empty stage does not emit mouse-events
// so fill the stage with a background rectangle
// that can emit mouse-events
var background = new Kinetic.Rect({
x: 0,
y: 0,
width: stage.getWidth(),
height: stage.getHeight(),
fill: 'white',
stroke: 'black',
strokeWidth: 1,
})
layer.add(background);
layer.draw();
// a flag we use to see if we're dragging the mouse
var isMouseDown = false;
// a reference to the line we are currently drawing
var newline;
// a reference to the array of points making newline
var points = [];
// on the background
// listen for mousedown, mouseup and mousemove events
background.on('mousedown', function () {
onMousedown();
});
background.on('mouseup', function () {
onMouseup();
});
background.on('mousemove', function () {
onMousemove();
});
// On mousedown
// Set the isMouseDown flag to true
// Create a new line,
// Clear the points array for new points
// set newline reference to the newly created line
function onMousedown(event) {
isMouseDown = true;
points = [];
points.push(stage.getMousePosition());
var line = new Kinetic.Line({
points: points,
stroke: "green",
strokeWidth: 5,
lineCap: 'round',
lineJoin: 'round'
});
layer.add(line);
newline = line;
}
// on mouseup end the line by clearing the isMouseDown flag
function onMouseup(event) {
isMouseDown = false;
}
// on mousemove
// Add the current mouse position to the points[] array
// Update newline to include all points in points[]
// and redraw the layer
function onMousemove(event) {
if (!isMouseDown) {
return;
};
points.push(stage.getMousePosition());
newline.setPoints(points);
// use layer.drawScene
// this is faster since the "hit" canvas is not refreshed
layer.drawScene();
}
Any help would be appreciated!!
Thanks.
Using toDataURL() method:
DEMO
$('button').on('click', function () {
var img = $('.kineticjs-content').find('canvas').get(0).toDataURL("image/png");
$('body').prepend('<img src="' + img + '">');
});
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.