Add style to `clipPath` Fabric.js Object - javascript

I need a canvas with a specific shape. I was able to do so thanks to this post using clipPath. The problem is I can't style it at all.
Here an example with a circle shaped canvas. I try to apply the same style to the object inside the container and to the container itself, but as you can see only the object inside the container is styled. How can I apply a stroke or a shadow to this circle container (clipPath)?
const canvas = new fabric.Canvas("canvas", {backgroundColor: "#d3d3d3"})
const container = new fabric.Circle({
radius: 150,
left: 50,
top: 50,
stroke: "rgba(0, 0, 0, 0.5)",
strokeWidth: 1,
})
const obj = new fabric.Rect({
width: 100,
height: 100,
left: 150,
top: 150,
fill: "blue",
stroke: "rgba(0, 0, 0, 0.5)",
strokeWidth: 1,
})
const shadow = new fabric.Shadow({
color: "rgba(0, 0, 0, 0.5)",
blur: 10,
})
container.set("shadow", shadow)
obj.set("shadow", shadow)
canvas.clipPath = container
canvas.add(obj)
canvas.requestRenderAll()
#canvas {
background: #e8e8e8;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.0.0-beta.5/fabric.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
[EDIT] I found a company who does that: https://www.plaqueomatic.fr/plaqueomatic/plastique. They use fabric v3.6.0. How can I achieve the same?

When clipping the canvas itself, styles like dropshadows are best applied using CSS. I've also enabled the controlsAboveOverlay property to make the resizing controls visible outside the clippath (feel free to disable if you don't need that).
const canvas = new fabric.Canvas("canvas", {
backgroundColor: "#d3d3d3",
controlsAboveOverlay: true
})
const container = new fabric.Circle({
radius: 150,
left: 50,
top: 50,
stroke: "rgba(0, 0, 0, 0.5)",
strokeWidth: 1,
})
const obj = new fabric.Rect({
width: 100,
height: 100,
left: 150,
top: 150,
fill: "blue",
stroke: "rgba(0, 0, 0, 0.5)",
strokeWidth: 1,
})
const shadow = new fabric.Shadow({
color: "rgba(0, 0, 0, 0.5)",
blur: 10,
})
obj.set("shadow", shadow)
canvas.clipPath = container
canvas.add(obj)
canvas.requestRenderAll()
#canvas {
-webkit-filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, 0.5));
filter: drop-shadow(0px 3px 3px rgba(0, 0, 0, 0.5));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.0.0-beta.5/fabric.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>

Related

Fabric js, Rotating point in each side

I would like to configure fabricJS to add extra rotation controls. I know there is a control API, I am not sure how to use it. control-api
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({ width: 100, height: 100, fill: 'red', transparentCorners: false, top: 50, left: 50 });
canvas.add(rect);
canvas.setActiveObject(rect);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
<canvas id="c" width="400" height="400" ></canvas>
The new control api allows you to add controls to the
fabric.Object.prototype.controls
This object has a key for each control, and the one that actually hold the rotation is the controls.mtr by default.
we can add mtr1, mtr2 and mtr3 contols in this way.
const originalControl = fabric.Object.prototype.controls.mtr;
fabric.Object.prototype.controls.mtr1 = new fabric.Control({
x: -0.5,
y: 0,
offsetX: -40,
actionHandler: originalControl.actionHandler,
});
Look in the snippet for more details
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({ width: 100, height: 100, fill: 'red', transparentCorners: false, top: 50, left: 50 });
canvas.add(rect);
const originalControl = fabric.Object.prototype.controls.mtr;
fabric.Object.prototype.controls.mtr1 = new fabric.Control({
x: -0.5,
y: 0,
offsetX: -40,
actionHandler: originalControl.actionHandler,
withConnection: true,
actionName: 'rotate',
});
fabric.Object.prototype.controls.mtr2 = new fabric.Control({
x: 0.5,
y: 0,
offsetX: 40,
actionHandler: originalControl.actionHandler,
withConnection: true,
actionName: 'rotate',
});
fabric.Object.prototype.controls.mtr3 = new fabric.Control({
x: 0,
y: 0.5,
offsetY: 40,
actionHandler: originalControl.actionHandler,
withConnection: true,
actionName: 'rotate',
});
canvas.setActiveObject(rect);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
<canvas id="c" width="400" height="400" ></canvas>
Ugly but work.
A note, not specifying actionName: 'rotate' does not keep the rotation point in the center. This is obviously wrong and should be addressed in the library probably.

How correctly apply globalCompositeOperation on canvas's elements?

I have an issue with globalCompositeOperation.
My goal is to make Blue element displayed only inside of Red element and should not be visible outside of Red rectangle at all (kind of Overflow-Hidden effect). Plus, both Red and Blue must have Transformation ability (both editable).
As you can see, if I'll add one more element into the canvas (yellow element), the Blue one become visible on the area where Yellow and Blue overlaps.
http://jsfiddle.net/redlive/q4bvu0tb/1/
var canvas = new fabric.Canvas('c');
var yellow = new fabric.Circle({
top: 200,
left: 0,
radius: 100,
strokeDashArray: [5, 5],
stroke: 'black',
strokeWidth: 5,
fill: 'yellow'
});
canvas.add(yellow);
var red = new fabric.Rect({
top: 0,
left: 0,
width: 300,
height: 300,
strokeDashArray: [5, 5],
stroke: 'black',
strokeWidth: 5,
fill: 'red',
rx: 40
});
canvas.add(red);
var blue = new fabric.Circle({
top: 150,
left: 80,
radius: 100,
strokeDashArray: [5, 5],
stroke: 'black',
strokeWidth: 5,
fill: 'blue',
globalCompositeOperation: 'source-atop'
});
canvas.add(blue);
var green = new fabric.Circle({
top: 0,
left: 0,
radius: 100,
strokeDashArray: [5, 5],
stroke: 'black',
strokeWidth: 5,
fill: 'green'
});
canvas.add(green);
Mandatory Conditions:
Preserve elements appearance on Canvas.
No clipping (since clipping
does not allow to add background color and image in the same time)
It could be accomplish-able using the following steps ...
remove the yellow element before drawing the blue one
after drawing the blue element set the yellow element's globalCompositeOperation to destination-over and add it back
var canvas = new fabric.Canvas('c');
var yellow = new fabric.Circle({
top: 200,
left: 0,
radius: 100,
strokeDashArray: [5, 5],
stroke: 'black',
strokeWidth: 5,
fill: 'yellow'
});
canvas.add(yellow);
var red = new fabric.Rect({
top: 0,
left: 0,
width: 300,
height: 300,
strokeDashArray: [5, 5],
stroke: 'black',
strokeWidth: 5,
fill: 'red',
rx: 40
});
canvas.add(red);
canvas.remove(yellow); //remove yellow
var blue = new fabric.Circle({
top: 150,
left: 80,
radius: 100,
strokeDashArray: [5, 5],
stroke: 'black',
strokeWidth: 5,
fill: 'blue',
globalCompositeOperation: 'source-atop'
});
canvas.add(blue);
yellow.set({globalCompositeOperation: 'destination-over'}); //set gCO for yellow
canvas.add(yellow); //add yellow back
var green = new fabric.Circle({
top: 0,
left: 0,
radius: 100,
strokeDashArray: [5, 5],
stroke: 'black',
strokeWidth: 5,
fill: 'green'
});
canvas.add(green);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<canvas id="c" width="800" height="800"></canvas>

Fabric js: Disable stroke shadow of imported SVG

I am working on fabric js app & i trying to apply shadow to imported SVG without stroke shadow
when i apply shadow to SVG then shadow is also applied to stroke of svg but i don't want this
Thanks in advance for help
My tried code is
canvas = new fabric.Canvas('canvas');
var shadow = {
color: 'rgba(0,0,0,0.6)',
blur: 20,
offsetX: 10,
offsetY: 10,
opacity: 0.6,
fillShadow: true,
strokeShadow: false
}
var s = '<svg width="100" height="100"><polygon points="30 0, 70 0, 99 33, 99 66, 70 99, 30 99, 0 66, 0 33" fill="#bbb"/></svg>'
fabric.loadSVGFromString(s, function(objects, options) {
var shape = fabric.util.groupSVGElements(objects, options);
//shape.setShadow("10px 10px 5px rgba(94, 128, 191, 0.5)");
if (shape.isSameColor && shape.isSameColor() || !shape.paths) {
console.log('shape');
shape.set({
strokeWidth: 3,
stroke: '#000'
});
}
if (shape.paths) {
console.log('shape path');
/*for (i in shape.paths) {
shape.paths[i].set({
strokeWidth: strokewidth,
stroke: strokecolor
});
}*/
allObjects = shape.paths;
$.each(allObjects, function(key, value) {
value.set({
strokeWidth: 3,
stroke: '#000'
});
});
}
//shape.setShadow("10px 10px 5px rgba(94, 128, 191, 0.5)");
shape.setShadow(shadow);
canvas.add(shape).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 id='canvas' width="500" height="400" style="border:#000 1px solid;"></canvas>
canvas = new fabric.Canvas('canvas');
var shadow = {
color: 'rgba(0,0,0,0.6)',
blur: 20,
offsetX: 10,
offsetY: 10,
opacity: 0.6,
affectStroke: false
}
var s = '<svg width="100" height="100"><polygon points="30 0, 70 0, 99 33, 99 66, 70 99, 30 99, 0 66, 0 33" fill="#bbb"/></svg>'
fabric.loadSVGFromString(s, function(objects, options) {
var shape = fabric.util.groupSVGElements(objects, options);
if (shape.paths) {
allObjects = shape.paths;
$.each(allObjects, function(key, value) {
value.set({
shadow: shadow,
strokeWidth: 3,
stroke: '#000',
});
value.shadow.affectStroke = false;
});
}
canvas.add(shape).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 id='canvas' width="500" height="400" style="border:#000 1px solid;"></canvas>
I refined your code to apply the shadow just on paths instead of pathGroup. For what concerns shadow.affectStroke = false not working on pathGroups, this may be a bug, or simply fabric.PathGroup shadow feature is poorly implemented.

Angularjs + Kineticjs mouse events

I want to use Angularjs with Kineticjs. The problem is that, it seems angular is overriding mouse events on my stage object.
The following html works fine as stand alone file, you can test it. But if you put this as an Angularjs template, the mouse events stop working.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
#container {
background-color: black;
background-image: url("http://www.html5canvastutorials.com/demos/assets/line-building.png");
background-position: 1px 0px;
background-repeat: no-repeat;
width: 580px;
height: 327px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.5.1.min.js"></script>
<script defer="defer">
function getData() {
return {
'1st Floor': {
color: 'blue',
points: [366, 298, 500, 284, 499, 204, 352, 183, 72, 228, 74, 274]
},
'2nd Floor': {
color: 'red',
points: [72, 228, 73, 193, 340, 96, 498, 154, 498, 191, 341, 171]
},
'3rd Floor': {
color: 'yellow',
points: [73, 192, 73, 160, 340, 23, 500, 109, 499, 139, 342, 93]
},
'Gym': {
color: 'green',
points: [498, 283, 503, 146, 560, 136, 576, 144, 576, 278, 500, 283]
}
}
}
function updateTooltip(tooltip, x, y, text) {
tooltip.getText().setText(text);
tooltip.setPosition(x, y);
tooltip.show();
}
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 325
});
var shapesLayer = new Kinetic.Layer();
var tooltipLayer = new Kinetic.Layer();
var tooltip = new Kinetic.Label({
opacity: 0.75,
visible: false,
listening: false,
text: {
text: '',
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white'
},
rect: {
fill: 'black',
pointerDirection: 'down',
pointerWidth: 10,
pointerHeight: 10,
lineJoin: 'round',
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: 10,
shadowOpacity: 0.5
}
});
tooltipLayer.add(tooltip);
// get areas data
var areas = getData();
// draw areas
for(var key in areas) {
var area = areas[key];
var points = area.points;
var shape = new Kinetic.Polygon({
points: points,
fill: area.color,
opacity: 0,
// custom attr
key: key
});
shapesLayer.add(shape);
}
stage.add(shapesLayer);
stage.add(tooltipLayer);
stage.on('mouseover', function(evt) {
var shape = evt.targetNode;
shape.setOpacity(0.5);
shapesLayer.draw();
});
stage.on('mouseout', function(evt) {
var shape = evt.targetNode;
shape.setOpacity(0);
shapesLayer.draw();
tooltip.hide();
tooltipLayer.draw();
});
stage.on('mousemove', function(evt) {
var shape = evt.targetNode;
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y - 5;
updateTooltip(tooltip, x, y, shape.attrs.key);
tooltipLayer.batchDraw();
});
</script>
How does event handling work on angular? And how could I prevent from overriding events like:
stage.on('mouseover', function(evt) {
var shape = evt.targetNode;
shape.setOpacity(0.5);
shapesLayer.draw();
});
Thanks!
Ok, it seems Angular was capturing all DOM events.
The solution is to put Kineticjs tag before angular.js(and probably before jQuery too), and be sure you don't declare the tag again elsewhere in any view.

KineticJS change property after object creation (NOOB)

Hello guys I am a noob in KineticJS and I want to know how to change property values. For example for a rectangle after its creation:
var rect = new Kinetic.Rect({
x: 239,
y: 75,
width: 100,
height: 50,
fill: "#00D2FF",
stroke: "black",
strokeWidth: 4
});
How would I do something like this:
rect.NewProperty({
x: 100,
y: 30,
width: 100,
height: 50,
fill: "#cccccc",
});
and leave the other properties the same?
Like this:
var rect = new Kinetic.Rect({
x: 239,
y: 75,
width: 100,
height: 50,
fill: "#00D2FF",
stroke: "black",
strokeWidth: 4
});
rect.setFill("#D200FF");
rect.setStrokeWidth(1);
thanks for the info..
i also tried
rect.setWidth(100);
and
rect.setHeight(100);
it works :)

Categories

Resources