Kineticjs - Fixed fill pattern of shape being rotated - javascript

I use kineticjs to work with shapes and transitions. For now I have made next code example:
http://jsfiddle.net/z6LaH/2/
hexagon = new Kinetic.RegularPolygon({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
sides: 6,
radius: hexRadius,
cornerRadius: 0,
fillPatternImage: img,
fillPatternOffset: [150, -150],
//fill: 'white',
stroke: 'black',
strokeWidth: 0
});
hexagon.on('mouseover touchstart', function() {
this.transitionTo({
cornerRadius: transRadius,
rotation: Math.PI / 2,
scale: {x: 0.75, y: 0.75},
easing: 'ease-in',
duration: duration,
callback: function() {
hexagon.transitionTo({
scale: {x: 1.1, y: 1.1},
duration: duration * 7,
easing: 'elastic-ease-out'
});
}
});
});
As you can see, fill pattern is rotating with shape. I need it to be fixed. So my question is:
Is it posible to make fixed fill pattern, while shape is rotating, and how?
UPDATE:
I got next approach: rotate fill pattern in opposite direction.
http://jsfiddle.net/z6LaH/3/
Is there any more elegant way to do the same?

Eric has just added the ability to save a user-defined clipping function to layers and groups.
First, you define a function that draws a clipping region on a layer or group
var layer = new Kinetic.Layer({
clipFunc: function(canvas) {
var context = canvas.getContext();
context.rect(0, 0, 400, 100);
}
});
Then you call the .clip() function to apply the clip. Here is Kinetic’s clip() function in the source code:
_clip: function(container) {
var context = this.getContext();
context.save();
this._applyAncestorTransforms(container);
context.beginPath();
container.getClipFunc()(this);
context.clip();
context.setTransform(1, 0, 0, 1, 0, 0);
}
The clip() function applies existing transforms before doing the clip. If you don’t like the transform part of the Kinetic function, you can always use “container.getClipFunc()” and then build your own myClipWithoutTransform() based on the _clip function above.

Related

How to clip image in fabricjs version 4 beta

I updated fabricjs for new control feature.
But my old image clipping is not working anymore since clipTo in fabric.Object is removed in new version.
How can I clip image without using clipTo, in change log they said I should use clipPath instead.
img.set({
clipTo: function (ctx) {
ctx.moveTo(0, 0);
ctx.arc(0, 0,300, -Math.PI/6, -Math.PI+Math.PI/6 , true);
}
});
Here is jsfiddle
Also this official demo won't work in version 4 beta
http://fabricjs.com/clipping
So, clipTo is deprecated since version 2 and was removed in version 4. The correct way of clipping is to use the clipPath property. Here is a simple example:
var radius = 150;
var clipPath = new fabric.Circle({
left: 0,
top: 0,
radius: radius,
startAngle: 0,
angle: Math.PI * 2,
originX: "top"
});
fabric.Image.fromURL("../public/pug_small.jpg", function(img) {
img.scale(0.5).set({
left: 100,
top: 100,
angle: -15,
clipPath: clipPath
});
canvas.add(img).setActiveObject(img);
});
Here is the official tutorial for the clipPath http://fabricjs.com/clippath-part1
Recent discussion about clipPath in the beta 4 version: https://github.com/fabricjs/fabric.js/issues/6159
And a sample SandBox demo: https://codesandbox.io/s/stackoverflow-60664120-fabric-js-400-beta8-mi0y7
I found temporary solution but it is not really answer:
I draw circle and polygon
var radius = 100;
var start = -150*Math.PI/180
var end = -30*Math.PI/180
let point1 = new fabric.Point(
(radius+1)*Math.cos(start),
(radius+1)*Math.sin(start)
)
let point2 = new fabric.Point(
(radius+1)*Math.cos(end),
(radius+1)*Math.sin(end)
)
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function (img) {
img.scale(1).set({
left: 0,
top: 0,
clipPath:new fabric.Group([
new fabric.Circle({
originX:'center',
originY:'center',
radius,
startAngle: start,
endAngle: end,
stroke:false,
strokeWidth:6
}),
new fabric.Polygon([point1,{x:0,y:0},point2],{
originX:'center',
originY:'center',
strokeWidth:6
})
])
);
canvas.add(img).setActiveObject(img);
});
http://jsfiddle.net/mudin/z75nvgqs/31/
Help me to find better solution

Creating complex clipping path for image?

I'm new to fabricjs (and to Javascript development in general). I am "porting" a legacy Flex/Actionscript project and need to enable the user to create a complex clipping path for an image.
My approach in Actionscript was to use the Actionscript Graphics class using BlendMode.ERASE to "erase" from the yellow base rectangle (i.e. give the appearance of erasing) and then using that set of rects to create a bitmap to serve as an alpha channel for the final image (Step 3) created on the fly.
Can anyone suggest how I might accomplish a similar functionality in Fabric? It doesn't seem to support HTML5 Canvas Blend modes and while I see that it supports clipping paths for images, I'm not seeing how I can enable the user to interactively create a clipping path without doing lots of intersection checks to try to derive the points to create a new path on the fly.
Thanks!
Step 1: After the user has drawn a base rectangle, drag-option/alt-key enables them to draw a rectangle (the red line) which will be subtracted from the base rect.
Step 2: The base rect is shown with the subtraction.
Step 3: The base rect is used to clip or mask a section of the base image
Step 1
Step 2
Step 3
Will Tower,
There is no easy way to do it. Here are the steps:
Draw 'Yellow' rectangle
Draw 'Red' rectangle
Use clipping library like PolyBool for intersection and xor operations
Convert drawing result into the clipped path of combining rectangles
clip your image
I created some quick fiddle. You have to click on a each button to clip. It won't clip if you will not add 2 rectangles on the canvas. This is very simple example. In order to work properly you have to draw rectangles with mouse (make them dynamic). Also, this logic is not accounting for these variations (you have to work on them as well):
For these use cases Clipping Library will return to you 2 set of results, which means different logic should be implemented.
Actual code without jQuery, FabriJs, and PolyBool libraries:
var imgURL = 'http://fabricjs.com/lib/pug.jpg';
var clipYellowRect = null;
var clipRedRect = null;
var pug = null;
var canvas = new fabric.Canvas('c');
// insert image into canvas
var pugImg = new Image();
pugImg.onload = function (img) {
pug = new fabric.Image(pugImg, {
angle: 0,
width: 500,
height: 500,
left: 100,
top: 50,
scaleX: 0.5,
scaleY: 0.5,
clipName: 'pug',
});
canvas.add(pug);
};
pugImg.src = imgURL;
//draw yellow rectangle
$('#btnYellowRect').on('click', function(){
clipYellowRect = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 120,
top: 60,
width: 200,
height: 200,
fill: 'rgba(255,255,0,0.5)',
strokeWidth: 0,
selectable: false
});
canvas.add(clipYellowRect);
});
//draw red rectangle
$('#btnRedRect').on('click', function(){
clipRedRect = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 90,
top: 120,
width: 100,
height: 100,
strokeWidth: 3,
fill: 'transparent',
stroke: 'rgba(255,0,0,1)', /* use transparent for no fill */
strokeWidth: 0,
selectable: false
});
canvas.add(clipRedRect);
});
//clip
$('#btnClip').on('click', function(){
var yellowRectRegion = getRegion(clipYellowRect);
var redRectRegion = getRegion(clipRedRect);
//determine inersection
var intersectResult = PolyBool.intersect({
regions: [yellowRectRegion],
inverted: false
}, {
regions: [redRectRegion],
inverted: false
});
//generate clipping path
var xorResult = PolyBool.xor({
regions: [yellowRectRegion],
inverted: false
}, {
regions: intersectResult.regions,
inverted: false
});
clipImage(xorResult.regions[0]);
});
//prepare data for clipping library
function getRegion(rect){
return [[rect.left, rect.top],
[rect.left + rect.width, rect.top],
[rect.left + rect.width, rect.top + rect.height],
[rect.left, rect.top + rect.height]]
}
function clipImage(points){
//actual clipping
pug.clipTo = function (ctx) {
var scaleXTo1 = (1 / pug.scaleX);
var scaleYTo1 = (1 / pug.scaleY);
ctx.save();
var ctxLeft = -( pug.width / 2 );
var ctxTop = -( pug.height / 2 );
ctx.translate( ctxLeft, ctxTop );
ctx.scale(scaleXTo1, scaleYTo1);
ctx.beginPath();
console.log(points)
ctx.moveTo(points[0][0] - pug.oCoords.tl.x, points[0][1] - pug.oCoords.tl.y);
for (var i=1; i < points.length; i++){
ctx.lineTo(points[i][0] - pug.oCoords.tl.x, points[i][1] - pug.oCoords.tl.y);
}
ctx.closePath();
ctx.restore();
};
clipYellowRect.remove();
clipRedRect.remove();
canvas.renderAll();
}
Hopefully it will help you.

Kineticjs 'mouseover' tween is working but 'mouseout' tween is not

I am currently trying to make it so a grid of triangles will expand and move to the 'top' layer when you mouse over them, but then shrink back down to original size and move back to the original layer when you mouse out of them. However right now only the mouse over function is working correctly.
Here is the current code Im working with:
var stage = new Kinetic.Stage({
container: 'container',
width: 300,
height: 300
});
var layer = new Kinetic.Layer();
var secondLayer = new Kinetic.Layer();
var tri = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 3,
radius: 30,
fill: '#111111',
closed: true,
shadowColor: '#5febff',
shadowBlur: 5,
shadowOpacity: 0.18,
});
layer.add(tri);
stage.add(layer);
stage.add(secondLayer);
// bind stage handlers
layer.on('mouseover', function(evt) {
var shape = evt.targetNode;
shape.moveTo(secondLayer);
stage.draw()
var tween = new Kinetic.Tween({
node: shape,
duration: 0.05,
scaleX: 1.5,
scaleY: 1.5
});
tween.play()
});
secondLayer.on('mouseout', function(evt) {
var shape = evt.targetNode;
var tween = new Kinetic.Tween({
node: shape,
duration: 0.05,
scaleX: 1.5,
scaleY: 1.5
});
tween.reverse()
shape.moveTo(layer);
stage.draw();
});
And here is a jsfiddle: http://jsfiddle.net/y2C3Z/1/
You can use tween.reverse() only after tween.play(). So you can just change scale attributes to original values.
Don't move shape between layers while shape under a tween. You can move shape after tween is done.
http://jsfiddle.net/y2C3Z/3/

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',
});

How to draw hyperbola using javascript

Can some one help me to draw hyperbola using JavaScript.As i am able to draw parabola now. but in hyperbola getting confused. For parabola i used:
function drawParabola(y)
{
myGraph.context.clearRect(0, 0, myGraph.canvas.width, myGraph.canvas.height);
myGraph = new Graph({
canvasId: "myCanvas",
minX: -10,
minY: -10,
maxX: 10,
maxY: 10,
unitsPerTick: 1
});
myGraph.drawEquation(function(x){
return y*x*x;
}, "red", 2);
}
Please suggest me for hyperbola

Categories