Particles.js : limit number of particles - particles.js

I can't find if it is possible to limit the total number of particles.
Is there any way of doing this?
Particles.js Github

You can modify particles.js (row 750) adding an additional check in the push function:
/* ---------- pJS functions - modes events ------------ */
pJS.fn.modes.pushParticles = function(nb, pos){
pJS.tmp.pushing = true;
if(pJS.particles.array.length<140){
for(var i = 0; i < nb; i++){
pJS.particles.array.push(
new pJS.fn.particle(
pJS.particles.color,
pJS.particles.opacity.value,
{
'x': pos ? pos.pos_x : Math.random() * pJS.canvas.w,
'y': pos ? pos.pos_y : Math.random() * pJS.canvas.h
}
)
)
if(i == nb-1){
if(!pJS.particles.move.enable){
pJS.fn.particlesDraw();
}
pJS.tmp.pushing = false;
}
}
}
};
I used 140 as maximum number since it is a good value in order to not lose performance. Obviously you can modify it as needed.

I added a little on #Nicola Zaltron's solution making it responsive to the screen size by making the limit of particles based on the screen width and by doing that on smaller screens it wont look bad and it wont effect the performance.
/* ---------- pJS functions - modes events ------------ */
pJS.fn.modes.pushParticles = function(nb, pos){
pJS.tmp.pushing = true;
var limitNumOfParticles = Math.floor(pJS.canvas.el.width / 20);
if(pJS.particles.array.length < limitNumOfParticles){
// console.log("limit: ", limitNumOfParticles);
// console.log("array: ", pJS.particles.array.length);
for(var i = 0; i < nb; i++){
pJS.particles.array.push(
new pJS.fn.particle(
pJS.particles.color,
pJS.particles.opacity.value,
{
'x': pos ? pos.pos_x : Math.random() * pJS.canvas.w,
'y': pos ? pos.pos_y : Math.random() * pJS.canvas.h
}
)
)
if(i == nb-1){
if(!pJS.particles.move.enable){
pJS.fn.particlesDraw();
}
pJS.tmp.pushing = false;
}
}
}
};

A solution can be(with javascript):
// Get total particles
let particles = window['pJSDom'][0].pJS.particles.array.length
// Here set limit value
if(particles == 25){
// Get particles array
let array = window['pJSDom'][0].pJS.particles.array
// Remove (starting from zero) 16 particles starting from 5
array.splice(5,15)
// Set new particles array
window['pJSDom'][0].pJS.particles.array = array
}

I think that there is no way to limit the number of particles onscreen, so I changed the properties of the onHover and onClick so it won't add other particles. Solved my problem...
EDIT
This was previously marked as answer because at the time it solved my problem, but is not a proper answer.
No longer marked as answer, since apparently #Nicola Zaltron answer works !

i've found a good solution!
This is the simple code:
const maxParticles = 60;
particlesJS.load('particles', './assets/particlesjs.json', () => {
const pJSIndex = 0;
const pJS = pJSDom[pJSIndex].pJS;
pJS.particles.array.splice(0, pJS.particles.array.length - maxParticles);
pJS.interactivity.el.addEventListener('click', () => {
pJS.particles.array.splice(0, pJS.particles.array.length - maxParticles);
});
});

When initialising particlesJS, change the value of number.value to limit number of particles
particlesJS("particles-js", {
"particles": {
"number": {
"value": <change this to limit number of particles>,
"density": {
"enable": true,
"value_area": 800
}
},
......
}
See it in action : http://codepen.io/anon/pen/ggoRXy

Related

Qt3D - Import gltf and play animation

I'm currently struggling to play the animation of a gltf-file with Qt3D/QML.
Model I want to use:
https://sketchfab.com/3d-models/plane-cedc8a07370747f7b0d14400cdf2faf9
Code I have so far:
Entity {
id: root
Transform {
id: planeTransform
scale: 0.1
}
components: [
planeClip,
planeTransform,
planeScene
]
SceneLoader{
id:planeScene
source: "qrc:/Modells/3DModelle/plane/scene.gltf"
}
AnimationClipLoader{
id: planeClipLoader
source: "qrc:/Modells/3DModelle/plane/scene.gltf"
}
ClipAnimator{
id: planeClip
clip: planeClipLoader
channelMapper: ChannelMapper {
mappings: [ ] // Dont know where to get this
}
loops: Animation.Infinite
running: true
}
}
I use the SceneLoader to check if the gltf can be even loaded (it works, I can see the plane).
But I'm struggling to start the animation because I don't know where I get the ChannelMapper from.
When I open the file with 3DViewer on Windows, it plays the Animation correctly, so I suppose it should work without much insider knowledge of the file itself.
Anyone here familiar with playing gltf animations?
Thanks for the help!
To do this you need to traverse the tree of Entity(s) build by SceneLoader and find the appropriate component to initialize ClipAnimator with.
It's going to look something like this:
SceneLoader {
id: planeScene
source: "qrc:/Modells/3DModelle/plane/scene.gltf"
onStatusChanged: {
console.log("SceneLoader status=" + status);
if (status != SceneLoader.Ready)
return;
// retrieve a list of all the Entity(s) in the scene
var entityNames = planeScene.entityNames();
console.log("SceneLoader: entityNames=" + entityNames);
// traverse the Entity tree
for (var i = 0; i < entityNames.length; ++i) {
var entityName = entityNames[i];
var entityVar = planeScene.entity(entityName);
console.log("SceneLoader: entity=" + entityName + " components.length=" + entityVar.components.length);
// iterate on the components of this Entity
for (var j = 0; j < entityVar.components.length; ++j) {
var cmp = entityVar.components[j];
if (!cmp)
continue;
var cmp_class = cmp.toString();
console.log("SceneLoader: -> " + cmp_class);
// compare the type of this component to whatever type
// you are looking for. On this silly example, its QPhongMaterial:
if (cmp_class.indexOf("QPhongMaterial") >= 0) {
// initialize ClipAnimator with this data
}
}
}
}
}
I'm sharing the generic procedure here as I don't have a way to test this right now and figure out the data type you need to initialize ClipAnimator correctly.

How to close the path

I have a shape that is created using a 'for' loop. The first path has no anchors and therefore is pointy. I'm not sure if that is because it is not closed? Joined? Sketch
var circle= new Path.Circle({
radius: 100,
position: [200,200]
})
splat= new Path()
splat.fillColor= 'pink'
var count= 5
var length= circle.length
for(var i = 0; i <= count; i++){
var offset= i / count * length
const normal = i === 0 || i === count
? new Point(0, 0)
: circle.getNormalAt(offset) * (Math.random() * 50);
const point = circle.getPointAt(offset).add(i % 2 == 0 ? normal
: -normal);
console.log(point)
splat.add(point)
splat.smooth({ type: 'catmull-rom', factor: 0.5 });
}
splat.fullySelected= true
splat.closed= true
Thanks in advance again.
You just need to call the smooth() function once and you have to call it after setting the path to closed.
Then another thing that prevent the smoothing to work properly is that your first and last points of the path are the same, remove the last point and it will work as expected.
Sketch

Google Maps API complaining in IE11 about polyfill Array.from()

I'm trying to figure out an issue with Google Maps v3 and a polyfill we use for non-ES6 browsers (IE11 for example). The error we get is:
This site overrides Array.from() with an implementation that doesn't support iterables, which could cause Google Maps JavaScript API v3 to not work correctly.
The polyfill is: ( from https://vanillajstoolkit.com/polyfills/arrayfrom/ )
if (!Array.from) {
Array.from = (function () {
var toStr = Object.prototype.toString;
var isCallable = function (fn) {
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};
// The length property of the from method is 1.
return function from(arrayLike/*, mapFn, thisArg */) {
// 1. Let C be the this value.
var C = this;
// 2. Let items be ToObject(arrayLike).
var items = Object(arrayLike);
// 3. ReturnIfAbrupt(items).
if (arrayLike == null) {
throw new TypeError('Array.from requires an array-like object - not null or undefined');
}
// 4. If mapfn is undefined, then let mapping be false.
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
var T;
if (typeof mapFn !== 'undefined') {
// 5. else
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
if (!isCallable(mapFn)) {
throw new TypeError('Array.from: when provided, the second argument must be a function');
}
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 2) {
T = arguments[2];
}
}
// 10. Let lenValue be Get(items, "length").
// 11. Let len be ToLength(lenValue).
var len = toLength(items.length);
// 13. If IsConstructor(C) is true, then
// 13. a. Let A be the result of calling the [[Construct]] internal method
// of C with an argument list containing the single item len.
// 14. a. Else, Let A be ArrayCreate(len).
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
// 16. Let k be 0.
var k = 0;
// 17. Repeat, while k < len… (also steps a - h)
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
// 18. Let putStatus be Put(A, "length", len, true).
A.length = len;
// 20. Return A.
return A;
};
}());
}
This works fine on other pages - but for some reason Google Maps seems to have an issue with it!
Even more frustratingly, is that it then breaks one of my other plugins (a lazy load script), which works fine until the Google map stuff is loaded
Any ideas on what its moaning about, and how to fix it?
If you have IE11 or a VM, you can test it at: https://www.chambresdhotes.org/Detailed/1768.html (click on the map at the bottom of the page, and this will load the Google Map - but then you get this annoying error, and it breaks the lazyload scrolling after)
Thanks!

Aframe: cycle through colors using array

I've been trying to cycle through colors using a custom component.
<script>
AFRAME.registerComponent('floor-cycle', {
init: function () {
var sceneEl = document.querySelector('a-scene');
var floorEl = sceneEl.querySelector('#floor')
status = 1;
floorEl.addEventListener('click', function () {
if(status==1) {
floorEl.setAttribute('color', 'red'); status = 2
}
else if(status==2) {
floorEl.setAttribute('color', 'blue'); status = 3;
}
else if(status==3) {
floorEl.setAttribute('color', 'green'); status = 1
}
}
);
}
});
</script>
The component uses status to set the color attribute on click event, however this seems inefficient. Can this be implemented using an array rather than status?
demo - https://codepen.io/MannyMeadows/pen/GWzJRB
You can make an array ['red','green','blue'] and Cycle through it:
colors = ['red','green','blue'];
let i = 0;
floorEl.addEventListener('click',function(){
floorEl.setAttribute('material','color', colors[i]);
function add(){(i==colors.length-1) ? i = 0 : i++;}
add();
});
Seems better as the array is now dynamic, not sure how about the performance.
working fiddle here: https://jsfiddle.net/gftruj/g9wfLgab/2/

How can I test for clip-path support?

clip-path:shape() does not seem to work in IE (no surprise) and Firefox (a bit surprised). Is there a way to test for clip-path support? I use modernizr. (By the way, I know I can get this to work using SVGs and -webkit-clip-path:url(#mySVG))
You asked this a while ago, and to be honest, I'm not sure if Modernizr has yet to add support for this, but it's pretty easy to roll your own test in this case.
The steps are:
Create, but do not append, a DOM element.
Check that it supports any kind of clipPath by checking the JS style attribute of the newly created element (element.style.clipPath === '' if it can support it).
Check that it supports CSS clip path shapes by making element.style.clipPath equal some valid CSS clip path shape.
Of course, it's a little more complex than that, as you have to check for vendor-specific prefixes.
Here it is all together:
var areClipPathShapesSupported = function () {
var base = 'clipPath',
prefixes = [ 'webkit', 'moz', 'ms', 'o' ],
properties = [ base ],
testElement = document.createElement( 'testelement' ),
attribute = 'polygon(50% 0%, 0% 100%, 100% 100%)';
// Push the prefixed properties into the array of properties.
for ( var i = 0, l = prefixes.length; i < l; i++ ) {
var prefixedProperty = prefixes[i] + base.charAt( 0 ).toUpperCase() + base.slice( 1 ); // remember to capitalize!
properties.push( prefixedProperty );
}
// Interate over the properties and see if they pass two tests.
for ( var i = 0, l = properties.length; i < l; i++ ) {
var property = properties[i];
// First, they need to even support clip-path (IE <= 11 does not)...
if ( testElement.style[property] === '' ) {
// Second, we need to see what happens when we try to create a CSS shape...
testElement.style[property] = attribute;
if ( testElement.style[property] !== '' ) {
return true;
}
}
}
return false;
};
Here's a codepen proof-of-concept: http://codepen.io/anon/pen/YXyyMJ
You can test with Modernizr.
(function (Modernizr) {
// Here are all the values we will test. If you want to use just one or two, comment out the lines of test you don't need.
var tests = [{
name: 'svg',
value: 'url(#test)'
}, // False positive in IE, supports SVG clip-path, but not on HTML element
{
name: 'inset',
value: 'inset(10px 20px 30px 40px)'
}, {
name: 'circle',
value: 'circle(60px at center)'
}, {
name: 'ellipse',
value: 'ellipse(50% 50% at 50% 50%)'
}, {
name: 'polygon',
value: 'polygon(50% 0%, 0% 100%, 100% 100%)'
}
];
var t = 0, name, value, prop;
for (; t < tests.length; t++) {
name = tests[t].name;
value = tests[t].value;
Modernizr.addTest('cssclippath' + name, function () {
// Try using window.CSS.supports
if ('CSS' in window && 'supports' in window.CSS) {
for (var i = 0; i < Modernizr._prefixes.length; i++) {
prop = Modernizr._prefixes[i] + 'clip-path'
if (window.CSS.supports(prop, value)) {
return true;
}
}
return false;
}
// Otherwise, use Modernizr.testStyles and examine the property manually
return Modernizr.testStyles('#modernizr { ' + Modernizr._prefixes.join('clip-path:' + value + '; ') + ' }', function (elem, rule) {
var style = getComputedStyle(elem),
clip = style.clipPath;
if (!clip || clip == "none") {
clip = false;
for (var i = 0; i < Modernizr._domPrefixes.length; i++) {
test = Modernizr._domPrefixes[i] + 'ClipPath';
if (style[test] && style[test] !== "none") {
clip = true;
break;
}
}
}
return Modernizr.testProp('clipPath') && clip;
});
});
}
})(Modernizr);
Check this codepen to see it in action.

Resources