particles.js 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539
  1. /*!
  2. Particles
  3. Version: 2.0.0
  4. Plugin URL: https://vincentgarreau.com/particles.js/
  5. License: Copyright Vincent Garreau - vincentgarreau.com | Licensed under MIT
  6. !*/
  7. var pJS = function(tag_id, params){
  8. var canvas_el = document.querySelector('#'+tag_id+' > .particles-js-canvas-el');
  9. /* particles.js variables with default values */
  10. this.pJS = {
  11. canvas: {
  12. el: canvas_el,
  13. w: canvas_el.offsetWidth,
  14. h: canvas_el.offsetHeight
  15. },
  16. particles: {
  17. number: {
  18. value: 400,
  19. density: {
  20. enable: true,
  21. value_area: 800
  22. }
  23. },
  24. color: {
  25. value: '#fff'
  26. },
  27. shape: {
  28. type: 'circle',
  29. stroke: {
  30. width: 0,
  31. color: '#ff0000'
  32. },
  33. polygon: {
  34. nb_sides: 5
  35. },
  36. image: {
  37. src: '',
  38. width: 100,
  39. height: 100
  40. }
  41. },
  42. opacity: {
  43. value: 1,
  44. random: false,
  45. anim: {
  46. enable: false,
  47. speed: 2,
  48. opacity_min: 0,
  49. sync: false
  50. }
  51. },
  52. size: {
  53. value: 20,
  54. random: false,
  55. anim: {
  56. enable: false,
  57. speed: 20,
  58. size_min: 0,
  59. sync: false
  60. }
  61. },
  62. line_linked: {
  63. enable: true,
  64. distance: 100,
  65. color: '#fff',
  66. opacity: 1,
  67. width: 1
  68. },
  69. move: {
  70. enable: true,
  71. speed: 2,
  72. direction: 'none',
  73. random: false,
  74. straight: false,
  75. out_mode: 'out',
  76. bounce: false,
  77. attract: {
  78. enable: false,
  79. rotateX: 3000,
  80. rotateY: 3000
  81. }
  82. },
  83. array: []
  84. },
  85. interactivity: {
  86. detect_on: 'canvas',
  87. events: {
  88. onhover: {
  89. enable: true,
  90. mode: 'grab'
  91. },
  92. onclick: {
  93. enable: true,
  94. mode: 'push'
  95. },
  96. resize: true
  97. },
  98. modes: {
  99. grab:{
  100. distance: 100,
  101. line_linked:{
  102. opacity: 1
  103. }
  104. },
  105. bubble:{
  106. distance: 200,
  107. size: 80,
  108. duration: 0.4
  109. },
  110. repulse:{
  111. distance: 200,
  112. duration: 0.4
  113. },
  114. push:{
  115. particles_nb: 4
  116. },
  117. remove:{
  118. particles_nb: 2
  119. }
  120. },
  121. mouse:{}
  122. },
  123. retina_detect: false,
  124. fn: {
  125. interact: {},
  126. modes: {},
  127. vendors:{}
  128. },
  129. tmp: {}
  130. };
  131. var pJS = this.pJS;
  132. /* params settings */
  133. if(params){
  134. Object.deepExtend(pJS, params);
  135. }
  136. pJS.tmp.obj = {
  137. size_value: pJS.particles.size.value,
  138. size_anim_speed: pJS.particles.size.anim.speed,
  139. move_speed: pJS.particles.move.speed,
  140. line_linked_distance: pJS.particles.line_linked.distance,
  141. line_linked_width: pJS.particles.line_linked.width,
  142. mode_grab_distance: pJS.interactivity.modes.grab.distance,
  143. mode_bubble_distance: pJS.interactivity.modes.bubble.distance,
  144. mode_bubble_size: pJS.interactivity.modes.bubble.size,
  145. mode_repulse_distance: pJS.interactivity.modes.repulse.distance
  146. };
  147. pJS.fn.retinaInit = function(){
  148. if(pJS.retina_detect && window.devicePixelRatio > 1){
  149. pJS.canvas.pxratio = window.devicePixelRatio;
  150. pJS.tmp.retina = true;
  151. }
  152. else{
  153. pJS.canvas.pxratio = 1;
  154. pJS.tmp.retina = false;
  155. }
  156. pJS.canvas.w = pJS.canvas.el.offsetWidth * pJS.canvas.pxratio;
  157. pJS.canvas.h = pJS.canvas.el.offsetHeight * pJS.canvas.pxratio;
  158. pJS.particles.size.value = pJS.tmp.obj.size_value * pJS.canvas.pxratio;
  159. pJS.particles.size.anim.speed = pJS.tmp.obj.size_anim_speed * pJS.canvas.pxratio;
  160. pJS.particles.move.speed = pJS.tmp.obj.move_speed * pJS.canvas.pxratio;
  161. pJS.particles.line_linked.distance = pJS.tmp.obj.line_linked_distance * pJS.canvas.pxratio;
  162. pJS.interactivity.modes.grab.distance = pJS.tmp.obj.mode_grab_distance * pJS.canvas.pxratio;
  163. pJS.interactivity.modes.bubble.distance = pJS.tmp.obj.mode_bubble_distance * pJS.canvas.pxratio;
  164. pJS.particles.line_linked.width = pJS.tmp.obj.line_linked_width * pJS.canvas.pxratio;
  165. pJS.interactivity.modes.bubble.size = pJS.tmp.obj.mode_bubble_size * pJS.canvas.pxratio;
  166. pJS.interactivity.modes.repulse.distance = pJS.tmp.obj.mode_repulse_distance * pJS.canvas.pxratio;
  167. };
  168. /* ---------- pJS functions - canvas ------------ */
  169. pJS.fn.canvasInit = function(){
  170. pJS.canvas.ctx = pJS.canvas.el.getContext('2d');
  171. };
  172. pJS.fn.canvasSize = function(){
  173. pJS.canvas.el.width = pJS.canvas.w;
  174. pJS.canvas.el.height = pJS.canvas.h;
  175. if(pJS && pJS.interactivity.events.resize){
  176. window.addEventListener('resize', function(){
  177. pJS.canvas.w = pJS.canvas.el.offsetWidth;
  178. pJS.canvas.h = pJS.canvas.el.offsetHeight;
  179. /* resize canvas */
  180. if(pJS.tmp.retina){
  181. pJS.canvas.w *= pJS.canvas.pxratio;
  182. pJS.canvas.h *= pJS.canvas.pxratio;
  183. }
  184. pJS.canvas.el.width = pJS.canvas.w;
  185. pJS.canvas.el.height = pJS.canvas.h;
  186. /* repaint canvas on anim disabled */
  187. if(!pJS.particles.move.enable){
  188. pJS.fn.particlesEmpty();
  189. pJS.fn.particlesCreate();
  190. pJS.fn.particlesDraw();
  191. pJS.fn.vendors.densityAutoParticles();
  192. }
  193. /* density particles enabled */
  194. pJS.fn.vendors.densityAutoParticles();
  195. });
  196. }
  197. };
  198. pJS.fn.canvasPaint = function(){
  199. pJS.canvas.ctx.fillRect(0, 0, pJS.canvas.w, pJS.canvas.h);
  200. };
  201. pJS.fn.canvasClear = function(){
  202. pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h);
  203. };
  204. /* --------- pJS functions - particles ----------- */
  205. pJS.fn.particle = function(color, opacity, position){
  206. /* size */
  207. this.radius = (pJS.particles.size.random ? Math.random() : 1) * pJS.particles.size.value;
  208. if(pJS.particles.size.anim.enable){
  209. this.size_status = false;
  210. this.vs = pJS.particles.size.anim.speed / 100;
  211. if(!pJS.particles.size.anim.sync){
  212. this.vs = this.vs * Math.random();
  213. }
  214. }
  215. /* position */
  216. this.x = position ? position.x : Math.random() * pJS.canvas.w;
  217. this.y = position ? position.y : Math.random() * pJS.canvas.h;
  218. /* check position - into the canvas */
  219. if(this.x > pJS.canvas.w - this.radius*2) this.x = this.x - this.radius;
  220. else if(this.x < this.radius*2) this.x = this.x + this.radius;
  221. if(this.y > pJS.canvas.h - this.radius*2) this.y = this.y - this.radius;
  222. else if(this.y < this.radius*2) this.y = this.y + this.radius;
  223. /* check position - avoid overlap */
  224. if(pJS.particles.move.bounce){
  225. pJS.fn.vendors.checkOverlap(this, position);
  226. }
  227. /* color */
  228. this.color = {};
  229. if(typeof(color.value) == 'object'){
  230. if(color.value instanceof Array){
  231. var color_selected = color.value[Math.floor(Math.random() * pJS.particles.color.value.length)];
  232. this.color.rgb = hexToRgb(color_selected);
  233. }else{
  234. if(color.value.r != undefined && color.value.g != undefined && color.value.b != undefined){
  235. this.color.rgb = {
  236. r: color.value.r,
  237. g: color.value.g,
  238. b: color.value.b
  239. }
  240. }
  241. if(color.value.h != undefined && color.value.s != undefined && color.value.l != undefined){
  242. this.color.hsl = {
  243. h: color.value.h,
  244. s: color.value.s,
  245. l: color.value.l
  246. }
  247. }
  248. }
  249. }
  250. else if(color.value == 'random'){
  251. this.color.rgb = {
  252. r: (Math.floor(Math.random() * (255 - 0 + 1)) + 0),
  253. g: (Math.floor(Math.random() * (255 - 0 + 1)) + 0),
  254. b: (Math.floor(Math.random() * (255 - 0 + 1)) + 0)
  255. }
  256. }
  257. else if(typeof(color.value) == 'string'){
  258. this.color = color;
  259. this.color.rgb = hexToRgb(this.color.value);
  260. }
  261. /* opacity */
  262. this.opacity = (pJS.particles.opacity.random ? Math.random() : 1) * pJS.particles.opacity.value;
  263. if(pJS.particles.opacity.anim.enable){
  264. this.opacity_status = false;
  265. this.vo = pJS.particles.opacity.anim.speed / 100;
  266. if(!pJS.particles.opacity.anim.sync){
  267. this.vo = this.vo * Math.random();
  268. }
  269. }
  270. /* animation - velocity for speed */
  271. var velbase = {}
  272. switch(pJS.particles.move.direction){
  273. case 'top':
  274. velbase = { x:0, y:-1 };
  275. break;
  276. case 'top-right':
  277. velbase = { x:0.5, y:-0.5 };
  278. break;
  279. case 'right':
  280. velbase = { x:1, y:-0 };
  281. break;
  282. case 'bottom-right':
  283. velbase = { x:0.5, y:0.5 };
  284. break;
  285. case 'bottom':
  286. velbase = { x:0, y:1 };
  287. break;
  288. case 'bottom-left':
  289. velbase = { x:-0.5, y:1 };
  290. break;
  291. case 'left':
  292. velbase = { x:-1, y:0 };
  293. break;
  294. case 'top-left':
  295. velbase = { x:-0.5, y:-0.5 };
  296. break;
  297. default:
  298. velbase = { x:0, y:0 };
  299. break;
  300. }
  301. if(pJS.particles.move.straight){
  302. this.vx = velbase.x;
  303. this.vy = velbase.y;
  304. if(pJS.particles.move.random){
  305. this.vx = this.vx * (Math.random());
  306. this.vy = this.vy * (Math.random());
  307. }
  308. }else{
  309. this.vx = velbase.x + Math.random()-0.5;
  310. this.vy = velbase.y + Math.random()-0.5;
  311. }
  312. // var theta = 2.0 * Math.PI * Math.random();
  313. // this.vx = Math.cos(theta);
  314. // this.vy = Math.sin(theta);
  315. this.vx_i = this.vx;
  316. this.vy_i = this.vy;
  317. /* if shape is image */
  318. var shape_type = pJS.particles.shape.type;
  319. if(typeof(shape_type) == 'object'){
  320. if(shape_type instanceof Array){
  321. var shape_selected = shape_type[Math.floor(Math.random() * shape_type.length)];
  322. this.shape = shape_selected;
  323. }
  324. }else{
  325. this.shape = shape_type;
  326. }
  327. if(this.shape == 'image'){
  328. var sh = pJS.particles.shape;
  329. this.img = {
  330. src: sh.image.src,
  331. ratio: sh.image.width / sh.image.height
  332. }
  333. if(!this.img.ratio) this.img.ratio = 1;
  334. if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg != undefined){
  335. pJS.fn.vendors.createSvgImg(this);
  336. if(pJS.tmp.pushing){
  337. this.img.loaded = false;
  338. }
  339. }
  340. }
  341. };
  342. pJS.fn.particle.prototype.draw = function() {
  343. var p = this;
  344. if(p.radius_bubble != undefined){
  345. var radius = p.radius_bubble;
  346. }else{
  347. var radius = p.radius;
  348. }
  349. if(p.opacity_bubble != undefined){
  350. var opacity = p.opacity_bubble;
  351. }else{
  352. var opacity = p.opacity;
  353. }
  354. if(p.color.rgb){
  355. var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+opacity+')';
  356. }else{
  357. var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+opacity+')';
  358. }
  359. pJS.canvas.ctx.fillStyle = color_value;
  360. pJS.canvas.ctx.beginPath();
  361. switch(p.shape){
  362. case 'circle':
  363. pJS.canvas.ctx.arc(p.x, p.y, radius, 0, Math.PI * 2, false);
  364. break;
  365. case 'edge':
  366. pJS.canvas.ctx.rect(p.x-radius, p.y-radius, radius*2, radius*2);
  367. break;
  368. case 'triangle':
  369. pJS.fn.vendors.drawShape(pJS.canvas.ctx, p.x-radius, p.y+radius / 1.66, radius*2, 3, 2);
  370. break;
  371. case 'polygon':
  372. pJS.fn.vendors.drawShape(
  373. pJS.canvas.ctx,
  374. p.x - radius / (pJS.particles.shape.polygon.nb_sides/3.5), // startX
  375. p.y - radius / (2.66/3.5), // startY
  376. radius*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength
  377. pJS.particles.shape.polygon.nb_sides, // sideCountNumerator
  378. 1 // sideCountDenominator
  379. );
  380. break;
  381. case 'star':
  382. pJS.fn.vendors.drawShape(
  383. pJS.canvas.ctx,
  384. p.x - radius*2 / (pJS.particles.shape.polygon.nb_sides/4), // startX
  385. p.y - radius / (2*2.66/3.5), // startY
  386. radius*2*2.66 / (pJS.particles.shape.polygon.nb_sides/3), // sideLength
  387. pJS.particles.shape.polygon.nb_sides, // sideCountNumerator
  388. 2 // sideCountDenominator
  389. );
  390. break;
  391. case 'image':
  392. function draw(){
  393. pJS.canvas.ctx.drawImage(
  394. img_obj,
  395. p.x-radius,
  396. p.y-radius,
  397. radius*2,
  398. radius*2 / p.img.ratio
  399. );
  400. }
  401. if(pJS.tmp.img_type == 'svg'){
  402. var img_obj = p.img.obj;
  403. }else{
  404. var img_obj = pJS.tmp.img_obj;
  405. }
  406. if(img_obj){
  407. draw();
  408. }
  409. break;
  410. }
  411. pJS.canvas.ctx.closePath();
  412. if(pJS.particles.shape.stroke.width > 0){
  413. pJS.canvas.ctx.strokeStyle = pJS.particles.shape.stroke.color;
  414. pJS.canvas.ctx.lineWidth = pJS.particles.shape.stroke.width;
  415. pJS.canvas.ctx.stroke();
  416. }
  417. pJS.canvas.ctx.fill();
  418. };
  419. pJS.fn.particlesCreate = function(){
  420. for(var i = 0; i < pJS.particles.number.value; i++) {
  421. pJS.particles.array.push(new pJS.fn.particle(pJS.particles.color, pJS.particles.opacity.value));
  422. }
  423. };
  424. pJS.fn.particlesUpdate = function(){
  425. for(var i = 0; i < pJS.particles.array.length; i++){
  426. /* the particle */
  427. var p = pJS.particles.array[i];
  428. // var d = ( dx = pJS.interactivity.mouse.click_pos_x - p.x ) * dx + ( dy = pJS.interactivity.mouse.click_pos_y - p.y ) * dy;
  429. // var f = -BANG_SIZE / d;
  430. // if ( d < BANG_SIZE ) {
  431. // var t = Math.atan2( dy, dx );
  432. // p.vx = f * Math.cos(t);
  433. // p.vy = f * Math.sin(t);
  434. // }
  435. /* move the particle */
  436. if(pJS.particles.move.enable){
  437. var ms = pJS.particles.move.speed/2;
  438. p.x += p.vx * ms;
  439. p.y += p.vy * ms;
  440. }
  441. /* change opacity status */
  442. if(pJS.particles.opacity.anim.enable) {
  443. if(p.opacity_status == true) {
  444. if(p.opacity >= pJS.particles.opacity.value) p.opacity_status = false;
  445. p.opacity += p.vo;
  446. }else {
  447. if(p.opacity <= pJS.particles.opacity.anim.opacity_min) p.opacity_status = true;
  448. p.opacity -= p.vo;
  449. }
  450. if(p.opacity < 0) p.opacity = 0;
  451. }
  452. /* change size */
  453. if(pJS.particles.size.anim.enable){
  454. if(p.size_status == true){
  455. if(p.radius >= pJS.particles.size.value) p.size_status = false;
  456. p.radius += p.vs;
  457. }else{
  458. if(p.radius <= pJS.particles.size.anim.size_min) p.size_status = true;
  459. p.radius -= p.vs;
  460. }
  461. if(p.radius < 0) p.radius = 0;
  462. }
  463. /* change particle position if it is out of canvas */
  464. if(pJS.particles.move.out_mode == 'bounce'){
  465. var new_pos = {
  466. x_left: p.radius,
  467. x_right: pJS.canvas.w,
  468. y_top: p.radius,
  469. y_bottom: pJS.canvas.h
  470. }
  471. }else{
  472. var new_pos = {
  473. x_left: -p.radius,
  474. x_right: pJS.canvas.w + p.radius,
  475. y_top: -p.radius,
  476. y_bottom: pJS.canvas.h + p.radius
  477. }
  478. }
  479. if(p.x - p.radius > pJS.canvas.w){
  480. p.x = new_pos.x_left;
  481. p.y = Math.random() * pJS.canvas.h;
  482. }
  483. else if(p.x + p.radius < 0){
  484. p.x = new_pos.x_right;
  485. p.y = Math.random() * pJS.canvas.h;
  486. }
  487. if(p.y - p.radius > pJS.canvas.h){
  488. p.y = new_pos.y_top;
  489. p.x = Math.random() * pJS.canvas.w;
  490. }
  491. else if(p.y + p.radius < 0){
  492. p.y = new_pos.y_bottom;
  493. p.x = Math.random() * pJS.canvas.w;
  494. }
  495. /* out of canvas modes */
  496. switch(pJS.particles.move.out_mode){
  497. case 'bounce':
  498. if (p.x + p.radius > pJS.canvas.w) p.vx = -p.vx;
  499. else if (p.x - p.radius < 0) p.vx = -p.vx;
  500. if (p.y + p.radius > pJS.canvas.h) p.vy = -p.vy;
  501. else if (p.y - p.radius < 0) p.vy = -p.vy;
  502. break;
  503. }
  504. /* events */
  505. if(isInArray('grab', pJS.interactivity.events.onhover.mode)){
  506. pJS.fn.modes.grabParticle(p);
  507. }
  508. if(isInArray('bubble', pJS.interactivity.events.onhover.mode) || isInArray('bubble', pJS.interactivity.events.onclick.mode)){
  509. pJS.fn.modes.bubbleParticle(p);
  510. }
  511. if(isInArray('repulse', pJS.interactivity.events.onhover.mode) || isInArray('repulse', pJS.interactivity.events.onclick.mode)){
  512. pJS.fn.modes.repulseParticle(p);
  513. }
  514. /* interaction auto between particles */
  515. if(pJS.particles.line_linked.enable || pJS.particles.move.attract.enable){
  516. for(var j = i + 1; j < pJS.particles.array.length; j++){
  517. var p2 = pJS.particles.array[j];
  518. /* link particles */
  519. if(pJS.particles.line_linked.enable){
  520. pJS.fn.interact.linkParticles(p,p2);
  521. }
  522. /* attract particles */
  523. if(pJS.particles.move.attract.enable){
  524. pJS.fn.interact.attractParticles(p,p2);
  525. }
  526. /* bounce particles */
  527. if(pJS.particles.move.bounce){
  528. pJS.fn.interact.bounceParticles(p,p2);
  529. }
  530. }
  531. }
  532. }
  533. };
  534. pJS.fn.particlesDraw = function(){
  535. /* clear canvas */
  536. pJS.canvas.ctx.clearRect(0, 0, pJS.canvas.w, pJS.canvas.h);
  537. /* update each particles param */
  538. pJS.fn.particlesUpdate();
  539. /* draw each particle */
  540. for(var i = 0; i < pJS.particles.array.length; i++){
  541. var p = pJS.particles.array[i];
  542. p.draw();
  543. }
  544. };
  545. pJS.fn.particlesEmpty = function(){
  546. pJS.particles.array = [];
  547. };
  548. pJS.fn.particlesRefresh = function(){
  549. /* init all */
  550. cancelRequestAnimFrame(pJS.fn.checkAnimFrame);
  551. cancelRequestAnimFrame(pJS.fn.drawAnimFrame);
  552. pJS.tmp.source_svg = undefined;
  553. pJS.tmp.img_obj = undefined;
  554. pJS.tmp.count_svg = 0;
  555. pJS.fn.particlesEmpty();
  556. pJS.fn.canvasClear();
  557. /* restart */
  558. pJS.fn.vendors.start();
  559. };
  560. /* ---------- pJS functions - particles interaction ------------ */
  561. pJS.fn.interact.linkParticles = function(p1, p2){
  562. var dx = p1.x - p2.x,
  563. dy = p1.y - p2.y,
  564. dist = Math.sqrt(dx*dx + dy*dy);
  565. /* draw a line between p1 and p2 if the distance between them is under the config distance */
  566. if(dist <= pJS.particles.line_linked.distance){
  567. var opacity_line = pJS.particles.line_linked.opacity - (dist / (1/pJS.particles.line_linked.opacity)) / pJS.particles.line_linked.distance;
  568. if(opacity_line > 0){
  569. /* style */
  570. var color_line = pJS.particles.line_linked.color_rgb_line;
  571. pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')';
  572. pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width;
  573. //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */
  574. /* path */
  575. pJS.canvas.ctx.beginPath();
  576. pJS.canvas.ctx.moveTo(p1.x, p1.y);
  577. pJS.canvas.ctx.lineTo(p2.x, p2.y);
  578. pJS.canvas.ctx.stroke();
  579. pJS.canvas.ctx.closePath();
  580. }
  581. }
  582. };
  583. pJS.fn.interact.attractParticles = function(p1, p2){
  584. /* condensed particles */
  585. var dx = p1.x - p2.x,
  586. dy = p1.y - p2.y,
  587. dist = Math.sqrt(dx*dx + dy*dy);
  588. if(dist <= pJS.particles.line_linked.distance){
  589. var ax = dx/(pJS.particles.move.attract.rotateX*1000),
  590. ay = dy/(pJS.particles.move.attract.rotateY*1000);
  591. p1.vx -= ax;
  592. p1.vy -= ay;
  593. p2.vx += ax;
  594. p2.vy += ay;
  595. }
  596. }
  597. pJS.fn.interact.bounceParticles = function(p1, p2){
  598. var dx = p1.x - p2.x,
  599. dy = p1.y - p2.y,
  600. dist = Math.sqrt(dx*dx + dy*dy),
  601. dist_p = p1.radius+p2.radius;
  602. if(dist <= dist_p){
  603. p1.vx = -p1.vx;
  604. p1.vy = -p1.vy;
  605. p2.vx = -p2.vx;
  606. p2.vy = -p2.vy;
  607. }
  608. }
  609. /* ---------- pJS functions - modes events ------------ */
  610. pJS.fn.modes.pushParticles = function(nb, pos){
  611. pJS.tmp.pushing = true;
  612. for(var i = 0; i < nb; i++){
  613. pJS.particles.array.push(
  614. new pJS.fn.particle(
  615. pJS.particles.color,
  616. pJS.particles.opacity.value,
  617. {
  618. 'x': pos ? pos.pos_x : Math.random() * pJS.canvas.w,
  619. 'y': pos ? pos.pos_y : Math.random() * pJS.canvas.h
  620. }
  621. )
  622. )
  623. if(i == nb-1){
  624. if(!pJS.particles.move.enable){
  625. pJS.fn.particlesDraw();
  626. }
  627. pJS.tmp.pushing = false;
  628. }
  629. }
  630. };
  631. pJS.fn.modes.removeParticles = function(nb){
  632. pJS.particles.array.splice(0, nb);
  633. if(!pJS.particles.move.enable){
  634. pJS.fn.particlesDraw();
  635. }
  636. };
  637. pJS.fn.modes.bubbleParticle = function(p){
  638. /* on hover event */
  639. if(pJS.interactivity.events.onhover.enable && isInArray('bubble', pJS.interactivity.events.onhover.mode)){
  640. var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,
  641. dy_mouse = p.y - pJS.interactivity.mouse.pos_y,
  642. dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse),
  643. ratio = 1 - dist_mouse / pJS.interactivity.modes.bubble.distance;
  644. function init(){
  645. p.opacity_bubble = p.opacity;
  646. p.radius_bubble = p.radius;
  647. }
  648. /* mousemove - check ratio */
  649. if(dist_mouse <= pJS.interactivity.modes.bubble.distance){
  650. if(ratio >= 0 && pJS.interactivity.status == 'mousemove'){
  651. /* size */
  652. if(pJS.interactivity.modes.bubble.size != pJS.particles.size.value){
  653. if(pJS.interactivity.modes.bubble.size > pJS.particles.size.value){
  654. var size = p.radius + (pJS.interactivity.modes.bubble.size*ratio);
  655. if(size >= 0){
  656. p.radius_bubble = size;
  657. }
  658. }else{
  659. var dif = p.radius - pJS.interactivity.modes.bubble.size,
  660. size = p.radius - (dif*ratio);
  661. if(size > 0){
  662. p.radius_bubble = size;
  663. }else{
  664. p.radius_bubble = 0;
  665. }
  666. }
  667. }
  668. /* opacity */
  669. if(pJS.interactivity.modes.bubble.opacity != pJS.particles.opacity.value){
  670. if(pJS.interactivity.modes.bubble.opacity > pJS.particles.opacity.value){
  671. var opacity = pJS.interactivity.modes.bubble.opacity*ratio;
  672. if(opacity > p.opacity && opacity <= pJS.interactivity.modes.bubble.opacity){
  673. p.opacity_bubble = opacity;
  674. }
  675. }else{
  676. var opacity = p.opacity - (pJS.particles.opacity.value-pJS.interactivity.modes.bubble.opacity)*ratio;
  677. if(opacity < p.opacity && opacity >= pJS.interactivity.modes.bubble.opacity){
  678. p.opacity_bubble = opacity;
  679. }
  680. }
  681. }
  682. }
  683. }else{
  684. init();
  685. }
  686. /* mouseleave */
  687. if(pJS.interactivity.status == 'mouseleave'){
  688. init();
  689. }
  690. }
  691. /* on click event */
  692. else if(pJS.interactivity.events.onclick.enable && isInArray('bubble', pJS.interactivity.events.onclick.mode)){
  693. if(pJS.tmp.bubble_clicking){
  694. var dx_mouse = p.x - pJS.interactivity.mouse.click_pos_x,
  695. dy_mouse = p.y - pJS.interactivity.mouse.click_pos_y,
  696. dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse),
  697. time_spent = (new Date().getTime() - pJS.interactivity.mouse.click_time)/1000;
  698. if(time_spent > pJS.interactivity.modes.bubble.duration){
  699. pJS.tmp.bubble_duration_end = true;
  700. }
  701. if(time_spent > pJS.interactivity.modes.bubble.duration*2){
  702. pJS.tmp.bubble_clicking = false;
  703. pJS.tmp.bubble_duration_end = false;
  704. }
  705. }
  706. function process(bubble_param, particles_param, p_obj_bubble, p_obj, id){
  707. if(bubble_param != particles_param){
  708. if(!pJS.tmp.bubble_duration_end){
  709. if(dist_mouse <= pJS.interactivity.modes.bubble.distance){
  710. if(p_obj_bubble != undefined) var obj = p_obj_bubble;
  711. else var obj = p_obj;
  712. if(obj != bubble_param){
  713. var value = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration);
  714. if(id == 'size') p.radius_bubble = value;
  715. if(id == 'opacity') p.opacity_bubble = value;
  716. }
  717. }else{
  718. if(id == 'size') p.radius_bubble = undefined;
  719. if(id == 'opacity') p.opacity_bubble = undefined;
  720. }
  721. }else{
  722. if(p_obj_bubble != undefined){
  723. var value_tmp = p_obj - (time_spent * (p_obj - bubble_param) / pJS.interactivity.modes.bubble.duration),
  724. dif = bubble_param - value_tmp;
  725. value = bubble_param + dif;
  726. if(id == 'size') p.radius_bubble = value;
  727. if(id == 'opacity') p.opacity_bubble = value;
  728. }
  729. }
  730. }
  731. }
  732. if(pJS.tmp.bubble_clicking){
  733. /* size */
  734. process(pJS.interactivity.modes.bubble.size, pJS.particles.size.value, p.radius_bubble, p.radius, 'size');
  735. /* opacity */
  736. process(pJS.interactivity.modes.bubble.opacity, pJS.particles.opacity.value, p.opacity_bubble, p.opacity, 'opacity');
  737. }
  738. }
  739. };
  740. pJS.fn.modes.repulseParticle = function(p){
  741. if(pJS.interactivity.events.onhover.enable && isInArray('repulse', pJS.interactivity.events.onhover.mode) && pJS.interactivity.status == 'mousemove') {
  742. var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,
  743. dy_mouse = p.y - pJS.interactivity.mouse.pos_y,
  744. dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse);
  745. var normVec = {x: dx_mouse/dist_mouse, y: dy_mouse/dist_mouse},
  746. repulseRadius = pJS.interactivity.modes.repulse.distance,
  747. velocity = 100,
  748. repulseFactor = clamp((1/repulseRadius)*(-1*Math.pow(dist_mouse/repulseRadius,2)+1)*repulseRadius*velocity, 0, 50);
  749. var pos = {
  750. x: p.x + normVec.x * repulseFactor,
  751. y: p.y + normVec.y * repulseFactor
  752. }
  753. if(pJS.particles.move.out_mode == 'bounce'){
  754. if(pos.x - p.radius > 0 && pos.x + p.radius < pJS.canvas.w) p.x = pos.x;
  755. if(pos.y - p.radius > 0 && pos.y + p.radius < pJS.canvas.h) p.y = pos.y;
  756. }else{
  757. p.x = pos.x;
  758. p.y = pos.y;
  759. }
  760. }
  761. else if(pJS.interactivity.events.onclick.enable && isInArray('repulse', pJS.interactivity.events.onclick.mode)) {
  762. if(!pJS.tmp.repulse_finish){
  763. pJS.tmp.repulse_count++;
  764. if(pJS.tmp.repulse_count == pJS.particles.array.length){
  765. pJS.tmp.repulse_finish = true;
  766. }
  767. }
  768. if(pJS.tmp.repulse_clicking){
  769. var repulseRadius = Math.pow(pJS.interactivity.modes.repulse.distance/6, 3);
  770. var dx = pJS.interactivity.mouse.click_pos_x - p.x,
  771. dy = pJS.interactivity.mouse.click_pos_y - p.y,
  772. d = dx*dx + dy*dy;
  773. var force = -repulseRadius / d * 1;
  774. function process(){
  775. var f = Math.atan2(dy,dx);
  776. p.vx = force * Math.cos(f);
  777. p.vy = force * Math.sin(f);
  778. if(pJS.particles.move.out_mode == 'bounce'){
  779. var pos = {
  780. x: p.x + p.vx,
  781. y: p.y + p.vy
  782. }
  783. if (pos.x + p.radius > pJS.canvas.w) p.vx = -p.vx;
  784. else if (pos.x - p.radius < 0) p.vx = -p.vx;
  785. if (pos.y + p.radius > pJS.canvas.h) p.vy = -p.vy;
  786. else if (pos.y - p.radius < 0) p.vy = -p.vy;
  787. }
  788. }
  789. // default
  790. if(d <= repulseRadius){
  791. process();
  792. }
  793. // bang - slow motion mode
  794. // if(!pJS.tmp.repulse_finish){
  795. // if(d <= repulseRadius){
  796. // process();
  797. // }
  798. // }else{
  799. // process();
  800. // }
  801. }else{
  802. if(pJS.tmp.repulse_clicking == false){
  803. p.vx = p.vx_i;
  804. p.vy = p.vy_i;
  805. }
  806. }
  807. }
  808. }
  809. pJS.fn.modes.grabParticle = function(p){
  810. if(pJS.interactivity.events.onhover.enable && pJS.interactivity.status == 'mousemove'){
  811. var dx_mouse = p.x - pJS.interactivity.mouse.pos_x,
  812. dy_mouse = p.y - pJS.interactivity.mouse.pos_y,
  813. dist_mouse = Math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse);
  814. /* draw a line between the cursor and the particle if the distance between them is under the config distance */
  815. if(dist_mouse <= pJS.interactivity.modes.grab.distance){
  816. var opacity_line = pJS.interactivity.modes.grab.line_linked.opacity - (dist_mouse / (1/pJS.interactivity.modes.grab.line_linked.opacity)) / pJS.interactivity.modes.grab.distance;
  817. if(opacity_line > 0){
  818. /* style */
  819. var color_line = pJS.particles.line_linked.color_rgb_line;
  820. pJS.canvas.ctx.strokeStyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+opacity_line+')';
  821. pJS.canvas.ctx.lineWidth = pJS.particles.line_linked.width;
  822. //pJS.canvas.ctx.lineCap = 'round'; /* performance issue */
  823. /* path */
  824. pJS.canvas.ctx.beginPath();
  825. pJS.canvas.ctx.moveTo(p.x, p.y);
  826. pJS.canvas.ctx.lineTo(pJS.interactivity.mouse.pos_x, pJS.interactivity.mouse.pos_y);
  827. pJS.canvas.ctx.stroke();
  828. pJS.canvas.ctx.closePath();
  829. }
  830. }
  831. }
  832. };
  833. /* ---------- pJS functions - vendors ------------ */
  834. pJS.fn.vendors.eventsListeners = function(){
  835. /* events target element */
  836. if(pJS.interactivity.detect_on == 'window'){
  837. pJS.interactivity.el = window;
  838. }else{
  839. pJS.interactivity.el = pJS.canvas.el;
  840. }
  841. /* detect mouse pos - on hover / click event */
  842. if(pJS.interactivity.events.onhover.enable || pJS.interactivity.events.onclick.enable){
  843. /* el on mousemove */
  844. pJS.interactivity.el.addEventListener('mousemove', function(e){
  845. if(pJS.interactivity.el == window){
  846. var pos_x = e.clientX,
  847. pos_y = e.clientY;
  848. }
  849. else{
  850. var pos_x = e.offsetX || e.clientX,
  851. pos_y = e.offsetY || e.clientY;
  852. }
  853. pJS.interactivity.mouse.pos_x = pos_x;
  854. pJS.interactivity.mouse.pos_y = pos_y;
  855. if(pJS.tmp.retina){
  856. pJS.interactivity.mouse.pos_x *= pJS.canvas.pxratio;
  857. pJS.interactivity.mouse.pos_y *= pJS.canvas.pxratio;
  858. }
  859. pJS.interactivity.status = 'mousemove';
  860. });
  861. /* el on onmouseleave */
  862. pJS.interactivity.el.addEventListener('mouseleave', function(e){
  863. pJS.interactivity.mouse.pos_x = null;
  864. pJS.interactivity.mouse.pos_y = null;
  865. pJS.interactivity.status = 'mouseleave';
  866. });
  867. }
  868. /* on click event */
  869. if(pJS.interactivity.events.onclick.enable){
  870. pJS.interactivity.el.addEventListener('click', function(){
  871. pJS.interactivity.mouse.click_pos_x = pJS.interactivity.mouse.pos_x;
  872. pJS.interactivity.mouse.click_pos_y = pJS.interactivity.mouse.pos_y;
  873. pJS.interactivity.mouse.click_time = new Date().getTime();
  874. if(pJS.interactivity.events.onclick.enable){
  875. switch(pJS.interactivity.events.onclick.mode){
  876. case 'push':
  877. if(pJS.particles.move.enable){
  878. pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse);
  879. }else{
  880. if(pJS.interactivity.modes.push.particles_nb == 1){
  881. pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb, pJS.interactivity.mouse);
  882. }
  883. else if(pJS.interactivity.modes.push.particles_nb > 1){
  884. pJS.fn.modes.pushParticles(pJS.interactivity.modes.push.particles_nb);
  885. }
  886. }
  887. break;
  888. case 'remove':
  889. pJS.fn.modes.removeParticles(pJS.interactivity.modes.remove.particles_nb);
  890. break;
  891. case 'bubble':
  892. pJS.tmp.bubble_clicking = true;
  893. break;
  894. case 'repulse':
  895. pJS.tmp.repulse_clicking = true;
  896. pJS.tmp.repulse_count = 0;
  897. pJS.tmp.repulse_finish = false;
  898. setTimeout(function(){
  899. pJS.tmp.repulse_clicking = false;
  900. }, pJS.interactivity.modes.repulse.duration*1000)
  901. break;
  902. }
  903. }
  904. });
  905. }
  906. };
  907. pJS.fn.vendors.densityAutoParticles = function(){
  908. if(pJS.particles.number.density.enable){
  909. /* calc area */
  910. var area = pJS.canvas.el.width * pJS.canvas.el.height / 1000;
  911. if(pJS.tmp.retina){
  912. area = area/(pJS.canvas.pxratio*2);
  913. }
  914. /* calc number of particles based on density area */
  915. var nb_particles = area * pJS.particles.number.value / pJS.particles.number.density.value_area;
  916. /* add or remove X particles */
  917. var missing_particles = pJS.particles.array.length - nb_particles;
  918. if(missing_particles < 0) pJS.fn.modes.pushParticles(Math.abs(missing_particles));
  919. else pJS.fn.modes.removeParticles(missing_particles);
  920. }
  921. };
  922. pJS.fn.vendors.checkOverlap = function(p1, position){
  923. for(var i = 0; i < pJS.particles.array.length; i++){
  924. var p2 = pJS.particles.array[i];
  925. var dx = p1.x - p2.x,
  926. dy = p1.y - p2.y,
  927. dist = Math.sqrt(dx*dx + dy*dy);
  928. if(dist <= p1.radius + p2.radius){
  929. p1.x = position ? position.x : Math.random() * pJS.canvas.w;
  930. p1.y = position ? position.y : Math.random() * pJS.canvas.h;
  931. pJS.fn.vendors.checkOverlap(p1);
  932. }
  933. }
  934. };
  935. pJS.fn.vendors.createSvgImg = function(p){
  936. /* set color to svg element */
  937. var svgXml = pJS.tmp.source_svg,
  938. rgbHex = /#([0-9A-F]{3,6})/gi,
  939. coloredSvgXml = svgXml.replace(rgbHex, function (m, r, g, b) {
  940. if(p.color.rgb){
  941. var color_value = 'rgba('+p.color.rgb.r+','+p.color.rgb.g+','+p.color.rgb.b+','+p.opacity+')';
  942. }else{
  943. var color_value = 'hsla('+p.color.hsl.h+','+p.color.hsl.s+'%,'+p.color.hsl.l+'%,'+p.opacity+')';
  944. }
  945. return color_value;
  946. });
  947. /* prepare to create img with colored svg */
  948. var svg = new Blob([coloredSvgXml], {type: 'image/svg+xml;charset=utf-8'}),
  949. DOMURL = window.URL || window.webkitURL || window,
  950. url = DOMURL.createObjectURL(svg);
  951. /* create particle img obj */
  952. var img = new Image();
  953. img.addEventListener('load', function(){
  954. p.img.obj = img;
  955. p.img.loaded = true;
  956. DOMURL.revokeObjectURL(url);
  957. pJS.tmp.count_svg++;
  958. });
  959. img.src = url;
  960. };
  961. pJS.fn.vendors.destroypJS = function(){
  962. cancelAnimationFrame(pJS.fn.drawAnimFrame);
  963. canvas_el.remove();
  964. pJSDom = null;
  965. };
  966. pJS.fn.vendors.drawShape = function(c, startX, startY, sideLength, sideCountNumerator, sideCountDenominator){
  967. // By Programming Thomas - https://programmingthomas.wordpress.com/2013/04/03/n-sided-shapes/
  968. var sideCount = sideCountNumerator * sideCountDenominator;
  969. var decimalSides = sideCountNumerator / sideCountDenominator;
  970. var interiorAngleDegrees = (180 * (decimalSides - 2)) / decimalSides;
  971. var interiorAngle = Math.PI - Math.PI * interiorAngleDegrees / 180; // convert to radians
  972. c.save();
  973. c.beginPath();
  974. c.translate(startX, startY);
  975. c.moveTo(0,0);
  976. for (var i = 0; i < sideCount; i++) {
  977. c.lineTo(sideLength,0);
  978. c.translate(sideLength,0);
  979. c.rotate(interiorAngle);
  980. }
  981. //c.stroke();
  982. c.fill();
  983. c.restore();
  984. };
  985. pJS.fn.vendors.exportImg = function(){
  986. window.open(pJS.canvas.el.toDataURL('image/png'), '_blank');
  987. };
  988. pJS.fn.vendors.loadImg = function(type){
  989. pJS.tmp.img_error = undefined;
  990. if(pJS.particles.shape.image.src != ''){
  991. if(type == 'svg'){
  992. var xhr = new XMLHttpRequest();
  993. xhr.open('GET', pJS.particles.shape.image.src);
  994. xhr.onreadystatechange = function (data) {
  995. if(xhr.readyState == 4){
  996. if(xhr.status == 200){
  997. pJS.tmp.source_svg = data.currentTarget.response;
  998. pJS.fn.vendors.checkBeforeDraw();
  999. }else{
  1000. console.log('Error pJS - Image not found');
  1001. pJS.tmp.img_error = true;
  1002. }
  1003. }
  1004. }
  1005. xhr.send();
  1006. }else{
  1007. var img = new Image();
  1008. img.addEventListener('load', function(){
  1009. pJS.tmp.img_obj = img;
  1010. pJS.fn.vendors.checkBeforeDraw();
  1011. });
  1012. img.src = pJS.particles.shape.image.src;
  1013. }
  1014. }else{
  1015. console.log('Error pJS - No image.src');
  1016. pJS.tmp.img_error = true;
  1017. }
  1018. };
  1019. pJS.fn.vendors.draw = function(){
  1020. if(pJS.particles.shape.type == 'image'){
  1021. if(pJS.tmp.img_type == 'svg'){
  1022. if(pJS.tmp.count_svg >= pJS.particles.number.value){
  1023. pJS.fn.particlesDraw();
  1024. if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);
  1025. else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
  1026. }else{
  1027. //console.log('still loading...');
  1028. if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
  1029. }
  1030. }else{
  1031. if(pJS.tmp.img_obj != undefined){
  1032. pJS.fn.particlesDraw();
  1033. if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);
  1034. else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
  1035. }else{
  1036. if(!pJS.tmp.img_error) pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
  1037. }
  1038. }
  1039. }else{
  1040. pJS.fn.particlesDraw();
  1041. if(!pJS.particles.move.enable) cancelRequestAnimFrame(pJS.fn.drawAnimFrame);
  1042. else pJS.fn.drawAnimFrame = requestAnimFrame(pJS.fn.vendors.draw);
  1043. }
  1044. };
  1045. pJS.fn.vendors.checkBeforeDraw = function(){
  1046. // if shape is image
  1047. if(pJS.particles.shape.type == 'image'){
  1048. if(pJS.tmp.img_type == 'svg' && pJS.tmp.source_svg == undefined){
  1049. pJS.tmp.checkAnimFrame = requestAnimFrame(check);
  1050. }else{
  1051. //console.log('images loaded! cancel check');
  1052. cancelRequestAnimFrame(pJS.tmp.checkAnimFrame);
  1053. if(!pJS.tmp.img_error){
  1054. pJS.fn.vendors.init();
  1055. pJS.fn.vendors.draw();
  1056. }
  1057. }
  1058. }else{
  1059. pJS.fn.vendors.init();
  1060. pJS.fn.vendors.draw();
  1061. }
  1062. };
  1063. pJS.fn.vendors.init = function(){
  1064. /* init canvas + particles */
  1065. pJS.fn.retinaInit();
  1066. pJS.fn.canvasInit();
  1067. pJS.fn.canvasSize();
  1068. pJS.fn.canvasPaint();
  1069. pJS.fn.particlesCreate();
  1070. pJS.fn.vendors.densityAutoParticles();
  1071. /* particles.line_linked - convert hex colors to rgb */
  1072. pJS.particles.line_linked.color_rgb_line = hexToRgb(pJS.particles.line_linked.color);
  1073. };
  1074. pJS.fn.vendors.start = function(){
  1075. if(isInArray('image', pJS.particles.shape.type)){
  1076. pJS.tmp.img_type = pJS.particles.shape.image.src.substr(pJS.particles.shape.image.src.length - 3);
  1077. pJS.fn.vendors.loadImg(pJS.tmp.img_type);
  1078. }else{
  1079. pJS.fn.vendors.checkBeforeDraw();
  1080. }
  1081. };
  1082. /* ---------- pJS - start ------------ */
  1083. pJS.fn.vendors.eventsListeners();
  1084. pJS.fn.vendors.start();
  1085. };
  1086. /* ---------- global functions - vendors ------------ */
  1087. Object.deepExtend = function(destination, source) {
  1088. for (var property in source) {
  1089. if (source[property] && source[property].constructor &&
  1090. source[property].constructor === Object) {
  1091. destination[property] = destination[property] || {};
  1092. arguments.callee(destination[property], source[property]);
  1093. } else {
  1094. destination[property] = source[property];
  1095. }
  1096. }
  1097. return destination;
  1098. };
  1099. window.requestAnimFrame = (function(){
  1100. return window.requestAnimationFrame ||
  1101. window.webkitRequestAnimationFrame ||
  1102. window.mozRequestAnimationFrame ||
  1103. window.oRequestAnimationFrame ||
  1104. window.msRequestAnimationFrame ||
  1105. function(callback){
  1106. window.setTimeout(callback, 1000 / 60);
  1107. };
  1108. })();
  1109. window.cancelRequestAnimFrame = ( function() {
  1110. return window.cancelAnimationFrame ||
  1111. window.webkitCancelRequestAnimationFrame ||
  1112. window.mozCancelRequestAnimationFrame ||
  1113. window.oCancelRequestAnimationFrame ||
  1114. window.msCancelRequestAnimationFrame ||
  1115. clearTimeout
  1116. } )();
  1117. function hexToRgb(hex){
  1118. // By Tim Down - http://stackoverflow.com/a/5624139/3493650
  1119. // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
  1120. var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  1121. hex = hex.replace(shorthandRegex, function(m, r, g, b) {
  1122. return r + r + g + g + b + b;
  1123. });
  1124. var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  1125. return result ? {
  1126. r: parseInt(result[1], 16),
  1127. g: parseInt(result[2], 16),
  1128. b: parseInt(result[3], 16)
  1129. } : null;
  1130. };
  1131. function clamp(number, min, max) {
  1132. return Math.min(Math.max(number, min), max);
  1133. };
  1134. function isInArray(value, array) {
  1135. return array.indexOf(value) > -1;
  1136. }
  1137. /* ---------- particles.js functions - start ------------ */
  1138. window.pJSDom = [];
  1139. window.particlesJS = function(tag_id, params){
  1140. //console.log(params);
  1141. /* no string id? so it's object params, and set the id with default id */
  1142. if(typeof(tag_id) != 'string'){
  1143. params = tag_id;
  1144. tag_id = 'particles-js';
  1145. }
  1146. /* no id? set the id to default id */
  1147. if(!tag_id){
  1148. tag_id = 'particles-js';
  1149. }
  1150. /* pJS elements */
  1151. var pJS_tag = document.getElementById(tag_id),
  1152. pJS_canvas_class = 'particles-js-canvas-el',
  1153. exist_canvas = pJS_tag.getElementsByClassName(pJS_canvas_class);
  1154. /* remove canvas if exists into the pJS target tag */
  1155. if(exist_canvas.length){
  1156. while(exist_canvas.length > 0){
  1157. pJS_tag.removeChild(exist_canvas[0]);
  1158. }
  1159. }
  1160. /* create canvas element */
  1161. var canvas_el = document.createElement('canvas');
  1162. canvas_el.className = pJS_canvas_class;
  1163. /* set size canvas */
  1164. canvas_el.style.width = "100%";
  1165. canvas_el.style.height = "100%";
  1166. /* append canvas */
  1167. var canvas = document.getElementById(tag_id).appendChild(canvas_el);
  1168. /* launch particle.js */
  1169. if(canvas != null){
  1170. pJSDom.push(new pJS(tag_id, params));
  1171. }
  1172. };
  1173. window.particlesJS.load = function(tag_id, path_config_json, callback){
  1174. /* load json config */
  1175. var xhr = new XMLHttpRequest();
  1176. xhr.open('GET', path_config_json);
  1177. xhr.onreadystatechange = function (data) {
  1178. if(xhr.readyState == 4){
  1179. if(xhr.status == 200){
  1180. var params = JSON.parse(data.currentTarget.response);
  1181. window.particlesJS(tag_id, params);
  1182. if(callback) callback();
  1183. }else{
  1184. console.log('Error pJS - XMLHttpRequest status: '+xhr.status);
  1185. console.log('Error pJS - File config not found');
  1186. }
  1187. }
  1188. };
  1189. xhr.send();
  1190. };