Vec

·ver .md ↗·chatgpt ↗·claude ↗

Código

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): number {
        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
    }
}

Propiedades

PropiedadDescripción
xComponente horizontal del vector.
yComponente vertical del vector.
zComponente de profundidad (opcional).

Métodos principales

MétodoDescripción
copy(v)Copia los valores de otro vector.
set(x, y, z)Asigna nuevos valores rápidamente.
clone()Devuelve una copia del vector actual.
add(v) / sub(v)Suma o resta otro vector.
mul(v) / div(v)Multiplica o divide componente a componente.
scale(s)Escala el vector por un escalar.
length()Regresa la magnitud del vector.
lerp(target, alpha)Interpola entre el vector actual y el objetivo.
equals(v)Compara si ambos vectores son idénticos.
dot(v)Calcula el producto punto.
distanceTo(v)Distancia entre dos vectores.
normalize()Normaliza el vector para que su longitud sea 1.

Ejemplo en Nuxt

<script setup lang="ts">
// Puedes colocar la clase en ~/utils/math/vec.ts e importarla aquí.
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>