CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vueuse--math

Reactive mathematical utility functions and composables for Vue.js applications

Pending
Overview
Eval results
Files

value-control.mddocs/

Value Control Functions

Utilities for clamping values within bounds and controlling numerical precision with configurable rounding methods. These functions provide fine-grained control over numeric values with reactive behavior.

Capabilities

useClamp

Reactively clamps a value between minimum and maximum bounds. Returns different ref types based on the input type to optimize for readonly vs writable scenarios.

/**
 * Clamp a readonly value between min and max bounds
 * @param value - Readonly reactive number or getter
 * @param min - Minimum bound (reactive)
 * @param max - Maximum bound (reactive)
 * @returns ComputedRef with clamped value
 */
function useClamp(
  value: ReadonlyRefOrGetter<number>,
  min: MaybeRefOrGetter<number>,
  max: MaybeRefOrGetter<number>
): ComputedRef<number>;

/**
 * Clamp a writable value between min and max bounds
 * @param value - Writable reactive number
 * @param min - Minimum bound (reactive)
 * @param max - Maximum bound (reactive)
 * @returns Writable Ref that clamps on both get and set
 */
function useClamp(
  value: MaybeRefOrGetter<number>,
  min: MaybeRefOrGetter<number>,
  max: MaybeRefOrGetter<number>
): Ref<number>;

Usage Examples:

import { ref } from "vue";
import { useClamp } from "@vueuse/math";

// Basic clamping with writable ref
const value = ref(15);
const clamped = useClamp(value, 0, 10);

console.log(clamped.value); // 10 (clamped from 15)

// Setting the clamped value also clamps
clamped.value = 20;
console.log(clamped.value); // 10 (clamped)
console.log(value.value);   // 10 (original ref is updated)

clamped.value = 5;
console.log(clamped.value); // 5 (within bounds)

// Reactive bounds
const minBound = ref(0);
const maxBound = ref(100);
const input = ref(150);
const boundedValue = useClamp(input, minBound, maxBound);

console.log(boundedValue.value); // 100

// Changing bounds affects the result
maxBound.value = 200;
console.log(boundedValue.value); // 150 (now within new bounds)

// Readonly usage (computed or getter)
const readonlyValue = useClamp(() => Math.random() * 200, 50, 100);
console.log(readonlyValue.value); // Between 50-100

usePrecision

Reactively sets the precision of a number using configurable rounding methods.

/**
 * Set the precision of a number with configurable rounding
 * @param value - The number to set precision for
 * @param digits - Number of decimal digits to preserve
 * @param options - Optional configuration for rounding method
 * @returns ComputedRef containing the precision-controlled number
 */
function usePrecision(
  value: MaybeRefOrGetter<number>,
  digits: MaybeRefOrGetter<number>,
  options?: MaybeRefOrGetter<UsePrecisionOptions>
): ComputedRef<number>;

/**
 * Configuration options for precision control
 */
interface UsePrecisionOptions {
  /**
   * Method to use for rounding
   * @default 'round'
   */
  math?: 'floor' | 'ceil' | 'round';
}

Usage Examples:

import { ref } from "vue";
import { usePrecision } from "@vueuse/math";

// Basic precision control
const value = ref(3.14159265);
const precise = usePrecision(value, 2);

console.log(precise.value); // 3.14

// Different rounding methods
const number = ref(3.7869);

const rounded = usePrecision(number, 2, { math: 'round' });
const floored = usePrecision(number, 2, { math: 'floor' });
const ceiled = usePrecision(number, 2, { math: 'ceil' });

console.log(rounded.value); // 3.79 (default rounding)
console.log(floored.value); // 3.78 (always round down)
console.log(ceiled.value);  // 3.79 (always round up)

// Reactive precision
const price = ref(19.99567);
const decimalPlaces = ref(2);
const formattedPrice = usePrecision(price, decimalPlaces);

console.log(formattedPrice.value); // 20.00

decimalPlaces.value = 3;
console.log(formattedPrice.value); // 19.996

// Different rounding methods with negative numbers
const negative = ref(-2.456);
const roundedNeg = usePrecision(negative, 1, { math: 'round' });
const flooredNeg = usePrecision(negative, 1, { math: 'floor' });
const ceiledNeg = usePrecision(negative, 1, { math: 'ceil' });

console.log(roundedNeg.value); // -2.5
console.log(flooredNeg.value); // -2.5 (floor toward negative infinity)
console.log(ceiledNeg.value);  // -2.4 (ceil toward positive infinity)

Advanced Usage Patterns

Dynamic Bounds and Precision

import { ref, computed } from "vue";
import { useClamp, usePrecision } from "@vueuse/math";

// Create a controlled input with dynamic bounds and precision
const rawInput = ref(0);
const minValue = ref(0);
const maxValue = ref(100);
const precision = ref(1);

// Clamp first, then apply precision
const clampedInput = useClamp(rawInput, minValue, maxValue);
const preciseInput = usePrecision(clampedInput, precision);

// UI control that updates all values
const slider = computed({
  get: () => preciseInput.value,
  set: (val: number) => {
    rawInput.value = val;
  }
});

// Example usage
rawInput.value = 123.456789;
console.log(slider.value); // 100.0 (clamped to max, then precision applied)

maxValue.value = 150;
console.log(slider.value); // 123.5 (now within bounds, precision applied)

Financial Calculations

import { ref, computed } from "vue";
import { useClamp, usePrecision } from "@vueuse/math";

// Financial calculation with proper precision
const principal = ref(10000);
const interestRate = ref(0.05); // 5%
const years = ref(10);

// Ensure reasonable bounds for financial inputs
const clampedPrincipal = useClamp(principal, 0, 1000000);
const clampedRate = useClamp(interestRate, 0, 1); // 0-100%
const clampedYears = useClamp(years, 1, 50);

// Calculate compound interest
const futureValue = computed(() => {
  const p = clampedPrincipal.value;
  const r = clampedRate.value;
  const t = clampedYears.value;
  return p * Math.pow(1 + r, t);
});

// Format for display with proper precision
const formattedFutureValue = usePrecision(futureValue, 2);

console.log(formattedFutureValue.value); // 16288.95

// Interest calculation stays within bounds even with extreme inputs
principal.value = -5000; // Invalid negative
interestRate.value = 2.5; // Invalid > 100%
console.log(formattedFutureValue.value); // Still gives valid result due to clamping

Animation Value Control

import { ref, computed } from "vue";
import { useClamp, usePrecision } from "@vueuse/math";

// Animation progress control
const animationProgress = ref(0);
const duration = ref(1000); // ms
const currentTime = ref(0);

// Calculate normalized progress (0-1)
const rawProgress = computed(() => currentTime.value / duration.value);
const clampedProgress = useClamp(rawProgress, 0, 1);
const preciseProgress = usePrecision(clampedProgress, 3);

// Convert to percentage for display
const percentProgress = computed(() => preciseProgress.value * 100);
const displayPercent = usePrecision(percentProgress, 1);

// Simulate animation frames
function animate() {
  currentTime.value += 16; // ~60fps
  
  console.log(`Progress: ${displayPercent.value}%`);
  
  if (preciseProgress.value < 1) {
    requestAnimationFrame(animate);
  }
}

animate();

Install with Tessl CLI

npx tessl i tessl/npm-vueuse--math

docs

aggregation.md

basic-math.md

generic-math.md

index.md

logical.md

projection.md

value-control.md

tile.json