Vec

·view .md ↗·chatgpt ↗·claude ↗

Code

export class Vec {
    x: number
    y: number
    z: number

    constructor(x: number = 0, y: number = 0, z: number = 0) {
        this.x = x
        this.y = y
        this.z = z
    }

    copy(v: Vec): Vec {
        this.x = v.x
        this.y = v.y
        this.z = v.z
        return this
    }

    set(x: number, y: number, z: number = 0): Vec {
        this.x = x
        this.y = y
        this.z = z

        return this
    }

    clone(): Vec {
        return new Vec(this.x, this.y, this.z)
    }

    add(v: Vec): Vec {
        this.x += v.x
        this.y += v.y
        this.z += v.z

        return this
    }

    sub(v: Vec): Vec {
        this.x -= v.x
        this.y -= v.y
        this.z -= v.z

        return this
    }

    mul(v: Vec): Vec {
        this.x *= v.x
        this.y *= v.y
        this.z *= v.z

        return this
    }

    div(v: Vec): Vec {
        this.x /= v.x
        this.y /= v.y
        this.z /= v.z

        return this
    }

    scale(scl: number): Vec {
        this.x *= scl
        this.y *= scl
        this.z *= scl

        return this
    }

    length(): number {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z)
    }

    lerp(target: Vec, alpha: number): Vec {
        this.x = MathUtils.lerp(this.x, target.x, alpha)
        this.y = MathUtils.lerp(this.y, target.y, alpha)
        this.z = MathUtils.lerp(this.z, target.z, alpha)

        return this
    }

    equals(v: Vec): boolean {
        return this.x === v.x && this.y === v.y && this.z === v.z
    }

    dot(v: Vec): number {
        return this.x * v.x + this.y * v.y + this.z * v.z
    }

    distanceTo(v: Vec): Vec {
        return this.clone().sub(v).length()
    }

    normalize(): Vec {
        const L = this.length()
        if (L === 0) return this

        this.x /= L
        this.y /= L
        this.z /= L

        return this
    }
}

Properties

PropertyDescription
xHorizontal component of the vector.
yVertical component of the vector.
zDepth component (optional).

Main Methods

MethodDescription
copy(v)Copies the values of another vector.
set(x, y, z)Quickly assigns new values.
clone()Returns a copy of the current vector.
add(v) / sub(v)Adds or subtracts another vector.
mul(v) / div(v)Multiplies or divides component by component.
scale(s)Scales the vector by a scalar.
length()Returns the magnitude of the vector.
lerp(target, alpha)Interpolates between the current vector and the target.
equals(v)Checks whether both vectors are identical.
dot(v)Computes the dot product.
distanceTo(v)Distance between two vectors.
normalize()Normalizes the vector so its length is 1.

Nuxt Example

<script setup lang="ts">
// You can place the class in ~/utils/math/vec.ts and import it here.
import { Vec } from '~/utils/math/vec'

const pointer = ref(new Vec())
const target = new Vec()
let frame: number | null = null

const animate = () => {
  pointer.value.lerp(target, 0.15)
  frame = requestAnimationFrame(animate)
}

const handlePointer = (event: PointerEvent) => {
  target.set(event.clientX, event.clientY)
}

const styleCursor = computed(() => ({
  transform: `translate3d(${pointer.value.x}px, ${pointer.value.y}px, 0)`
}))

onMounted(() => {
  window.addEventListener('pointermove', handlePointer)
  frame = requestAnimationFrame(animate)
})

onUnmounted(() => {
  window.removeEventListener('pointermove', handlePointer)
  if (frame) cancelAnimationFrame(frame)
})
</script>

<template>
  <div class="cursor" :style="styleCursor">Vec</div>
</template>

<style scoped>
.cursor {
  position: fixed;
  inset: 0;
  width: 60px;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  background: rgba(37, 99, 235, 0.2);
  border: 1px solid #2563eb;
  border-radius: 999px;
  pointer-events: none;
  font-weight: 600;
}
</style>