Line width in Konva js - javascript

just need to create line with image background. I found this opportunity in official documentation here (https://konvajs.org/api/Konva.Line.html). For the start I just need to create line with tension, color fill and width, but the width property dont work(or I dont know how to do it).
My code and output:
let line2 = new Konva.Line({
x: 100,
y: 50,
points: [75, 75, 100, 200, 300, 140],
fill: "red",
tension: 0.5,
width: 50,
strokeWidth: 1,
stroke: 'green'
});

As mentioned in another answer, Konva#4.0.12 doesn't support pattern for strokes. But it is possible to do with 2d native canvas API
So you have to:
1 - Draw a custom shape and make a stroke manually
2 - Or you can use Blend mode to mix a line and an image:
const group = new Konva.Group();
layer.add(group);
// draw line
const line = new Konva.Line({
x: 100,
y: 50,
points: [75, 75, 100, 200, 300, 140],
fill: "red",
tension: 0.5,
strokeWidth: 1,
stroke: 'green'
});
group.add(line);
// "fill" line with globalCompositeOperation: 'source-in' rectangle
var lineClientRect = line.getClientRect();
var fillRect = new Konva.Rect({
x: lineClientRect.x,
y: lineClientRect.y,
width: lineClientRect.width,
height: lineClientRect.height,
fillPatternImage: img,
globalCompositeOperation: 'source-in'
});
layer.add(fillRect);
group.cache();
layer.draw();
It may be a bit tricky, because globalCompositeOperation may effect all the shapes around your line. To fix that we can add the line and the "fill" rectangle into the group and cache it.
Demo: https://jsbin.com/zodojezuma/2/edit?html,js,output

It is not possible with Konva current version (4.0.12) to apply a pattern to the stroke of a line object. The snippet below uses a closed line with image fill pattern, but I don't think this is what you area after, but I created it to see what was possible and so will post it here in case useful in the future.
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
// add the layer to the stage
stage.add(layer);
var layer2 = new Konva.Layer();
var rect1 = new Konva.Rect({width:10, height:10, fill: 'magenta'})
var rect2 = new Konva.Rect({width:5, height:5, fill: 'cyan'})
var rect3 = new Konva.Rect({x: 5, y:5, width:5, height:5, fill: 'cyan'})
stage.add(layer2);
layer2.add(rect1);
layer2.add(rect2);
layer2.add(rect3);
stage.draw();
// make an image out of layer2
// Note - be sure to include width & height when using toImage() otherwise uses size of stage and fillpatternrepeat will seem to fail.
var image = layer2.toImage({
width: 10, height: 10,
callback(img) {
// do stuff with img
var blob = new Konva.Line({
points: [23, 20, 23, 160, 70, 93, 150, 109, 290, 139, 270, 93],
fill: '#00D2FF',
fillPriority: 'pattern',
stroke: 'black',
strokeWidth: 5,
closed: true,
tension: 0.3
});
// add the shape to the layer
layer.add(blob);
stage.draw();
var imageObj = new Image();
imageObj.onload = function() {
blob.fillPatternImage(imageObj);
layer2.remove(); // no longer needed.
blob.fillPatternImage(imageObj)
layer.draw();
stage.draw();
};
imageObj.src = img.src;
}
});
<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/4.0.12/konva.min.js"></script>
<div id="container"></div>
<img id='theImg' style='width:100px; height: 100px; border:"2px solid lime"; z-index: 10 '></img>

Related

How to do mouse hit testing on the edge / border / stroked part of a shape

In my application, I need to detect the mouse events on the edge / border / stroked part of a shape - but not on the filled part. I have not found a way to do this.
I do not know how to get started with this but here is pseudo code of what I am trying to do.
shape.on('mousemove', function () {
if (mouse on the edge of the shape) {
// change the cursor to a star
} else {
// do nothing
}
});
To detect mouse hits on the edge of a shape only, use the fillEnabled:false property. What this does is tell Konva to disregard fill - meaning that any event-listening on the fill-part of the shape will be switched off. However, with great power great responsibility comes and the fillEnabled property also stops any visual fill you might want to appear.
Putting that together, if you want to hit-test the stroke part of a shape only you will need another transparent shape drawn on top of the visual shape to detect mouse events.
As a bonus, you can use the hitStrokeWidth property to make the hit-detecting region of the stroke wider - as if you set the stroke 'thicker' for purposes of mouse detection.
Snippet below shows this approach on a rect and random polygon.
// Set up a stage
stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
}),
// add a layer to draw on
layer = new Konva.Layer(),
rect = new Konva.Rect({
name: 'r1',
x: 220,
y: 20,
width: 100,
height: 40,
stroke: 'cyan',
fill: 'transparent',
fillEnabled: false
}),
poly = new Konva.Line({
points: [23, 20, 23, 160, 70, 93, 150, 109, 290, 139, 270, 93],
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 5,
closed: true,
fillEnabled: false,
hitStrokeWidth: 10
});
// Add the layer to the stage
stage.add(layer);
layer.add(rect, poly)
stage.draw();
rect.on('mouseover', function() {
$('#info').html('Rect MouseEnter')
})
rect.on('mouseout', function() {
$('#info').html('Rect mouseOut')
})
poly.on('mouseover', function() {
$('#info').html('Poly MouseEnter')
})
poly.on('mouseout', function() {
$('#info').html('Poly mouseOut')
})
body {
margin: 10;
padding: 10;
overflow: hidden;
background-color: #f0f0f0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva#^3/konva.min.js"></script>
<p>Move mouse over the shapes </p>
<p id='info'>Events show here</p>
<div id="container"></div>
It is easy to clone a shape to make an edge-event detecting version and place the clone over the original shape so that you can detect the edge-events specifically. See the following working snippet - enable the console to view the events sequence.
// Set up a stage
stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
}),
// add a layer to draw on
layer = new Konva.Layer(),
rect = new Konva.Rect({
name: 'r1',
x: 220,
y: 20,
width: 100,
height: 40,
stroke: 'cyan',
fill: 'magenta'
}),
poly = new Konva.Line({
points: [23, 20, 23, 160, 70, 93, 150, 109, 290, 139, 270, 93],
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 5,
closed: true,
hitStrokeWidth: 10
}),
// this is a clone of rect with fillEnabled set to false, placed 'above' rect in the z-order.
rect2 = rect.clone({
fillEnabled: false
}),
poly2 = poly.clone({
fillEnabled: false
}),
// Add the layer to the stage
stage.add(layer);
layer.add(rect, rect2, poly, poly2)
stage.draw();
rect.on('mouseover', function() {
showMsg('Rect MouseEnter');
})
rect2.on('mouseover', function() {
showMsg('Rect2 Edge MouseEnter');
})
rect2.on('mouseout', function() {
showMsg('Rect2 Edge mouseOut');
})
poly.on('mouseover', function() {
showMsg('Poly MouseEnter');
})
poly.on('mouseout', function() {
showMsg('Poly MouseOut');
})
poly2.on('mouseover', function() {
showMsg('Poly2 Edge MouseEnter');
})
poly2.on('mouseout', function() {
showMsg('Poly2 Edge MouseOut');
})
function showMsg(msg) {
console.log(msg)
$('#info').html(msg)
}
body {
margin: 10;
padding: 10;
overflow: hidden;
background-color: #f0f0f0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva#^3/konva.min.js"></script>
<p>Move mouse over the shapes </p>
<p id='info'>Events show here</p>
<div id="container"></div>
It's not a precise approach ,just a approximate way which detect if the cursor just near the out edge of the object.
stage.on('mousemove', function (e) {
var deta = 3;
var node8 = stage.getIntersection({x: e.evt.clientX, y: e.evt.clientY});
if(node8){
console.log(node8.getClassName()+"====mouse on object=====");
return;
}
var node = stage.getIntersection({x: e.evt.clientX+deta, y: e.evt.clientY});
if(node){
console.log(node.getClassName()+"====mouse on edge=====");
return;
}
var node1 = stage.getIntersection({x: e.evt.clientX, y: e.evt.clientY+deta});
if(node1){
console.log(node1.getClassName()+"====mouse on edge=====");
return;
}
var node2 = stage.getIntersection({x: e.evt.clientX+deta, y: e.evt.clientY+deta});
if(node2){
console.log(node2.getClassName()+"====mouse on edge=====");
return;
}
var node3 = stage.getIntersection({x: e.evt.clientX-deta, y: e.evt.clientY});
if(node3){
console.log(node3.getClassName()+"====mouse on edge=====");
return;
}
var node4 = stage.getIntersection({x: e.evt.clientX, y: e.evt.clientY-deta});
if(node4){
console.log(node4.getClassName()+"====mouse on edge=====");
return;
}
var node5 = stage.getIntersection({x: e.evt.clientX-deta, y: e.evt.clientY-deta});
if(node5){
console.log(node5.getClassName()+"====mouse on edge=====");
return;
}
var node6 = stage.getIntersection({x: e.evt.clientX-deta, y: e.evt.clientY+deta});
if(node6){
console.log(node6.getClassName()+"====mouse on edge=====");
return;
}
var node7 = stage.getIntersection({x: e.evt.clientX+deta, y: e.evt.clientY-deta});
if(node7){
console.log(node7.getClassName()+"====mouse on edge=====");
return;
}
});

kineticjs change tween start color

I am trying to have a three state mouseover for a rectangle in KineticJS. The rectangle starts as white, then a mouseover on the rectangle changes the colour to red, which then starts a tween and tweens over 1 second to white. Another mouseover would repeat the process I was able to get this running using the version 4 library, but not version 5. JsFiddle: http://jsfiddle.net/cmh600/uFFN9/12/
Any help greatly appreciated
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: 20,
y: 20,
width: 100,
height: 50,
fillRed: 255,
fillGreen: 255,
fillBlue: 255,
stroke: 'black',
strokeWidth: 2,
});
layer.add(rect);
stage.add(layer);
var tween = new Kinetic.Tween({
node: rect,
duration: 2,
opacity: 1,
easing: Kinetic.Easings.Linear,
fillRed: 255,
fillGreen: 255,
fillBlue: 255
});
rect.on("mouseover", function() {
rect._setAttr('fillRed',255);
rect._setAttr('fillGreen',0);
rect._setAttr('fillBlue',0);
rect.draw();
tween.play();
});
You must declare the tween after set the new fill color of the rect, because else, starting values will be wrong (and it will be transformed to white instantly). Working with KJ5 :
rect.on("mouseover", function() {
rect.fillBlue(0);
rect.fillGreen(0);
rect.draw();
var tween = new Kinetic.Tween({
node: rect,
duration: 2,
opacity: 1,
easing: Kinetic.Easings.Linear,
fillRed: 255,
fillGreen: 255,
fillBlue: 255
});
tween.play();
});

Why Isn't My Shadow Blurry? (Kinetic JS)

I'm learning KineticJS and tinkering with shadows. I made a shape and gave it shadow blur, but the shadow isn't blurry. I'm using the latest version of Chrome. This is the code I'm using:
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: 250,
y: 120,
width: 100,
height: 50,
fill: '#00D2FF',
stroke: 'black',
strokeWidth: 4,
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: {
x: 10,
y: 10
},
shadowOpacity: 0.5
});
// add the shape to the layer
layer.add(rect);
// add the layer to the stage
stage.add(layer);
Its copied from here: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-shadows/
Here's a Fiddle: http://jsfiddle.net/acbabis/Tu8Qh/
Yes, this is a misbehavior in Chrome which will apply a shadow on both the Kinetic stroke and a second on the Kinetic fill (double shadowing).
The current version of KineticJS has not yet accounted for / corrected this misbehavior satisfactorily.
The workaround is to:
use only a stroke or fill, but not both.
draw 1 filled rect and shadow it. Then draw a second stroked rect on top.

How can I make a Kinetic.Line fire an event like the other Kinetic shapes?

You can interact with this code here on jsFiddle
In the fiddle you can see that I have made a flag (Kinetic.Rect) on a flagpole (Kinetic.Line). I desire to fire an event when the user moves the mouse over any portion of the flag or flagpole. In prior attempts I have attached event handlers to each shape individually, only to learn that Kinetic.Line does not fire events.
In this latest attempt I added the shapes to a group and attached the handler to the group thinking this would solve the issue: it does not.
Is there any way to achieve the desired behavior? Thank you, and remember to press F12 to see the handler's console messages.
var handler = function(e) {
console.log("Event fired.");
};
var stage = new Kinetic.Stage({
container: 'testBlock',
width: 200,
height: 200
});
var layer = new Kinetic.Layer();
var group = new Kinetic.Group();
var rect = new Kinetic.Rect({
x: 75,
y: 10,
width: 50,
height: 50,
fill: 'silver',
});
line = new Kinetic.Line({
points: [
{x: 125, y: 10},
{x: 125, y: 160},
],
stroke: 'black',
strokeWidth: 1
});
// add the shapes to the group
group.add(rect);
group.add(line);
// event handler for the group
group.on("mouseover", handler);
// add the group to the layer
layer.add(group);
// add the layer to the stage
stage.add(layer);
Kinetic.Line's have trouble with events when the stroke is too small, you can see this evident with any line with stroke < 3px.
This was the response I got from Eric Rowell (creator of KineticJS):
yep, KineticJS ignores the anti-aliased pixels. If you're drawing a 1px diagonal line, and you want it to be detectable, you need to create a custom hit function to define the hit region. You probably will want to create a hit region that's a line which is about 5px thick or so. Here's an example on creating custom hit regions:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-custom-hit-function-tutorial/
So in addition to Ani's answer, you can also use the drawHitFunc property to make a custom hit region for the line that is thicker than the actual line:
line = new Kinetic.Line({
points: [
{x: 125, y: 10},
{x: 125, y: 160},
],
stroke: 'black',
strokeWidth: 1,
drawHitFunc: function(canvas) {
var x1=this.getPoints()[0].x;
var x2=this.getPoints()[1].x;
var y1=this.getPoints()[0].y;
var y2=this.getPoints()[1].y;
var ctx = canvas.getContext();
ctx.beginPath();
ctx.lineWidth = 1;
ctx.moveTo(x1-3,y1-3);
ctx.lineTo(x1-3,y2+3);
ctx.lineTo(x2+3,y2+3);
ctx.lineTo(x2+3,y1-3);
ctx.closePath();
canvas.fillStroke(this);
}
});
jsfiddle
Try this fiddle
I'm using Kinetic.Rect with width=1 and height= y2-y1 of your line.
line = new Kinetic.Rect({
x: 125, y: 10,
width: 1, height: 150,
fill: 'black',
});

Create a masked overlay with draggable/resizable underneath

I'm trying to get kineticjs down and worked out a little app which makes my images draggable and resizable. So far so good;
However: I want an overlay with a variable height/width block in the center which should show the image underneath(With the draggable/resizable intact) with a semi-transparent overlay.
I want to be able to still resize/drag behind the overlay while the overlay is still intact(Like this, but with kineticjs: http://envyum.nl/pointer/)
Is there a way to do so? By cutting a block out of an overlaying rectangle perhaps? And can the mouse ignore the overlay such as pointer-events: none can in css3?
Thanks in advance,
I have a sample of what I was talking about in the comments above: http://jsfiddle.net/KwQBB/
This did not require a new layer, but might be good practice to do so.
You can tailor the logic to be whatever you want, especially to simulate a 'cut-out'
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 500
});
var layer = new Kinetic.Layer();
var pentagon = new Kinetic.RegularPolygon({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
sides: 5,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4,
draggable: true,
dragOnTop: false
});
var rect1 = new Kinetic.Rect({ // overlay
x: 0,
y: 0,
width: stage.getWidth(),
height: 100,
fill: 'gray',
opacity: 0.5,
listening: false // <------ Extremely important
});
var rect2 = new Kinetic.Rect({ // overlay
x: 0,
y: stage.getHeight()/2,
width: stage.getWidth(),
height: 100,
fill: 'gray',
opacity: 0.5,
listening: false // <------ Extremely important
});
// add the shape to the layer
layer.add(pentagon);
layer.add(rect1);
layer.add(rect2); // add more rectangles to complete overlay
// add the layer to the stage
stage.add(layer);

Categories