var canvas = new fabric.Canvas('canvas');
canvas.setWidth(500);
canvas.setHeight(500);
canvas.setBackgroundColor('#ccc');
canvas.allowTouchScrolling = true;
var line = new fabric.Path('M100,350 Q200,100 300,350', {
fill: '',
stroke: 'red',
strokeWidth: 5,
objectCaching: false
});
var circle = new fabric.Circle({
radius: 15,
fill: 'blue',
lockScalingY: true,
lockScalingX: true,
left: 300,
top: 350
});
canvas.add(line, circle);
canvas.on('object:moving', function(event) {
var path = canvas.item(0);
path.path[1][3] = event.target.left;
path.path[1][4] = event.target.top;
path.setCoords();
canvas.calcOffset();
canvas.renderAll();
});
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.js"></script>
<canvas id="canvas"></canvas>
show demo on fiddle
When I move circle there is a change path of line. After change path of line I see wrong bounding rectangles.
How I can fix that?
I can just remove line and create again, but maybe there is an easier way to do it.
You can actually recalculate the bounding box, but you have to do by yourself.
So is actually tricky, you need to remove the current pathOffset from path, and then call the internal method to recalculate the bbox.
It works, but being an internal method and a non supported feature, it could be changed in the future.
var canvas = new fabric.Canvas('canvas');
canvas.setWidth(500);
canvas.setHeight(500);
canvas.setBackgroundColor('#ccc');
canvas.allowTouchScrolling = true;
var line = new fabric.Path('M100,350 Q200,100 300,350', {
fill: '',
stroke: 'red',
strokeWidth: 5,
objectCaching: false
});
var circle = new fabric.Circle({
radius: 15,
fill: 'blue',
lockScalingY: true,
lockScalingX: true,
left: 300,
top: 350
});
canvas.add(line, circle);
canvas.on('object:moving', function(event) {
var path = canvas.item(0);
path.path[1][3] = event.target.left;
path.path[1][4] = event.target.top;
path.pathOffset = null;
path._setPositionDimensions({});
path.setCoords();
canvas.calcOffset();
canvas.renderAll();
});
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.js"></script>
<canvas id="canvas"></canvas>
Related
So I'm currently working on a project that requires similar functionality to what you would expect in a floorplan designing application, and I need to be able to essentially draw a polygon (room) by connecting points generated on-click, with lines (walls).
I've found a few really great examples that resemble the functionality I'm looking for, but I'm unsure of how to approach implementing it as a functional react component.
I've seen Fabric.js and Konva.js used in a few other implementations of this kind of functionality, but I can't seem to be able to find a react example to use as a reference..
Below is an example I found on codepen using jQuery
var min = 99;
var max = 999999;
var polygonMode = true;
var pointArray = new Array();
var lineArray = new Array();
var activeLine;
var activeShape = false;
var canvas
$(window).load(function() {
prototypefabric.initCanvas();
$('#create-polygon').click(function() {
prototypefabric.polygon.drawPolygon();
});
});
var prototypefabric = new function() {
this.initCanvas = function() {
canvas = window._canvas = new fabric.Canvas('c');
canvas.setWidth($(window).width());
canvas.setHeight($(window).height() - $('#nav-bar').height());
//canvas.selection = false;
canvas.on('mouse:down', function(options) {
if (options.target && options.target.id == pointArray[0].id) {
prototypefabric.polygon.generatePolygon(pointArray);
}
if (polygonMode) {
prototypefabric.polygon.addPoint(options);
}
});
canvas.on('mouse:up', function(options) {
});
canvas.on('mouse:move', function(options) {
if (activeLine && activeLine.class == "line") {
var pointer = canvas.getPointer(options.e);
activeLine.set({
x2: pointer.x,
y2: pointer.y
});
var points = activeShape.get("points");
points[pointArray.length] = {
x: pointer.x,
y: pointer.y
}
activeShape.set({
points: points
});
canvas.renderAll();
}
canvas.renderAll();
});
};
};
prototypefabric.polygon = {
drawPolygon: function() {
polygonMode = true;
pointArray = new Array();
lineArray = new Array();
activeLine;
},
addPoint: function(options) {
var random = Math.floor(Math.random() * (max - min + 1)) + min;
var id = new Date().getTime() + random;
var circle = new fabric.Circle({
radius: 5,
fill: '#ffffff',
stroke: '#333333',
strokeWidth: 0.5,
left: (options.e.layerX / canvas.getZoom()),
top: (options.e.layerY / canvas.getZoom()),
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'center',
originY: 'center',
id: id,
objectCaching: false
});
if (pointArray.length == 0) {
circle.set({
fill: 'red'
})
}
var points = [(options.e.layerX / canvas.getZoom()), (options.e.layerY / canvas.getZoom()), (options.e.layerX / canvas.getZoom()), (options.e.layerY / canvas.getZoom())];
line = new fabric.Line(points, {
strokeWidth: 2,
fill: '#999999',
stroke: '#999999',
class: 'line',
originX: 'center',
originY: 'center',
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
if (activeShape) {
var pos = canvas.getPointer(options.e);
var points = activeShape.get("points");
points.push({
x: pos.x,
y: pos.y
});
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
canvas.remove(activeShape);
canvas.add(polygon);
activeShape = polygon;
canvas.renderAll();
} else {
var polyPoint = [{
x: (options.e.layerX / canvas.getZoom()),
y: (options.e.layerY / canvas.getZoom())
}];
var polygon = new fabric.Polygon(polyPoint, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
activeShape = polygon;
canvas.add(polygon);
}
activeLine = line;
pointArray.push(circle);
lineArray.push(line);
canvas.add(line);
canvas.add(circle);
canvas.selection = false;
},
generatePolygon: function(pointArray) {
var points = new Array();
$.each(pointArray, function(index, point) {
points.push({
x: point.left,
y: point.top
});
canvas.remove(point);
});
$.each(lineArray, function(index, line) {
canvas.remove(line);
});
canvas.remove(activeShape).remove(activeLine);
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 0.5,
fill: 'red',
opacity: 1,
hasBorders: false,
hasControls: false
});
canvas.add(polygon);
activeLine = null;
activeShape = null;
polygonMode = false;
canvas.selection = true;
}
};
* {
font-family: "Roboto", sans-serif;
font-weight: 100;
}
body {
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.2.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" rel="stylesheet" />
<nav class="grey darken-4" role="navigation" id="nav-bar" style="height:50px;">
<div class="nav-wrapper container">
<a id="logo-container" class="brand-logo" style="line-height: 50px;font-size: 20px;">Fabric Polygon</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a id="create-polygon" style="line-height: 50px;font-size: 20px;cursor:pointer;">Create Polygon</a></li>
</ul>
</div>
</nav>
<div class="section no-pad-bot no-pad-top">
<canvas id="c"></canvas>
</div>
And this is essentially the end functionality I'd like to achieve.
I really appreciate any help or advice!
To begin with, you should keep in mind that react is meant for structuring the visual components of your application and their interaction with the rest of your code, that is to say react is not a framework that covers everything you may need.
I'm not even sure that react is what you need, maybe you need a canvas. Maybe the canvas resides in a react component.
Nevertheless, if you decide that you would like to go at it with react then lets break down what you need, visually:
a "canvas" component which contains all your polygons or lines
a "polygon" which contains lines
a "line" component.
to begin with, before the interaction, make sure you create a state that makes sense that will be sent to the canvas, maybe an array of polygon or line objects. a line object is clearly two points, maybe a color, etc.
If you got this far with a mock of your polgyon state, its time to add a state manager (like redux) and some event listeners to update the state when events happen in the DOM (like mouse or keyboard events.)
To summarize:
Use react for visual elements.
Use redux to manage the state of the canvas.
When initializing the parent component (the "canvas"), create event listeners that fire actions that affect the state.
WARNING: turn the volume down before you run the snippet!
I want to be able to click on the stage to add a 'module' shape. But I have found that a click on the 'module' shape itself creates another, meaning that the stage.click listener is being fired when it should not be.
How can I have a stage.click listener that does not fire incorrectly when I click on a shape ?
var width = window.innerWidth;
var height = window.innerHeight;
var rectButtonClicked = false;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var group = new Konva.Group({
draggable: true
});
stage.on('contentClick', function() {
createModule();
});
function createModule() {
var mouseX = stage.getPointerPosition().x;
var mouseY = stage.getPointerPosition().y;
var rect = new Konva.Rect({ //module rect
x: mouseX,
y: mouseY,
width: 100,
height: 50,
cornerRadius: 5,
fill: '#BEDBDD',
stroke: '#807C7B',
strokeWidth: 2,
draggable: true
});
group.add(rect);
var buttonRect = new Konva.Rect({ //button
x: mouseX+80,
y: mouseY+20,
width: 10,
height: 10,
cornerRadius: 1,
fill: 'blue',
stroke: '#807C7B',
strokeWidth: 1,
});
group.add(buttonRect)
var text = new Konva.Text({ //text on module
x: mouseX + 20,
y: mouseY + 20,
//fontFamily: 'Calibri',
fontSize: 16,
text: 'OSC',
fill: 'black'
});
group.add(text);
var randomFreq = getRandomInt();
var osc = new Tone.Oscillator(randomFreq, "sawtooth");
layer.add(group);
stage.add(layer);
buttonRect.on('click', function() {
rectButtonClicked = !rectButtonClicked;
if(rectButtonClicked){
osc.toMaster().start();
this.setFill('red');
} else {
osc.stop();
this.setFill('blue');
}
});
}
function getRandomInt() {
min = Math.ceil(100);
max = Math.floor(1000);
return Math.floor(Math.random() * (max - min)) + min;
}
var width = window.innerWidth;
var height = window.innerHeight;
//var drag = false;
var rectButtonClicked = false;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var group = new Konva.Group({
draggable: true
});
stage.on('contentClick', function() {
createModule();
});
function createModule() {
var mouseX = stage.getPointerPosition().x;
var mouseY = stage.getPointerPosition().y;
var rect = new Konva.Rect({ //module rect
x: mouseX,
y: mouseY,
width: 100,
height: 50,
cornerRadius: 5,
fill: '#BEDBDD',
stroke: '#807C7B',
strokeWidth: 2,
draggable: true
});
group.add(rect);
var buttonRect = new Konva.Rect({ //button
x: mouseX+80,
y: mouseY+20,
width: 10,
height: 10,
cornerRadius: 1,
fill: 'blue',
stroke: '#807C7B',
strokeWidth: 1,
});
group.add(buttonRect)
var text = new Konva.Text({ //text on module
x: mouseX + 20,
y: mouseY + 20,
//fontFamily: 'Calibri',
fontSize: 16,
text: 'OSC',
fill: 'black'
});
group.add(text);
var randomFreq = getRandomInt();
var osc = new Tone.Oscillator(randomFreq, "sawtooth");
layer.add(group);
stage.add(layer);
buttonRect.on('click', function() {
rectButtonClicked = !rectButtonClicked;
if(rectButtonClicked){
osc.toMaster().start();
this.setFill('red');
} else {
osc.stop();
this.setFill('blue');
}
});
}
function getRandomInt() {
min = Math.ceil(100);
max = Math.floor(1000);
return Math.floor(Math.random() * (max - min)) + min;
}
<script src="https://tonejs.github.io/build/Tone.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.7.6/konva.min.js"></script>
<div id="container"></div>
The stage.contentClick() listener is a special case to be used when you want the stage to listen to events on the stage content. However, the cancelBubble() function does not stop events bubbling from say a click on a shape to the stage.contentClick() listener.
To get the effect that you want, which is to give the impression that a click on the stage has happened, you need to add a rect that fills the stage and listen for events on that rect instead of the stage.
Below is a working example. The red background I added deliberately so you know there is something else above the stage. To remove this take out the fill color on the clickRect.
I also fixed up your buttons so that the contents are correctly grouped and drag together. You were almost correct but you needed the group to be created within in the createModule() function. You can see that I also made the group elements dragabble = false to complete the process.
I added a couple of console writes to show when the events fire.
[Also I got quite a shock when I switched on the tone for tone].
var width = window.innerWidth;
var height = window.innerHeight;
//var drag = false;
var rectButtonClicked = false;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
stage.add(layer);
var clickRect = new Konva.Rect({
x:0,
y:0,
width: width,
height: height,
fill: 'red',
stroke: '#807C7B',
strokeWidth: 2,
listening: 'true'
})
layer.add(clickRect);
clickRect.on('click', function() {
console.log('Stage click');
createModule();
});
function createModule() {
var group = new Konva.Group({ // move group create into createModule
draggable: true // we will make the elements not draggable - we drag the group
});
var mouseX = stage.getPointerPosition().x;
var mouseY = stage.getPointerPosition().y;
var rect = new Konva.Rect({ //module rect
x: mouseX,
y: mouseY,
width: 100,
height: 50,
cornerRadius: 5,
fill: '#BEDBDD',
stroke: '#807C7B',
strokeWidth: 2,
draggable: false // make the element not draggable - we drag the group
});
group.add(rect);
rect.on('click', function(evt){
console.log('Clicked on button');
})
var buttonRect = new Konva.Rect({ //button
x: mouseX+80,
y: mouseY+20,
width: 10,
height: 10,
cornerRadius: 1,
fill: 'blue',
stroke: '#807C7B',
strokeWidth: 1,
listening: true,
draggable: false // make the element not draggable - we drag the group
});
group.add(buttonRect)
var text = new Konva.Text({ //text on module
x: mouseX + 20,
y: mouseY + 20,
//fontFamily: 'Calibri',
fontSize: 16,
text: 'OSC',
fill: 'black',
draggable: false // make the element not draggable - we drag the group
});
group.add(text);
var randomFreq = getRandomInt();
var osc = new Tone.Oscillator(randomFreq, "sawtooth");
layer.add(group);
stage.add(layer);
buttonRect.on('click', function(evt) {
rectButtonClicked = !rectButtonClicked;
if(rectButtonClicked){
osc.toMaster().start();
this.setFill('red');
} else {
osc.stop();
this.setFill('blue');
}
});
}
function getRandomInt() {
min = Math.ceil(100);
max = Math.floor(1000);
return Math.floor(Math.random() * (max - min)) + min;
}
stage.draw(); // draw so we can see click rect.
<script src="https://tonejs.github.io/build/Tone.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.7.6/konva.min.js"></script>
<div id="container" style="background-color: gold;"></div>
I am working at a project with fabric js. I tried to minimize my problem, so I'm hoping that the code isn't too messed up.
I am creating some Objects which are linked with each other:
A Line, which contains a Start and an Endpoint
A Circle, which is StartPoint of 1 line and Endpoint of another line
with this combination i can create different shapes(like a polygon) and modify my move-functions for them too.
When a Circle is dragged, the related Lines are scaling and moving too. (in my code you can move the lines too and the shape is resized after that, but i didnt put it into this example, bc this short extract should be enough to show what my problem is.)
I got a little example in jsfiddle: https://jsfiddle.net/bxgox7cr/
When you look at the ends of the lines, you can clearly see a cut, so the eye soon recognize, that this is not a connected shape but rather some lines which are close to each other. Is there a way to modify the look of the lines, that the shape looks "closed"?
Here is my code, i tried to put some comments, that it is easy to read:
var canvas = new fabric.Canvas('canvas');
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
document.getElementById("canvas").tabIndex = 1000;
/** ------------creating a Line Object, which contains a start and an endpoint ------------**/
fabric.LineWithPoints = fabric.util.createClass(fabric.Line, {
initialize: function(points, options) {
options || (options = {});
this.callSuper('initialize', points, options);
options &&
this.set('type', options.type),
this.set('name', options.name),
this.set('start_point', options.start_point),
this.set('end_point', options.end_point),
this.set('current_x', options.current_x),
this.set('current_y', options.current_y)
},
setStartPointAndEndPoint: function(start_point, end_point) {
this.set({
start_point: start_point,
end_point: end_point
});
},
setValues: function(new_x1, new_y1, new_x2, new_y2) {
// console.log(this);
this.set({
x1: new_x1,
x2: new_x2,
y1: new_y1,
y2: new_y2
});
this.setCoords();
}
});
/**--- modifie the circle element, adding new functions for the movement of the object-------*/
fabric.LinePoint = fabric.util.createClass(fabric.Circle, {
initialize: function(options) {
options || (options = {});
this.callSuper('initialize', options);
options &&
this.set('subtype', 'line_point'),
this.set('x', this.left),
this.set('y', this.top)
},
setPointCoordinates: function(new_left, new_top) {
this.set({
x: new_left,
y: new_top,
left: new_left,
top: new_top
});
this.setCoords();
},
move: function(new_left, new_top) {
var wall_1 = this.line1;
var wall_2 = this.line2;
this.setPointCoordinates(new_left, new_top);
wall_1.setValues(wall_1.x1, wall_1.y1, this.getLeft(), this.getTop());
wall_2.setValues(this.getLeft(), this.getTop(), wall_2.x2, wall_2.y2);
canvas.renderAll();
},
});
/**------------------- Moving Function------------------------------------------------- */
canvas.on('object:moving', function(event) {
var object = event.target;
if (object.subtype == "line_point") {
object.move(object.getLeft(), object.getTop());
}
});
/**------------------------------ create functions for the objects -----------------------*/
function newCircleObject(left, top, wall_1, wall_2) {
var circle = new fabric.LinePoint({
left: left,
top: top,
strokeWidth: 2,
radius: 15,
fill: 'grey',
stroke: 'black',
opacity: 0.1,
perPixelTargetFind: true,
subtype: 'line_point',
includeDefaultValues: false
});
circle.hasControls = false;
circle.hasBorders = false;
circle.line1 = wall_1;
circle.line2 = wall_2;
return circle;
}
function newWallObject(coords) {
var wall = new fabric.LineWithPoints(coords, {
stroke: 'black',
strokeWidth: 6,
lockScalingX: true,
lockScalingY: true,
perPixelTargetFind: true,
subtype: 'line',
type: 'line',
padding: 10,
includeDefaultValues: false
});
wall.hasControls = false;
wall.hasBorders = false;
return wall;
}
/**------------------------------ adding the shapes--------------------------------*/
var wall_1 = newWallObject([100, 100, 100, 500]);
var wall_2 = newWallObject([100, 500, 500, 500]);
var wall_3 = newWallObject([500, 500, 500, 100]);
var wall_4 = newWallObject([500, 100, 100, 100]);
var end_point_1 = newCircleObject(wall_1.x1, wall_1.y1, wall_4, wall_1);
var end_point_2 = newCircleObject(wall_2.x1, wall_2.y1, wall_1, wall_2);
var end_point_3 = newCircleObject(wall_3.x1, wall_3.y1, wall_2, wall_3);
var end_point_4 = newCircleObject(wall_4.x1, wall_4.y1, wall_3, wall_4);
wall_1.setStartPointAndEndPoint(end_point_1.name, end_point_2.name);
wall_2.setStartPointAndEndPoint(end_point_2.name, end_point_3.name);
wall_3.setStartPointAndEndPoint(end_point_3.name, end_point_4.name);
wall_4.setStartPointAndEndPoint(end_point_4.name, end_point_1.name);
canvas.add(wall_1, wall_2, wall_3, wall_4, end_point_1, end_point_2, end_point_3, end_point_4);
Add strokeLineCap: 'round',:
function newWallObject(coords) {
var wall = new fabric.LineWithPoints(coords, {
stroke: 'black',
strokeWidth: 6,
lockScalingX: true,
lockScalingY: true,
perPixelTargetFind: true,
strokeLineCap: 'round',
subtype: 'line',
type: 'line',
padding: 10,
includeDefaultValues: false
});
wall.hasControls = false;
wall.hasBorders = false;
return wall;
}
I looked up: http://fabricjs.com/docs/fabric.Object.html#strokeLineCap
I am working on a fabricjs application & i need to set a inside stroke to object, it means apply stroke to a object without increase it's size.
eg if i apply strokeWidth 20 to 100*100 rect then it's size is also increase but i want if stroke is apply to object then size will also remain same
var recta = new fabric.Rect({
left: 10,
top: 10,
fill: '#000',
width: 100,
height: 100,
});
var rectb = new fabric.Rect({
left: 150,
top: 10,
fill: '#000',
width: 100,
height: 100,
});
canvas.add(recta, rectb);
rectb.set('stroke', '#f00');
rectb.set('strokeWidth', 20);
canvas.renderAll();
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas style="border:#000 1px solid" id="design-canvas" width="500" height="400">
<script type="text/javascript">
canvas = new fabric.Canvas('design-canvas');
</script>
Is there is any way or trick to apply stroke without increase size
Thanks in Advance
Assuming it'd just be for rectangles, The best way would likely be too attach 4 lines which you update the positions based off of that rectangles x, y, width and height. You can then set the stroked width of those lines separately.
The other way would be too simply resize the owning element. Look at this JSFiddle.
var canvas = new fabric.Canvas('c', { selection: false });
var circle, isDown, origX, origY;
canvas.on('mouse:down', function(o){
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
circle = new fabric.Circle({
left: pointer.x,
top: pointer.y,
radius: 1,
strokeWidth: 5,
stroke: 'red',
selectable: false,
originX: 'center', originY: 'center'
});
canvas.add(circle);
});
canvas.on('mouse:move', function(o){
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
circle.set({ radius: Math.abs(origX - pointer.x) });
canvas.renderAll();
});
canvas.on('mouse:up', function(o){
isDown = false;
});
I have a scrollbar that moves a layer, so the layer is moved while in the scrollbar's "dragmove" callback. This causes all bound events to be disconnected on the moved layer!
Please see this fiddle: http://jsfiddle.net/NY4QK/10/
var stage = new Kinetic.Stage({
container: 'container',
width: 200,
height: 200,
});
var fixedLayer = new Kinetic.Layer();
stage.add(fixedLayer);
var old_x = 100;
var old_y = 100;
var scroller = new Kinetic.Circle({
x: old_x,
y: old_y,
radius: 10,
fill: '#00F',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
scroller.on('dragmove', function(event){
var pos = scroller.getAbsolutePosition();
layer.move(pos.x - old_x, pos.y - old_y);
old_x = pos.x;
old_y = pos.y;
layer.draw();
});
fixedLayer.add(scroller);
var layer = new Kinetic.Layer();
stage.add(layer);
var rect = new Kinetic.Rect({
x: 10,
y: 10,
width: 50,
height: 50,
fill: '#0F0',
stroke: 'black',
strokeWidth: 4
});
rect.on('click', function(){
rect.remove();
layer.draw();
});
layer.add(rect);
layer.draw();
fixedLayer.draw();
Is this a bug or am I doing something wrong?
When you use a drag event, KineticJS create a temporary layer on top as a result of which your events where not getting registered after the dragmove.
Just add another handler for dragend like this:
scroller.on('dragend', function(event){
layer.moveToTop();
layer.draw();
});
And here is the updated fiddle.
For more details/explanation on the problem you faced, check this: https://github.com/ericdrowell/KineticJS/issues/219