Fairy Example

This example puts balls around the outside of a component by translating the screen position to a 3D world position.

import { Fx } from 'react-neon/dist/index.babel.js'
import * as THREE from 'three'

//input THREE.Vector3
function screenToWorld(position, cam) {
  var z = position.z
  position.z = 0
  position.unproject(cam)
  return cam.position.clone().add(
    position
      .sub(cam.position)
      .normalize()
      .multiplyScalar(-(cam.position.z - z) / position.z)
  )
}

/**
 *
 * Basic particles radiating from the user's mouse, with more when the user clicks.
 *
 **/
export default class Lantern extends Fx {
  context = 'webgl'

  init() {
    this.fairylights = []

    this.addFairyLight.bind(this)
    this.initGeometry()
    this.initColors()

    this.scene = new THREE.Scene()
    this.camera = new THREE.PerspectiveCamera(
      45,
      this.bb.width / this.bb.height,
      0.1,
      1000
    )
    this.camera.position.z = 0

    let box_geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5)
    let box_material = new THREE.MeshBasicMaterial({ color: 0xffffff })
    this.cube = new THREE.Mesh(box_geometry, box_material)
    this.cube.position.set(5, 5, 0)
    this.scene.add(this.cube)

    let light = new THREE.PointLight(0xffffff, 1, 100)
    light.position.set(5, 5, 0)
    this.scene.add(light)

    const w = this.bb.width
    const h = this.bb.height
    const p = this.options.padding

    for (let x = 0; x < 8; x++) {
      let px = ((p + (x % 8) * ((w - p * 2) / 7)) / w) * 2 - 1
      let py = ((p + 0 * ((h - p * 2) / 7)) / h) * 2 - 1
      this.addFairyLight(x, px, py)
    }

    for (let x = 8; x < 16; x++) {
      let px = ((p + (x % 8) * ((w - p * 2) / 7)) / w) * 2 - 1
      let py = ((p + 7 * ((h - p * 2) / 7)) / h) * 2 - 1
      this.addFairyLight(x, px, py)
    }

    for (let x = 16; x < 22; x++) {
      let px = ((p + 0 * ((w - p * 2) / 7)) / w) * 2 - 1
      let py = ((p + ((x + 1) % 8) * ((h - p * 2) / 7)) / h) * 2 - 1
      this.addFairyLight(x, px, py)
    }

    for (let x = 22; x < 28; x++) {
      let px = ((p + 7 * ((w - p * 2) / 7)) / w) * 2 - 1
      let py = ((p + ((x + 3) % 8) * ((h - p * 2) / 7)) / h) * 2 - 1
      this.addFairyLight(x, px, py)
    }

    this.renderer = new THREE.WebGLRenderer({
      canvas: this.ctx.canvas,
      antialias: true
    })
    this.renderer.setPixelRatio(window.devicePixelRatio)
    this.renderer.setSize(this.bb.width, this.bb.height)
  }

  initGeometry() {
    this.sphere_geometry = new THREE.SphereGeometry(0.5, 12, 12)
  }

  initColors() {
    this.sphere_colors = [
      new THREE.MeshStandardMaterial({
        color: 0xffff00,
        flatShading: true
      }),
      new THREE.MeshStandardMaterial({
        color: 0xff00ff,
        flatShading: true
      }),
      new THREE.MeshStandardMaterial({
        color: 0x00ffff,
        flatShading: true
      }),
      new THREE.MeshStandardMaterial({
        color: 0xff0000,
        flatShading: true
      }),
      new THREE.MeshStandardMaterial({
        color: 0x00ff00,
        flatShading: true
      }),
      new THREE.MeshStandardMaterial({
        color: 0x0000ff,
        flatShading: true
      })
    ]
  }

  addFairyLight(x, px, py) {
    this.fairylights.push(
      new THREE.Mesh(
        this.sphere_geometry,
        this.sphere_colors[x % this.sphere_colors.length]
      )
    )

    let pos = screenToWorld(new THREE.Vector3(px, py, -15), this.camera)

    this.fairylights[x].position.set(pos.x, pos.y, pos.z)
    this.scene.add(this.fairylights[x])
  }

  draw() {
    this.renderer.render(this.scene, this.camera)

    this.fairylights.forEach(f => {
      f.rotation.x += 0.01
      f.rotation.z += 0.01
    })

    this.raf = requestAnimationFrame(this.draw)
  }
}