Fourmis.js

À propos

Développée dans le cadre du projet Déborder Bolloré, fourmis.js est une bibliothèque JavaScript permettant l’insertion de fourmis sur une page web.

Les fourmis peuvent prendre différents comportements définis dans src/behaviors/, l’implémentation de l’interaction utilisateur est laissée à la discrétion du·de la développeur·euse.

Pour plus d’informations, consulter le dépôt sur GitLab.

Fonctionnement

Les fourmis sont ajoutées au DOM en tant que symboles SVG.
Les coordonnées sont exprimées dans le référentiel absolu de leur conteneur, c’est-à-dire qu’une fourmi positionnée en (0; 0) sera placée dans le coin haut-gauche de son conteneur.

À l’insertion d’une nouvelle fourmi aux coordonnées (x; y), la bibliothèque vérifie la place disponible au sein d’un éventuel cluster de fourmis déjà présentes aux alentours, et modifie la position d’insertion le cas échéant. Ensuite, une fonction de comportement définie par la propriété behavior défini l’angle de la fourmi en fonction de ses coordonnées d’insertion définitives.

Une fois instanciée, la bibliothèque propose plusieurs méthodes permettant l’ajout et la suppression des fourmis.

API

L’intégralité du code source est documenté au format JSDoc.
Voir src/index.js pour commencer.

Exemples

#1 : Ajout simple de fourmis
cliquer pour ajouter une fourmi

        import { Fourmis } from '@deborderbollore/fourmis.js'

        const container = document.querySelector('#example-1 .viewport')

        const fourmis = new Fourmis({ container })

        container.addEventListener('click', e => {
          const x = e.clientX + document.documentElement.scrollLeft - container.offsetLeft
          const y = e.clientY + document.documentElement.scrollTop - container.offsetTop
          fourmis.push([x, y])
        })
      
#2 : Orientation vers un élément HTML

        import { Fourmis, seek } from '@deborderbollore/fourmis.js'

        const container = document.querySelector('#example-2 .viewport')

        const fourmis = new Fourmis({
          container,
          behavior: seek(container.querySelector('.target'), {
            attractionRadius: container.clientWidth * 0.5
          })
        })

        container.addEventListener('click', e => {
          const x = e.clientX + document.documentElement.scrollLeft - container.offsetLeft
          const y = e.clientY + document.documentElement.scrollTop - container.offsetTop
          fourmis.push([x, y])
        })
      
#3 : Suivi d’un contour SVG

        import { Fourmis, sneakAroundPaths } from '@deborderbollore/fourmis.js'

        const container = document.querySelector('#example-3 .viewport')

        const fourmis = new Fourmis({
          container,
          behavior: sneakAroundPaths(container.querySelectorAll('svg path'))
        })

        container.addEventListener('mousemove', e => {
          if (e.buttons !== 1) return
          window.requestAnimationFrame(() => {
            const x = e.clientX + document.documentElement.scrollLeft - container.offsetLeft
            const y = e.clientY + document.documentElement.scrollTop - container.offsetTop
            fourmis.push([x, y])
          })
        })
      
#4 : Composition de plusieurs comportements

        import { Fourmis, compose, sneakAroundPaths, seek, wander } from '../src'

        const container = document.querySelector('#example-4 .viewport')

        const fourmis = new Fourmis({
          container,

          // Compose multiple behaviors
          behavior: compose([
            // Wander on the viewport
            wander(),

            // Seek center of SVG
            seek(container.querySelector('svg'), {
              maxDistance: container.clientHeight * 0.75
            }),

            // Sneak around SVG path elements, but with a weight twice as contrasted as the other behaviors
            compose.weight(
              sneakAroundPaths(container.querySelectorAll('svg path'), { maxDistance: 50 }),
              w => w + w * w
            )
          ])
        })

        container.addEventListener('mousemove', e => {
          if (e.buttons !== 1) return
          window.requestAnimationFrame(() => {
            const x = e.clientX + document.documentElement.scrollLeft - container.offsetLeft
            const y = e.clientY + document.documentElement.scrollTop - container.offsetTop
            fourmis.push([x, y])
          })
        })
      
#5 : Comportement personnalisé

        import { Fourmis } from '../src'

        const container = document.querySelector('#example-5 .viewport')
        const fourmis = new Fourmis({
          container,
          behavior: followPrevious
        })

        const distSq = (a, b) => (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])
        const getPoint = e => ([
          e.clientX + document.documentElement.scrollLeft - container.offsetLeft,
          e.clientY + document.documentElement.scrollTop - container.offsetTop
        ])

        let lastPoint

        container.addEventListener('mousedown', e => { lastPoint = getPoint(e) })
        container.addEventListener('mousemove', e => {
          if (e.buttons !== 1) return
          window.requestAnimationFrame(() => {
            const point = getPoint(e)
            if (distSq(point, lastPoint) > 20 ** 2) {
              const agent = fourmis.push(point)
              if (agent) lastPoint = agent.point
            }
          })
        })

        function followPrevious (instance) {
          const angleBetween = ([x1, y1], [x2, y2]) => Math.atan2(y2 - y1, x2 - x1)

          return point => ({
            point,
            theta: angleBetween(lastPoint, point)
          })
        }
      

Crédits

Fourmi dessinée par Alaric Garnier.