# Plugin: faceClick

  • Activate a face morph to click
  • Customize which face morph to use
  • Tags: ['browser']

Models: Jeeliz Weboji

Activate: handsfree.plugin.faceClick.enable()

Tags: ['browser']

About: This plugin is used in combination with the facePointer to help you click on things on the screen with a face gesture.

You can customize which handsfree.data.weboji.morphs[] to use and how activated that morph needs to be to register the click. The click triggers a MouseEvent on the element underneath the pointer with the following options:

new MouseEvent('click', {
  bubbles: true,
  cancelable: true,
  clientX: data.pointer.x,
  clientY: data.pointer.y
})

# Config

# During instantiation

const handsfree = new Handsfree({
  weboji: true,

  plugin: {
    faceClick: {
      // Morphs to watch for and their required confidences
      morphs: {
        // Smile right when it's at least 25% actitvated
        0: 0.25,
        // Smile left when it's at least 25% actitvated
        1: 0.25
      }
    }
  }
})

# After instantiation

// Let's only use the right smile
delete handsfree.plugin.faceClick.config.moprhs[1]
// Let's require it to be 50% activated
handsfree.plugin.faceClick.config.moprhs[0] = .5

# Full plugin code

/**
 * Click on things with a gesture
 */
export default {
  models: 'weboji',
  enabled: false,

  tags: ['browser'],

  config: {
    // How often in milliseconds to trigger clicks
    throttle: 50,

    // Max number of frames to keep down
    maxMouseDownedFrames: 1,

    // Morphs to watch for and their required confidences
    morphs: {
      0: 0.25,
      1: 0.25
    }
  },

  // Number of frames mouse has been downed
  mouseDowned: 0,
  // Is the mouse up?
  mouseUp: false,
  // Whether one of the morph confidences have been met
  thresholdMet: false,

  // The last held {x, y}, used to calculate move delta
  lastHeld: {x: 0, y: 0},
  // Original target under mousedown
  $origTarget: null,

  /**
   * Detect click state and trigger a real click event
   */
  onFrame ({weboji}) {
    // Detect if the threshold for clicking is met with specific morphs
    this.thresholdMet = false
    let event = ''
    Object.keys(this.config.morphs).forEach((key) => {
      const morph = +this.config.morphs[key]
      if (morph > 0 && weboji.morphs[key] >= morph) this.thresholdMet = true
    })

    // Click/release and add body classes
    if (this.thresholdMet) {
      this.mouseDowned++
      document.body.classList.add('handsfree-clicked')
    } else {
      this.mouseUp = this.mouseDowned
      this.mouseDowned = 0
      document.body.classList.remove('handsfree-clicked')
    }

    // Set the state
    if (
      this.mouseDowned > 0 &&
      this.mouseDowned <= this.config.maxMouseDownedFrames
    )
      event = weboji.pointer.state = 'mousedown'
    else if (this.mouseDowned > this.config.maxMouseDownedFrames)
      event = weboji.pointer.state = 'mousedrag'
    else if (this.mouseUp)
      event = weboji.pointer.state = 'mouseup'
    else
      event = 'mousemove'

    // Actually click something (or focus it)
    const $el = document.elementFromPoint(weboji.pointer.x, weboji.pointer.y)
    if ($el && event === 'mousedown') {
      this.$origTarget = $el
    }

    if ($el) {
      const eventOpts = {
        view: window,
        button: 0,
        bubbles: true,
        cancelable: true,
        clientX: weboji.pointer.x,
        clientY: weboji.pointer.y,
        // Only used when the mouse is captured in full screen mode
        movementX: weboji.pointer.x - this.lastHeld.x,
        movementY: weboji.pointer.y - this.lastHeld.y
      }
      
      $el.dispatchEvent(new MouseEvent(event, eventOpts))

      // Focus
      if (weboji.pointer.state === 'mousedown' && ['INPUT', 'TEXTAREA', 'BUTTON', 'A'].includes($el.nodeName))
        $el.focus()

      // Click
      if (weboji.pointer.state === 'mouseup' && $el === this.$origTarget) {
        $el.dispatchEvent(new MouseEvent('click', eventOpts))
      }

      weboji.pointer.$target = $el
    }

    this.lastHeld = weboji.pointer
  }
}
Debugger