How can I blur an image on Canvas using fabricjs with React - javascript

I'm new to fabricjs, I've been trying to blur an image using a HTML slider(range from 0 - 1). The image has already been loaded on the canvas. But while applying the blur effect using Filters arrays, I don't see any visible change.
I'm using a controlled blur component in React as follows :
<div>
Blur
<input
id="blur"
type="range"
min="0"
max="1"
step="0.1"
value={editor?.canvas?.getActiveObject()?.filters?.slice(-1)[0]?.blur || 0}
onChange={(e) => handleChangeSlider("blur", e?.target?.value)}
></input>
<span>{editor?.canvas?.getActiveObject()?.filters.slice(-1)[0]?.blur || 0}</span>
</div>
the onChange handler function is as follows :
function handleChangeSlider(){
setBlur(value)
editor?.canvas?.getActiveObject()?.filters?.push({
blur: 0.5,
horizontal: false,
aspectRatio: 1,
})
// editor?.canvas?.getActiveObject()?.applyFilters()
}
editor.canvas.renderAll()
}
The same approach works if I'm setting opacity on the image, like:
editor.canvas.getActiveObject().set({ opacity: value })
But, as far as I know, the Blur has to be inside the filters array, So I've tried doing the same, but it is not working at all.
Some Observations :
Even after doing the editor.canvas.renderAll() , I think editor?.canvas?.getActiveObject()?.applyFilters() is necessary to apply the blur property. But if I uncomment that line from above code, i get the following error "filter.isNeutralState is not a function" . While that line being kept commented, I don't see any error or any change.
I found this codepen link, where the author is literally doing the same, But I'm not sure what I'm doing wrong in my case.
My editor?.canvas?.getActiveObject()

Сreate a filter like this:
new fabric.Image.filters.Blur({
blur: 0.5,
horizontal: false,
aspectRatio: 1,
});
In order to solve the cross-origin issue use the additional parameter:
fabric.Image.fromURL(
activeDragDropItem.src,
function (oImg) {
oImg.scale(0.2);
oImg.top = e.nativeEvent.layerY;
oImg.left = e.nativeEvent.layerX;
editor.canvas.add(oImg);
},
{
crossOrigin: "",
}
);

Related

ion.rangeSlider: Using one handle position to update the other handle's position

I'm using the JavaScript ion.rangeSlider library (https://github.com/IonDen/ion.rangeSlider).
It supports using two handles.
I want to be able to update the "to" handle when I change the "from" handle.
$(function() {
console.log('jQuery here!');
let zfrom = -5;
const $zslider = $("#zslider");
$zslider.ionRangeSlider({
skin: 'big',
type: 'double',
min: -5.000,
max: 5.000,
from: -5.000,
to: 5.000,
step: 0.1,
//on slider handles change
onChange: function (data) {
zslider.update({
to: -data.from,
})
},
onUpdate: function(data) {
},
onFinish: function (data) {
}
})
const zslider = $zslider.data("ionRangeSlider");
})
HTML:
<input id=zslider type=text />
The problem is that when I move the "from" handle it moves the "to" handle a little bit and then the "from" handle loses focus and I have to grab it again.
I suspect that the zslider.update is causing the onChange/onUpdate to fire and so goes into an infinite loop.
I've tried using various flags and delays, but I can't get the logic to work.
I have a work around which is to move the "to" slider after I finish moving the "from" slider (onFinish), but it isn't pretty.

Fabric js - how to not run loadSVGFromURL when the passing image url is empty?

var lbr = (lengthbrsvg) ? lengthbrsvg : def_svg;
let's say def_svg is a valid image URL.
lengthbrsvg is dynamically set which is either image URL or empty.
Now lbr is valid image URL even if the lengthbrsvg is empty.
loadSVGFromURL function only accepts valid svg url. otherwise throws error. So the default image is passed when the lengthbrsvg is empty. We use loadSVGFromURL function inside another same function repeatedly to add svg to a canvas one by one. Due to the use of def_svg the number of request increases which is unnecessary. So, is there a way to not run that function if the image URL is empty which in turn dont request for def_svg image. in other words is there another way than using ternary operator.
fabric.loadSVGFromURL(lbr, function(obj_lengthbr) {
var lpatpath;
lpatpath = new fabric.PathGroup(obj_lengthbr, { left: 0, top: 0, width: 620, height: 641 });
lpatpath.setPatternFill({ source: lengthbrpattern, repeat: 'repeat', offsetX: 'left', offsetY: 'top' });
canva.add(lpatpath);
fabric.loadSVGFromURL((lengthbrbottom) ? lengthbrbottom : def_svg , function(obj_lengthbrbot) {
var BorPath;
//========
//========
fabric.loadSVGFromURL( , function(obj_lengthbrbot) {
//=====
//=====
});
});
});
From the above snippet, I don't want to run the loadSVGFromURL if the passing image URL is empty. how do I achieve that?

How to lock slider and prevent updating of values with mouse into dat.GUI menu

I try to implement a way to prevent the updating of values with mouse (actually when the three.js animation has started, launched with a click on button).
For the moment, I have the following dat.GUI menu:
Once "start" button is clicked, I would like to prevent user from modifying with mouse the parameters "Rotation x" and "Rotation y".
Here is the concerned part of code for this menu:
// Create GUI
var gui = new dat.GUI({
autoplace: false,
width: 350,
height: 9 * 32 - 1
});
var params = {
GreatCircle : '',
Rotationx : torusRotationInitX,
Rotationy : torusRotationInitY,
StartingVector : '',
ComponentVectorTheta : 15.0,
ComponentVectorPhi : 15.0,
CovariantDerivativeVector : '',
ComponentCovariantDerivativeTheta : 15.0,
ComponentCovariantDerivativePhi : 15.0
};
// Set parameters for GUI
gui.add(params, 'GreatCircle').name('Great Circle ');
controllerRotationx = gui.add(params, 'Rotationx', 0, 2*Math.PI, 0.001).name('Rotation x ');
controllerRotationy = gui.add(params, 'Rotationy', 0, 2*Math.PI, 0.001).name('Rotation y ');
...
When I click on reset button, I call the following function:
// Reset Button
resetButton.onclick = function ResetParameters() {
...
// Reinitialize parameters into gui
params.Rotationx = torusRotationInitX;
params.Rotationy = torusRotationInitY;
for (var i in gui.__controllers) {
gui.__controllers[i].updateDisplay();
}
render();
}
I don't know if there is an option for controller to lock these sliders which usually change their values. Is it possible?
Update 1
Maybe I could wrapper the dat.GUI menu into a div and make this div not clickable, is it a solution?
Update 2
I tried to apply the method used on Method for disabling a button in dat.gui?
Following this solution, I have added the extension into dat.gui, just after:
dat.controllers.FunctionController = (function (Controller, dom, common) {
...
});
The following added code snippet is:
function blockEvent(event)
{
event.stopPropagation();
}
Object.defineProperty(dat.controllers.FunctionController.prototype, "disabled", {
get: function()
{
return this.domElement.hasAttribute("disabled");
},
set: function(value)
{
if (value)
{
this.domElement.setAttribute("disabled", "disabled");
this.domElement.addEventListener("click", blockEvent, true);
}
else
{
this.domElement.removeAttribute("disabled");
this.domElement.removeEventListener("click", blockEvent, true);
}
},
enumerable: true
});
Is extension code well located into dat.GUI source?
Then, I set the property "disabled" into my code to prevent user from sliding "controllerRotationx" with mouse (once start button is pressed):
if (animation)
controllerRotationx.__li.disabled = true;
Unfortunately, my method doesn't work : when animation is started, I can still move the slider contained into "controllerRotationx".
I saw that above link (Method for disabling a button in dat.gui?), this was about a button and not for a slider, does it change anything for my case?
I didn't find an explicit controller for the slider.
I would do this. The slider is not a form element, there's nothing to disable in the traditional w3c sense. Luckily we can use pointer-events and disable it properly as if it were a form element using just public dat.gui properties.
var speeder = menu.add(text, 'speed', -5, 5);
speeder.domElement.style.pointerEvents = "none"
speeder.domElement.style.opacity = .5;
The solution given by #Radio works pretty well. But, with sliders, the slider is a sibling of the text box's DOM element. We need to disable pointer events on the div which contains all the controls (and which is not exposed directly by dat.gui). So,
var speeder = menu.add(text, 'speed', -5, 5);
// disables the text box
speeder.domElement.style.pointerEvents = "none"
// disables all controller elements related to "speeder"
speeder.domElement.parentElement.style.pointerEvents = 'none'
When the Start button is pressed, set:
controllerRotationx.__li.setAttribute( "style", "display: none" );
thanks for tips
on my side i hack the Common controller
so able to chainning.
gui.add(this, '_screenW').disable(true);
Common.extend(controller, {
disable: function disable(v) {
this.domElement.style.pointerEvents = v?"none":"auto";
this.domElement.style.opacity = v?.5:1;
return controller;
},

Set Container Element of a jsPlumb Line

I have two small green divs within my canvas. It can be drag within the canvas with an id myid_templates_editor_canvas, with the use of the code below:
myid_templates_editor_make_draggable('div1');
myid_templates_editor_make_draggable('div2');
// Make an element draggable within the canvas
function myid_templates_editor_make_draggable(id){
jQuery('#' + id).draggable({
cursor: 'move',
cursorAt: { top: 56, left: 56 },
containment: '#myid_templates_editor_canvas',
});
}
See images below:
I have made a line between 2 draggable divs using jsPlumb.
var jsPlumb_instance = jsPlumb.getInstance();
var endpointOptions = {
anchor:'BottomCenter',
maxConnections:1,
endpoint:['Rectangle',{width:'0px', height:'0px' }],
paintStyle:{fillStyle:'black'},
connectorStyle : { lineWidth: '1px' , strokeStyle: 'black' },
connector : ['Straight'],
setDragAllowedWhenFull:true,
};
div1Endpoint = jsPlumb_instance.addEndpoint('div1', endpointOptions);
div2Endpoint = jsPlumb_instance.addEndpoint('div2', endpointOptions);
jsPlumb_instance.connect({
source:div1Endpoint,
target:div2Endpoint,
});
jsPlumb_instance.draggable('div1');
jsPlumb_instance.draggable('div2');
I dont want the line outside the canvas border. See 3rd picture.
I want the line to be contained within the canvas with an id myid_templates_editor_canvas.See the image below:
I tried changing part of the code above with the the code below, with no luck.
jsPlumb_instance[id].draggable(id1, {containment:'#myid_templates_editor_canvas'});
jsPlumb_instance[id].draggable(id2 , {containment:'#myid_templates_editor_canvas'});
Yes, the two points was somehow constrained because the length of the maximum line was limited but still goes out of the border of the canvas.Below is the html set-up of the canvas and two points.
<table>
<tr>
<td>
<canvas id="myid_templates_editor_canvas"></canvas>
<div id="div1"></div>
<div id="div2"></div>
</td>
</tr>
</table>
I worked with jsPlumb for quite long already and I remembered it need to refer to a lot of library.
Because jsPlumb using draggable feature from jQuery UI, you can read this article for understanding how it is being worked.
https://jqueryui.com/draggable/#constrain-movement
In your case, myid_templates_editor_canvas will not be consider as the containment, it is for drawing only. So I suggest you to modify your html as below try, let me know your result as well.
I put an ID for table element and it will be the containment for 2 end points. The containment must be an element that contains child element - in the other words - parent element.
myid_templates_editor_make_draggable('div1');
myid_templates_editor_make_draggable('div2');
// Make an element draggable within the canvas
function myid_templates_editor_make_draggable(id){
jQuery('#' + id).draggable({
cursor: 'move',
cursorAt: { top: 56, left: 56 },
containment: '#main-container',
});
}
<table id="main-container">
<tr>
<td>
<canvas id="myid_templates_editor_canvas"></canvas>
<div id="div1"></div>
<div id="div2"></div>
</td>
</tr>
</table>
Actually you can set containment via jsPlumb. See my jsFiddle. The reason why your solution did not work is that you have set draggable via jsPlumb, not jQuery. They do not know about it other, so can not work together. You need to provide options to draggable function:
jsPlumb_instance.draggable(element, { containment:true });
To know more about draggable in jsPlumb see help. You can set containment container explicitly when you get instance of jsPlumb:
var jsPlumb_instance = jsPlumb.getInstance({ Container:"inner" });
You can also specify DragOptions and DropOptions if you need (more info).
It is better to set draggable via jsPlumb, as a plus, then no need to call repaint after dragging is finished. A huge performance benefit with a lot of elements.
A common feature of interfaces using jsPlumb is that the elements are
draggable. You should use the draggable method on a jsPlumbInstance to
configure this:
myInstanceOfJsPlumb.draggable("elementId");
...because if you don't, jsPlumb won't know that an element has been
dragged, and it won't repaint the element.
I had made a solution to my problem. I changed my code above to to code below:
myid_templates_editor_make_draggable('div1');
myid_templates_editor_make_draggable('div2');
//Make an element draggable within the canvas
function myid_templates_editor_make_draggable(id){
jQuery('#' + id).draggable({
cursor: 'move',
cursorAt: { top: 56, left: 56 },
containment: '#myid_templates_editor_canvas',
drag: function(e, ui){
jsPlumb_instance.repaintEverything();
},
});
}
I had also omit the line of code that make JsPlumb two endpoints draggable.
var jsPlumb_instance = jsPlumb.getInstance();
var endpointOptions = {
anchor:'BottomCenter',
maxConnections:1,
endpoint:['Rectangle',{width:'0px', height:'0px' }],
paintStyle:{fillStyle:'black'},
connectorStyle : { lineWidth: '1px' , strokeStyle: 'black' },
connector : ['Straight'],
setDragAllowedWhenFull:true,
};
div1Endpoint = jsPlumb_instance.addEndpoint('div1', endpointOptions);
div2Endpoint = jsPlumb_instance.addEndpoint('div2', endpointOptions);
jsPlumb_instance.connect({
source:div1Endpoint,
target:div2Endpoint,
});
Hope it helps everyone that is going through the same problem with me.

Add tip text dynamically to a slider

In my project, I am trying to add the tip text (config) dynamically to a slider. How to do that?
I need to add it dynamically because I am generating the array of variables in a "Controller", which holds the text for each value of the slider (for tip text).
var slider = Ext.getCmp('slider')
slider.setTipText(arrayOfVariables) //What should I do here instead?
There is no such method like setTipText in docs. What should I use then?
EDIT:
{
xtype:'slider',
animate: false,
//plugins: [Ext.create('App.view.SliderOverride')],
cls: 'sliderStyle',
width: "80%",
id: 'slider',
value: 36/2, //must be current month
//increment: 10,
minValue: 1,
maxValue: 36,
useTips: true,
tipText: function(thumb){
alert("hello");
App.getController('TaskController')._arrMonthView[thumb.value].month;
},
},
tipText requires a function config so you can add a function that will use your variables from controller;
Ext.create('Ext.slider.Multi', {
....
tipText: function(){
return App.getController('your controller').yourVariable
},
.....
});
This is added on the creation of the slider so you don't need to modify it , just your variables in controller. So you don't need to re set the tip text function.
I solved this issue by using getText method of Ext.slider.Tip.
Used to create the text that appears in the Tip's body. By default this just returns the value of the Slider Thumb that the Tip is attached to. Override to customize.
For example in which situation it can be used, you have a look at this link

Categories

Resources