How to make applying filters to multiple objects less laggy in fabric js? - fabricjs

i have about 10 objects that filters can be applied to, and a filter engine that goes through them one by one and applies filters to each.
import { fabric } from 'fabric'
const filters = {
brightness: new fabric.Image.filters.Brightness(),
premade: new fabric.Image.filters.ColorMatrix(),
}
const addFilter = (canvas, index, value = null) => {
let pickedFilter
let objects = canvas.getObjects()
switch (index) {
case 1:
pickedFilter = sepia
break;
case 8:
pickedFilter = brightness
break;
default:
break;
}
objects.forEach((o) => {
if (o.filters) {
if (!o.filters[0]) {
o.filters.push(filters.brightness)
o.filters.push(filters.premade)
}
pickedFilter(o, value)
}
});
canvas.renderAll();
}
export default addFilter
function sepia(o){
let value = [
0.67, 0, 0, 0, 0,
0, 0.54, 0, 0, 0,
0, 0, 0.4, 0, 0,
0, 0, 0, 1, 0
]
delete filters.contrast.contrast
delete filters.premade.matrix
filters.premade.matrix = value
o.applyFilters()
}
function brightness(o, value) {
value = (value / 100) / 2
filters.brightness.brightness = value
o.applyFilters()
}
initally when objects don't have filter at filters[0], i go ahead and push the filters.
later on when the user moves the slider like in this gif, i go through each object and if it can have filters i go ahead and change the value of filter and then apply the filters. as you can see it's a bit laggy and i think the biggest problem is that there are multiple objects that filters are applied to.
in this gif, i get rid of all objects but one and i can feel that the performance is better, but the problem is that i need to apply the filters to all of the objects.
i tried running the code inside filter functions only once (for example i put the value asignment, deletion of previous value and value asignment inside sepia funtion, outside the loop), but it didn't change anything.
how can i improve the performance and make it less laggy?

Related

Is there a way to recognize that a declarativeContent rule was matched in a chrome extension?

I was hoping that the setIconFct is called only when the condition is met. However, x = 1 even if the tab is not on "google.com" (condition is met). How can I found out, if the condition was met programmatically? As you see, I am changing the Icon, is there a way to find out, if the icon was changed, can I read properties of the icon? This would be just one idea, to find out that the condition was met through the fact that the icon was changed, I am open to any other ideas - eventually I need to find out if the condition was met or not and then run my own code. (I was also playing with RequestContentScript but it seems not to do anything in V3).
var x = 0;
function setIconFct (color, callback) {
x = 1;
const canvas = new OffscreenCanvas(16, 16);
const context = canvas.getContext('2d');
context.clearRect(0, 0, 16, 16);
context.fillStyle = color; // Green
context.fillRect(0, 0, 16, 16);
context.font = '19px arial';
context.fillText('X', 0,12);
const imageData = context.getImageData(0, 0, 16, 16);
var action = new chrome.declarativeContent.SetIcon({imageData: imageData});
callback(action);
}
chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
setIconFct(function(setIcon) {
chrome.declarativeContent.onPageChanged.addRules([
{
conditions : [
new chrome.declarativeContent.PageStateMatcher({
hostSuffix: 'google.com',
})
],
actions : [ setIcon ]
}
]);
});
});
I tried to execute my own code in case the conditions of pageStateMatcher was met. I was not able to find out if the condition was met.

How to calculate sum of an object each 2 its value in js

i have an object like this, how can i calculate total value of code and codePrev :
{
"code01Prev": 3756743628,
"code01": 10346000,
"code02Prev": 0,
"code02": 0,
"code10Prev": 3756743628,
"code10": 10346000,
"code11Prev": 0,
"code11": 0,
"code20Prev": 3756743628,
"code20": 10346000
}
Going with plain vanilla. The code is a little bit imperative (not explaining what but how). If possible it would be good to consider changing data structure.
data = {
code01Prev: 3756743628,
code01: 10346000,
code02Prev: 0,
code02: 0,
code10Prev: 3756743628,
code10: 10346000,
code11Prev: 0,
code11: 0,
code20Prev: 3756743628,
code20: 10346000
}
initialTotals = {
code: 0,
codePrev: 0}
dataKeyValues = Object.entries(data)
totals = dataKeyValues.reduce(
(totalAccumulated, [key, value]) => {
if (key.endsWith('Prev')){
return {
...totalAccumulated,
codePrev: totalAccumulated.codePrev + value}}
return {
...totalAccumulated,
code: totalAccumulated.code + value}},
initialTotals)
console.log(totals)
// totals:
// {
// "code": 31038000,
// "codePrev": 11270230884
// }

nouislider: adding pips non-numerical last value?

Is there a way to show a non-numerical last pips value. for instance, showing pips scale for 0, 100, 200, 300, More
Thanx for this great script!
The following solution is a bit of a hack but it will do what you want.
You can use the format option and send it to a function which translates the values via a switch statement.
function updatePips( value, type ){
switch(true) {
case (value > 300):
value = "More"
break;
}
return value;
}
$("#pips-steps").noUiSlider_pips({
mode: 'steps',
density: 100,
format: {to: updatePips}
});

How to get web element (id) from the element position in D3.js force graph

I am working with the D3.js force graph but I am not able to find out the element id from the element position (which I know).
I am using Leap motion. I need to simulate a mouse event (a click, a move, a drag, etc.) without a mouse. And, if I am right, in order to be able to do this, I need to find out what is the the element id from the coordinates x and y (these coordinates I know from the Leap motion pointer). So from what you wrote above, I need to find out the ('.node’).
Here is what I already tried but it did not work:
Is it possible to use non-mouse, non-touch events to interact with a D3.js graph? If so, what is the most efficient way to go about it?
So I used this function (see below), but I need to know the element id to make it work correctly:
//graph.simulate(document.getElementById("r_1"), 'dblclick', {pointerX: posX, pointerY: posY});
//here id r_1 is hardcoded, but I need to find out id from x and y coordinates.
this.simulate = function (element, eventName) {
function extend(destination, source) {
for (var property in source)
destination[property] = source[property];
return destination;
}
var eventMatchers = {
'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
'MouseEvents': /^(?:click|dblclick|mouse(?:down|up|over|move|out))$/
};
var defaultOptions = {
pointerX: 0,
pointerY: 0,
button: 0,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
bubbles: true,
cancelable: true
};
var options = extend(defaultOptions, arguments[2] || {});
var oEvent, eventType = null;
for (var name in eventMatchers) {
if (eventMatchers[name].test(eventName)) {
eventType = name;
break;
}
}
if (!eventType)
throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported');
if (document.createEvent) {
oEvent = document.createEvent(eventType);
if (eventType == 'HTMLEvents') {
oEvent.initEvent(eventName, options.bubbles, options.cancelable);
}
else {
oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
}
element.dispatchEvent(oEvent);
}
else {
options.clientX = options.pointerX;
options.clientY = options.pointerY;
var evt = document.createEventObject();
oEvent = extend(evt, options);
element.fireEvent('on' + eventName, oEvent);
}
return element;
}
Many thanks for your help and ideas.
If you want access to the element, it's implicit in D3's iterators via this.
d3.selectAll('.node').each(function(d) {
console.log(this); // Logs the element attached to d.
});
If you really need access to the id, you can get it with selection.attr():
d3.selectAll('.node').each(function() {
console.log(d3.select(this).attr('id')); // Logs the id attribute.
});
You don't have to use each. Any of the iterators, such as attr or style, etc., have 'this' as the bound element:
d3.selectAll('.node').style('opacity', function(d) {
console.log(this);// Logs the element attached to d.
});
If you want the x and y coordinates of a node, it's part of the data:
d3.selectAll('.node').each(function(d) {
console.log(d.x, d.y); // Logs the x and y position of the datum.
});
If you really need the node attributes themselves, you can use the attr accessor.
d3.selectAll('.node').each(function(d) {
// Logs the cx and cy attributes of a node.
console.log(d3.select(this).attr('cx'), d3.select(this).attr('cy'));
});
EDIT: It looks like you need an element reference, but the only thing you know about the node in context is its position. One solution is to search through all nodes for a node with matching coordinates.
// Brute force search of all nodes.
function search(root, x, y) {
var found;
function recurse(node) {
if (node.x === x && node.y === y)
found = node;
!found && node.children && node.children.forEach(function(child) {
recurse(child);
});
}
recurse(root);
return found;
}
However this only gives you the node object, not the element itself. You will likely need to store the element references on the nodes:
// Give each node a reference to its dom element.
d3.selectAll('.node').each(function(d) {
d.element = this;
});
With that in place, you should be able to access the element and get its id.
var id, node = search(root, x, y);
if (node) {
id = node.element.getAttribute('id');
}
The brute-force search is fine for a small number of nodes, but if you're pushing a large number of nodes you might want to use D3's quadtree (example) to speed up the search.
Use d3.select('#yourElementId')
For more info check this out: https://github.com/mbostock/d3/wiki/Selections

Is it possible to animate filter in Fabric.js?

Is it possible to animate the images filter in Fabric.js? Such as a "pixelate" filter.
I solved it in the same way like the demo.
Unfortunately filters aren't able to be animated - they need too much processing time.
Here's my Code:
image = ... //Image, where the filter should be applied
var filter = new fabric.Image.filters.RemoveWhite({
threshold: 0,
distance: 140
});
image.filters.push(filter);
image.applyFilters(canvas.renderAll.bind(canvas));
animate(image,1, 400); //Start the Animation
function animate(image,value, stop){
value += ((stop-value)*0.02); //Change the threshold-value
if (image.filters[0]) {
image.filters[0]['threshold'] = value;
console.log(value);
image.applyFilters(canvas.renderAll.bind(canvas)); //Start creating the new image
if(value<stop-100){
setTimeout(function(){act(image,value,stop);},1);
}
}
}
I know the code isn't the most efficient one, but it works. And you can see that Animating filters consumes too much time.
(I tested it with a 1920x1080px image, maybe you can use it with smaller images)
Here is a updated version for the brightness filter
var brightnessValue = 0.9;
var brightnessFilter = new fabric.Image.filters.Brightness({
brightness: brightnessValue
});
fabricImage.filters.push(brightnessFilter);
fabric.util.requestAnimFrame(function brightnessFilterAnimation() {
brightnessValue = brightnessValue - 0.04;
brightnessFilter.brightness = brightnessValue;
fabricImage.applyFilters();
if (brightnessValue > 0) {
fabric.util.requestAnimFrame(brightnessFilterAnimation);
}
});

Resources