Is it possible to blur a closed path element that has a fill using paperjs? Would I have to create an SVG feGaussianBlur primitive for each path that I wanted to blur? How would that affect performance if there were a few hundred path elements on the canvas? I thought about making use of the shadowBlur with some color magic, where the shadow would match the selected path, but it's not quite the same effect. I do not want to use raster.
A trick could be to use the shadowBlur property of the item.
The only thing is that the shadow is only drawn if the item has a fill.
And we don't want the fill to be displayed but only the shadow.
So we could make clever usage of blend modes to hide the fill but not the shadow.
Here's a simple sketch demonstrating this.
new Path.Circle({
center: view.center,
radius: 50,
shadowColor: 'black',
shadowBlur: 20,
selected: true,
// set a fill color to make sure that the shadow is displayed
fillColor: 'white',
// use blendmode to hide the fill and only see the shadow
blendMode: 'multiply',
});
edit
This technique indeed seems to reach a limit when trying to stack items on top of each other.
We can prevent the multiply blend mode from "bleeding out" by wrapping the item into a group which has a blend mode of source-over. This has the effect to scope the children blend modes.
But there is still a trick to find to compose the items together.
Here's a sketch demonstrating where I stopped my investigation.
I'm quite sure that you can find a solution following this track.
function drawBlurryCircle(center, radius, blurAmount, color) {
const circle = new Path.Circle({
center,
radius,
shadowColor: color,
shadowBlur: blurAmount,
// set a fill color to make sure that the shadow is displayed
fillColor: 'white',
// use blendmode to hide the fill and only see the shadow
blendMode: 'multiply'
});
const blurPlaceholder = circle
.clone()
.set({ shadowColor: null, fillColor: null })
.scale((circle.bounds.width + (blurAmount * 2)) / circle.bounds.width);
return new Group({
children: [circle, blurPlaceholder],
blendMode: 'source-over'
});
}
drawBlurryCircle(view.center, 50, 20, 'red');
drawBlurryCircle(view.center + 30, 40, 30, 'lime');
drawBlurryCircle(view.center + 60, 30, 30, 'blue');
Related
How to combine images and text with fabricjs and edit text when double clicking the combined object,
Still combine after editing text,
Please tell me how to edit text when rendering image and text combinations
const canvas = new fabric.Canvas("canvas");
// a rect
const rect = new fabric.Rect({
stroke: "#000",
strokeWidth: 1,
fill: "#ccc",
left: 170,
top: 80,
width: 50,
height: 50
});
// a text for describe rect
const text = new fabric.IText("describe rect", {
fontFamily: "Comic Sans",
fontSize: 14,
stroke: "#000",
strokeWidth: 1,
fill: "#000",
left: 170,
top: 80
});
// 1 Combine the above two things but can not edit text
// 2 use LabeledRect also
Based on your requirements, you can
Create a subclass of Image class (or whatever object that you need to attach a label to)
Make it create an instance of IText or Textbox and save an internal reference to it
By hooking into the event handlers on the image (e.g. double click), manually manage the events of the text. Make sure to keep the position and dimensions of the text updated when the image is modified.
Take a look at this answer: Resize Fabric Rect without resizing Textbox. It does something very similar to what you've described.
I am trying to add the clippath area(rect object) over canvas using fabricJS. Currently i set Overlay Image to the canvas and in addition to that am adding clippath area to place objects. I successfully able to place the object inside the clippath, but the problem is whatever image i place it inside clippath has opacity(means object also has transparency), i know is because i set transparent image as overlay which is always top over other object.
My query is
Can i set the clippath area alone to ignore overlay? Or atleast to set the preference like clippath is first priority and next overlay?
Currently clippath has no border and i couldn't know where the clippath is? So is there a way to set the border over clippath area?
Here is my below code
HTML
<canvas id="ex8" width="300" height="400"></canvas>
JS
(function () {
var canvas = new fabric.Canvas('ex8');
canvas.controlsAboveOverlay = true;
var clipPath = new fabric.Rect({ width: 100, height: 100, fill: 'red', top: 100, left: 100 });
canvas.setOverlayImage(
'https://i.ibb.co/88QkSMC/crew-front.png',
canvas.renderAll.bind(canvas),
{}
);
var group = new fabric.Group([
new fabric.Rect({ width: 100, height: 100, fill: 'red', globalCompositeOperation: 'xor' }),
new fabric.Rect({ width: 100, height: 100, fill: 'blue', top: 100 }),
new fabric.Rect({ width: 100, height: 100, fill: 'green', left: 100, top: 100 }),
]);
canvas.clipPath = clipPath;
canvas.add(group);
})();
Appreciate your help !!!
FIDDLE
Very interesting question.
The issue you encounter is linked to the order in which layers are being applied in Fabric.js.
As described in a related github post, the layers are applied as follows (from bottom up):
Hence, it's not that the clipPath is put on the T-shirt image (i.e. the Overlay image). It's the opposite, as the T-shirt layer is applied over the clipPath.
While it seems that the "transparency" comes from the clipPath resp. fabric group, it is actually coming from the Overlay image itself as the T-shirt in the image is transparent (the surrounding white background is not).
The opacity of the T-shirt image is also visible when hovering over it with a mouse (e.g. in file explorer as depicted below).
As for the borders, you can add the following to your code:
group.set({
borderColor: 'yellow',
cornerColor: 'yellow',
cornerSize: 6,
transparentCorners: false
});
group.setActiveObject(canvas);
Please note that the borders are only visible when the corresponding canvas item is activated. For more information, you can check this link on customization.
This question already has answers here:
Move images rendered on highcharts on resize
(2 answers)
Closed 8 years ago.
I've created this highchart but I need the renderer.text to scale with the parent highchart. For example. In the second chart i changed the max and min width so that the chart scales within the min and max values but the render.text stays static. I want to make the 2nd fiddle look like the 1st fiddle whenever the user changes width. Thank you for your help.
http://jsfiddle.net/jimbob25/V446C/
http://jsfiddle.net/jimbob25/V446C/1/
renderer.path(['M', 485, 40, 'L', 505, 40, 505,300, 485, 300])
.attr({
'stroke-width': 1,
stroke: 'red',
zIndex: 3
})
.add();
renderer.text('+/-2%', 435,93)
.attr({
zIndex: 3
})
.css({
color: '#000000',
fontSize: '12px'
})
.add();
In the second fiddle you had set the height:400px; width:530px; so you had fix the values and chart wont scale therefore you should set min-width and min-height you also can set max-width and max-height.
Here is the fiddle
Osyan thank you for the help but what i meant was the renderer text doesnt scale with the image.
I ended up drawing my renderer to work for 530px width, then anytime the hgihchart resizes i get the offset and redraw the images with that offset
redraw: function() {
var offset = 530 - chart.chartWidth;
initDrawing(chart,offset);
}
and then
function initDrawing(chart,offset){
$("#alert1").remove();
$("#greenArrow1").remove();
//Clear all of the old elements by id because we will redraw new ones.
// subtract the offset and add to chart
renderer.text('+/-2%', 435-offset, 40)
.attr({
id: 'alert1'
})
.css({
color: '#000000',
fontSize: '12px'
})
.add();
I am working on a webapp with openstreetmap and OpenLayers. What I have done is using JavaScript rendering a line on map with some given points, like the screen capture below:
Now what I would like to do is to have a tag displayed above the line, something like below:
I am not sure how I can achieve this through JavaScript, and I couldn't find any useful reference document. Anyone have done this before please share some experience. Thank you!
Try creating opaque (or zero-width) points with styled labels. Here's my proposition based on Labeled Features Example. (I don't have possibility to test it right now, so watch out).
First: Create new vector layer (as styling settings might interfere with your current layer with line) styled with invisible points and some parameters:
var vectorLayer = new OpenLayers.Layer.Vector("Labeled points", {
styleMap: new OpenLayers.StyleMap({'default':{
// should be invisible, if not, set opaque
strokeWidth: 0,
pointRadius: 0,
// label may have \n linebreaks
label : "${label}",
fontColor: "${fontColor}",
fontSize: "12px",
fontFamily: "Courier New, monospace",
fontWeight: "bold",
labelAlign: "${align}",
labelXOffset: "${xOffset}",
labelYOffset: "${yOffset}",
labelOutlineColor: "white",
labelOutlineWidth: 3
}})
});
Second: Here's how you create feature with given parameters (the ${parameter}'s):
var newLabeledPoint = new OpenLayers.Geometry.Point(-101.04, 35.68);
var newLabeledFeature = new OpenLayers.Feature.Vector(newLabeledPoint );
newLabeledFeature .attributes = {
label: "Line 1",
fontColor: 'blue',
align: "cm",
// positive value moves the label to the right
xOffset: 50,
// negative value moves the label down
yOffset: -15
};
and finally: vectorLayer.addFeatures([newLabeledFeature]);.
I am attempting to change the fill style of a KineticJS Rect object after it has been instantiated, after being added to the layer and scene, and in the animation loop.
I am trying to toggle the fill type between a single color type to a linear gradient type based on a user button control in my main app file that renders the canvas.
I instantiate the object in a different file which is a class I wrote that instantiates a KineticJS Rect with a linear gradient fill initially in it's constructor like so:
function MyBackground(width,height,c1,c2,mode) {
this.width = width;
this.height = height;
this.color1 = c1;
this.color2 = c2;
this.mode = mode;
this.background = new Kinetic.Rect({
x: 0,
y: 0,
width: this.width,
height: this.height,
fillLinearGradientStartPoint: [0, 0],
fillLinearGradientEndPoint: [this.width, this.height],
fillLinearGradientColorStops: [0, this.color1, 1, this.color2]
});
this.getMode = function() { return this.mode; }; }
I then create an instance of this in the main file:
var myBack= new MyBackground(stageWidth,stageHeight,getRandomHexColor(),getRandomHexColor(),'dualColorLinearGradientStatic');
var background = myBack.getBackground();
myBack.setMode('singleColor');
I then add the background Rect to the layer, and the layer to the stage:
layer.add(background);
stage.add(layer);
I then start the animation loop code and after that the event handler that catches the button press to change the fill trying this inside:
myBack.setMode(bgmodeselected);
background = myBack.getBackground();
The app page loads fine showing the initial linear gradient fill. If I select that same mode of linear gradient and press the button control, it changes the colors as I desire maintaining the fill type of linear gradient. If I then switch the mode to single color fill and click the control button, that works too, changing the rect to a single color.
Here is what I have inside my class function to change the mode that makes that specifically work:
this.background.setFill(this.color1);
Inside that same function based on a conditional it should change the fill to linear gradient (and does this as long as its not changed to single color fill as above first)
this.background.setAttrs({
fillLinearGradientStartPoint: [0, 0],
fillLinearGradientEndPoint: [960, 600],
fillLinearGradientColorStops: [0, this.color1, 1, this.color2]
});
I've also tried within that same block:
this.background.setFillLinearGradientStartPoint(0, 0);
this.background.setFillLinearGradientEndPoint(960, 600);
this.background.setFillLinearGradientColorStops([0, this.color1, 1, this.color2]);
I know that the proper variables and values are being passed to the function and that the conditionals are working because it will change the fill mode type from its (as initially instantiated) linear gradient fill to a single color fill (which will continue working even as a different single color).
The problem is when I try to switch BACK to linear gradient fill it will not do so, or repaint/refresh at least despite it's calling this same function with the proper values. So, I suppose that my specific question is how can I change the fill style of a KineticJS Rect multiple times, from a single color fill back to a linear gradient fill after it has been already been added to the layer and stage and has an animation loop implemented?
This is my first question post so I hope that I am doing so properly; please inform me if I should be doing anything differently. Thanks.
Welcome to stackoverflow!
When you’re re-applying your gradient, be sure to clear out the solid color fill:
// clear the solid fill
this.setFill('');
// then apply the gradient fill
this.setFillLinearGradientStartPoint(-50);
this.setFillLinearGradientEndPoint(50);
this.setFillLinearGradientColorStops([0, 'green', 1, 'yellow']);
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/dmMF2/
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="http://www.html5canvastutorials.com/libraries/kinetic-v4.3.0-beta2.js"></script>
<script>
function draw(images) {
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var linearGradPentagon = new Kinetic.Rect({
x: 100,
y: 30,
width: 75,
height: 50,
fill:"red",
stroke: 'black',
strokeWidth: 4,
draggable: true
});
linearGradPentagon.on('mouseover touchstart', function() {
this.setFill('');
this.setFillLinearGradientStartPoint(-50);
this.setFillLinearGradientEndPoint(50);
this.setFillLinearGradientColorStops([0, 'green', 1, 'yellow']);
layer.draw();
});
linearGradPentagon.on('mouseout touchend', function() {
this.setFill('red');
layer.draw();
});
layer.add(linearGradPentagon);
stage.add(layer);
}
draw();
</script>
</body>
</html>