or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aggregation.mdbasic-math.mdgeneric-math.mdindex.mdlogical.mdprojection.mdvalue-control.md

projection.mddocs/

0

# Projection Utilities

1

2

Functions for mapping values between different numerical ranges, useful for data visualization, animations, and value transformations. These utilities provide both numeric and generic type-safe projection capabilities.

3

4

## Core Types

5

6

```typescript { .api }

7

/**

8

* Function that projects input from one domain to another

9

* @param input - The input value to project

10

* @param from - Source domain as [min, max] tuple

11

* @param to - Target domain as [min, max] tuple

12

* @returns Projected value in target domain

13

*/

14

type ProjectorFunction<F, T> = (

15

input: F,

16

from: readonly [F, F],

17

to: readonly [T, T]

18

) => T;

19

20

/**

21

* Function type returned by projection creators

22

* @param input - Reactive input value to project

23

* @returns ComputedRef containing projected value

24

*/

25

type UseProjection<F, T> = (input: MaybeRefOrGetter<F>) => ComputedRef<T>;

26

```

27

28

## Capabilities

29

30

### createProjection

31

32

Creates a reusable numeric projection function for mapping values between number ranges with an optional custom projector.

33

34

```typescript { .api }

35

/**

36

* Creates a numeric projection function for mapping between ranges

37

* @param fromDomain - Source range as [min, max] (reactive)

38

* @param toDomain - Target range as [min, max] (reactive)

39

* @param projector - Optional custom projection function

40

* @returns Projection function that can be called with input values

41

*/

42

function createProjection(

43

fromDomain: MaybeRefOrGetter<readonly [number, number]>,

44

toDomain: MaybeRefOrGetter<readonly [number, number]>,

45

projector?: ProjectorFunction<number, number>

46

): UseProjection<number, number>;

47

```

48

49

**Usage Examples:**

50

51

```typescript

52

import { ref } from "vue";

53

import { createProjection } from "@vueuse/math";

54

55

// Create a projection from 0-100 to 0-1

56

const percentToDecimal = createProjection([0, 100], [0, 1]);

57

58

const percentage = ref(75);

59

const decimal = percentToDecimal(percentage);

60

61

console.log(decimal.value); // 0.75

62

63

percentage.value = 50;

64

console.log(decimal.value); // 0.5

65

66

// Create projection with reactive domains

67

const minInput = ref(0);

68

const maxInput = ref(1024);

69

const minOutput = ref(0);

70

const maxOutput = ref(255);

71

72

const inputRange = computed(() => [minInput.value, maxInput.value] as const);

73

const outputRange = computed(() => [minOutput.value, maxOutput.value] as const);

74

75

const scaleDown = createProjection(inputRange, outputRange);

76

77

const highResValue = ref(512);

78

const lowResValue = scaleDown(highResValue);

79

80

console.log(lowResValue.value); // 127.5

81

82

// Change the output range

83

maxOutput.value = 100;

84

console.log(lowResValue.value); // ~50 (automatically recalculated)

85

86

// Temperature conversion (Celsius to Fahrenheit)

87

const celsiusToFahrenheit = createProjection(

88

[0, 100], // Celsius range (water freezing to boiling)

89

[32, 212], // Fahrenheit range

90

);

91

92

const celsius = ref(25);

93

const fahrenheit = celsiusToFahrenheit(celsius);

94

console.log(fahrenheit.value); // 77°F

95

```

96

97

### createGenericProjection

98

99

Creates a generic projection function that can work with any types, not just numbers, using a custom projector function.

100

101

```typescript { .api }

102

/**

103

* Creates a generic typed projection function for any domain types

104

* @param fromDomain - Source range as [min, max] (reactive)

105

* @param toDomain - Target range as [min, max] (reactive)

106

* @param projector - Custom projection function for the specific types

107

* @returns Generic projection function

108

*/

109

function createGenericProjection<F = number, T = number>(

110

fromDomain: MaybeRefOrGetter<readonly [F, F]>,

111

toDomain: MaybeRefOrGetter<readonly [T, T]>,

112

projector: ProjectorFunction<F, T>

113

): UseProjection<F, T>;

114

```

115

116

**Usage Examples:**

117

118

```typescript

119

import { ref } from "vue";

120

import { createGenericProjection } from "@vueuse/math";

121

122

// Color interpolation between RGB values

123

type RGB = { r: number; g: number; b: number };

124

125

const colorProjector = (

126

input: number,

127

from: readonly [number, number],

128

to: readonly [RGB, RGB]

129

): RGB => {

130

const progress = (input - from[0]) / (from[1] - from[0]);

131

const [startColor, endColor] = to;

132

133

return {

134

r: Math.round(startColor.r + (endColor.r - startColor.r) * progress),

135

g: Math.round(startColor.g + (endColor.g - startColor.g) * progress),

136

b: Math.round(startColor.b + (endColor.b - startColor.b) * progress),

137

};

138

};

139

140

const interpolateColor = createGenericProjection(

141

[0, 100],

142

[{ r: 255, g: 0, b: 0 }, { r: 0, g: 255, b: 0 }], // Red to Green

143

colorProjector

144

);

145

146

const progress = ref(50);

147

const currentColor = interpolateColor(progress);

148

149

console.log(currentColor.value); // { r: 128, g: 128, b: 0 } (yellow)

150

151

// String interpolation example

152

const stringProjector = (

153

input: number,

154

from: readonly [number, number],

155

to: readonly [string, string]

156

): string => {

157

const progress = (input - from[0]) / (from[1] - from[0]);

158

const [start, end] = to;

159

const index = Math.round(progress * (end.length - start.length)) + start.length;

160

161

return start + end.slice(start.length, index);

162

};

163

164

const expandString = createGenericProjection(

165

[0, 10],

166

["Hello", "Hello World!"],

167

stringProjector

168

);

169

170

const step = ref(5);

171

const message = expandString(step);

172

console.log(message.value); // "Hello Wor"

173

```

174

175

### useProjection

176

177

Direct numeric projection function that immediately applies projection to a value without creating a reusable projector.

178

179

```typescript { .api }

180

/**

181

* Directly project a value between numeric ranges

182

* @param input - The value to project (reactive)

183

* @param fromDomain - Source range as [min, max] (reactive)

184

* @param toDomain - Target range as [min, max] (reactive)

185

* @param projector - Optional custom projection function

186

* @returns ComputedRef containing the projected value

187

*/

188

function useProjection(

189

input: MaybeRefOrGetter<number>,

190

fromDomain: MaybeRefOrGetter<readonly [number, number]>,

191

toDomain: MaybeRefOrGetter<readonly [number, number]>,

192

projector?: ProjectorFunction<number, number>

193

): ComputedRef<number>;

194

```

195

196

**Usage Examples:**

197

198

```typescript

199

import { ref } from "vue";

200

import { useProjection } from "@vueuse/math";

201

202

// Direct projection usage

203

const temperature = ref(20); // Celsius

204

const fahrenheit = useProjection(

205

temperature,

206

[0, 100], // Celsius range

207

[32, 212] // Fahrenheit range

208

);

209

210

console.log(fahrenheit.value); // 68°F

211

212

// Slider to progress bar

213

const sliderValue = ref(750);

214

const progressPercent = useProjection(

215

sliderValue,

216

[0, 1000], // Slider range

217

[0, 100] // Percentage range

218

);

219

220

console.log(progressPercent.value); // 75%

221

222

// Reactive domains

223

const minTemp = ref(-10);

224

const maxTemp = ref(40);

225

const currentTemp = ref(20);

226

227

const comfort = useProjection(

228

currentTemp,

229

computed(() => [minTemp.value, maxTemp.value]),

230

[0, 100] // Comfort index 0-100

231

);

232

233

console.log(comfort.value); // 60 (comfort index)

234

235

// Custom projector for non-linear scaling

236

const exponentialProjector = (

237

input: number,

238

from: readonly [number, number],

239

to: readonly [number, number]

240

): number => {

241

const normalizedInput = (input - from[0]) / (from[1] - from[0]);

242

const exponential = Math.pow(normalizedInput, 2); // Square for exponential curve

243

return to[0] + (to[1] - to[0]) * exponential;

244

};

245

246

const linearInput = ref(50);

247

const exponentialOutput = useProjection(

248

linearInput,

249

[0, 100],

250

[0, 1000],

251

exponentialProjector

252

);

253

254

console.log(exponentialOutput.value); // 250 (0.5² * 1000)

255

```

256

257

## Advanced Usage Patterns

258

259

### Animation and Easing

260

261

```typescript

262

import { ref, computed } from "vue";

263

import { createProjection, useProjection } from "@vueuse/math";

264

265

// Create easing projection

266

const easeInOut = (

267

input: number,

268

from: readonly [number, number],

269

to: readonly [number, number]

270

): number => {

271

const t = (input - from[0]) / (from[1] - from[0]);

272

const eased = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;

273

return to[0] + (to[1] - to[0]) * eased;

274

};

275

276

const animationProgress = ref(0); // 0 to 1

277

const easedPosition = useProjection(

278

animationProgress,

279

[0, 1],

280

[0, 500], // 500px movement

281

easeInOut

282

);

283

284

// Animate

285

function animate() {

286

animationProgress.value += 0.01;

287

console.log(`Position: ${easedPosition.value}px`);

288

289

if (animationProgress.value < 1) {

290

requestAnimationFrame(animate);

291

}

292

}

293

```

294

295

### Data Visualization Scaling

296

297

```typescript

298

import { ref, computed } from "vue";

299

import { createProjection } from "@vueuse/math";

300

301

// Chart scaling

302

const data = ref([10, 25, 15, 30, 20, 35, 40]);

303

const chartHeight = ref(400);

304

const chartPadding = ref(20);

305

306

// Find data range

307

const dataMin = computed(() => Math.min(...data.value));

308

const dataMax = computed(() => Math.max(...data.value));

309

310

// Create Y-axis projection

311

const dataToPixels = createProjection(

312

computed(() => [dataMin.value, dataMax.value]),

313

computed(() => [chartHeight.value - chartPadding.value, chartPadding.value])

314

);

315

316

// Convert data points to pixel positions

317

const pixelPositions = computed(() =>

318

data.value.map(value => dataToPixels(ref(value)).value)

319

);

320

321

console.log(pixelPositions.value);

322

// [380, 220, 300, 140, 260, 100, 20] (inverted Y for SVG/Canvas)

323

324

// X-axis projection for spacing

325

const indexToPixels = createProjection(

326

[0, data.value.length - 1],

327

[chartPadding.value, 800 - chartPadding.value]

328

);

329

330

const xPositions = computed(() =>

331

data.value.map((_, index) => indexToPixels(ref(index)).value)

332

);

333

```

334

335

### Multi-Range Projections

336

337

```typescript

338

import { ref, computed } from "vue";

339

import { createProjection } from "@vueuse/math";

340

341

// Create different projections for different ranges

342

const input = ref(0);

343

344

// Conditional projection based on input range

345

const complexProjection = computed(() => {

346

const val = input.value;

347

348

if (val <= 50) {

349

// Linear projection for 0-50

350

const linearProj = createProjection([0, 50], [0, 100]);

351

return linearProj(input).value;

352

} else {

353

// Logarithmic projection for 50-100

354

const logProj = createProjection(

355

[50, 100],

356

[100, 1000],

357

(input, from, to) => {

358

const normalized = (input - from[0]) / (from[1] - from[0]);

359

const logValue = Math.log10(1 + normalized * 9); // log10(1 to 10)

360

return to[0] + (to[1] - to[0]) * logValue;

361

}

362

);

363

return logProj(input).value;

364

}

365

});

366

367

// Test different input ranges

368

input.value = 25;

369

console.log(complexProjection.value); // 50 (linear)

370

371

input.value = 75;

372

console.log(complexProjection.value); // ~397 (logarithmic)

373

```