Skip to content

JEasing Chain

Description

A sequence of JEasings can be chained so that when one JEasing finishes, another JEasing begins.

This is useful for creating extended animation sequences.

See the chainJEasings function in the example script that demonstrates a repeating sequence of JEasings that modify an objects transforms.

<>

Example Script

./src/main.ts

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'
import Stats from 'three/addons/libs/stats.module.js'
import JEASINGS from 'jeasings'

const scene = new THREE.Scene()

const gridHelper = new THREE.GridHelper()
gridHelper.position.y = -1
scene.add(gridHelper)

await new RGBELoader().loadAsync('img/venice_sunset_1k.hdr').then((texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping
  scene.environment = texture
})

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100)
camera.position.set(0, 1, 4)

const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 0.8
renderer.shadowMap.enabled = true
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)

window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true
controls.autoRotate = true

let suzanne: THREE.Mesh, plane: THREE.Mesh

new GLTFLoader().load('models/suzanne_scene.glb', (gltf) => {
  suzanne = gltf.scene.getObjectByName('Suzanne') as THREE.Mesh
  suzanne.castShadow = true
  ;((suzanne.material as THREE.MeshStandardMaterial).map as THREE.Texture).colorSpace = THREE.LinearSRGBColorSpace

  plane = gltf.scene.getObjectByName('Plane') as THREE.Mesh
  plane.scale.set(50, 1, 50)
  ;(plane.material as THREE.MeshStandardMaterial).envMap = scene.environment // since three@163, we need to set `envMap` before changing `envMapIntensity` has any effect.
  ;(plane.material as THREE.MeshStandardMaterial).envMapIntensity = 0.05
  plane.receiveShadow = true

  const spotLight = gltf.scene.getObjectByName('Spot') as THREE.SpotLight
  spotLight.intensity /= 500
  spotLight.castShadow = true
  spotLight.target = suzanne

  scene.add(gltf.scene)

  chainJEasings()
})

function chainJEasings() {
  // Demonstrating a repeating sequence of JEasings.
  const changePositionZ = new JEASINGS.JEasing(suzanne.position).to({ z: -2 }, 2000) // 2 seconds
  const rotateY = new JEASINGS.JEasing(suzanne.rotation).to({ y: Math.PI * 2 }, 2000)
  const scaleXZ = new JEASINGS.JEasing(suzanne.scale).to({ x: 2, z: 0.5 }, 2000)
  const rotateZ = new JEASINGS.JEasing(suzanne.rotation).to({ z: Math.PI * 2 }, 2000)
  const resetScaleXZ = new JEASINGS.JEasing(suzanne.scale).to({ x: 1, z: 1 }, 2000)
  const resetPositionZ = new JEASINGS.JEasing(suzanne.position).to({ z: 0 }, 2000)
  const rotateX = new JEASINGS.JEasing(suzanne.rotation).to({ x: Math.PI * 2 }, 2000)
  const resetRotations = new JEASINGS.JEasing(suzanne.rotation).to({ x: 0, y: 0, z: 0 }, 0) // 0 seconds results in an instant JEasing

  changePositionZ.chain(rotateY)
  rotateY.chain(scaleXZ)
  scaleXZ.chain(rotateZ)
  rotateZ.chain(resetScaleXZ)
  resetScaleXZ.chain(resetPositionZ)
  resetPositionZ.chain(rotateX)
  rotateX.chain(resetRotations)
  resetRotations.chain(changePositionZ) // begin the loop again

  changePositionZ.start()
}

const stats = new Stats()
document.body.appendChild(stats.dom)

function animate() {
  requestAnimationFrame(animate)

  controls.update()

  JEASINGS.update()

  render()

  stats.update()
}

function render() {
  renderer.render(scene, camera)
}

animate()

JEASINGS (GitHub)

Comments