Skip to content

Commit d5399e0

Browse files
committed
Basic rgb hex interpolation. Fixes #3
1 parent 974ad2e commit d5399e0

File tree

7 files changed

+177
-31
lines changed

7 files changed

+177
-31
lines changed

src/__tests__/__snapshots__/index.test.js.snap

+58
Original file line numberDiff line numberDiff line change
@@ -189,33 +189,43 @@ Object {
189189
exports[`css-spring matches snapshots 2`] = `
190190
Object {
191191
"0%": Object {
192+
"background": "#bbd653",
192193
"padding-left": "-48px",
193194
},
194195
"1%": Object {
196+
"background": "#becf51",
195197
"padding-left": "-45px",
196198
},
197199
"10%": Object {
200+
"background": "#e9461b",
198201
"padding-left": "18px",
199202
},
200203
"100%": Object {
204+
"background": "#ff0000",
201205
"padding-left": "50px",
202206
},
203207
"11%": Object {
208+
"background": "#ee3615",
204209
"padding-left": "25px",
205210
},
206211
"12%": Object {
212+
"background": "#f3270f",
207213
"padding-left": "32px",
208214
},
209215
"13%": Object {
216+
"background": "#f71a0a",
210217
"padding-left": "38px",
211218
},
212219
"14%": Object {
220+
"background": "#fb0d05",
213221
"padding-left": "44px",
214222
},
215223
"15%": Object {
224+
"background": "#fe0201",
216225
"padding-left": "49px",
217226
},
218227
"16%": Object {
228+
"background": "#ff0000",
219229
"padding-left": "54px",
220230
},
221231
"17%": Object {
@@ -228,6 +238,7 @@ Object {
228238
"padding-left": "64px",
229239
},
230240
"2%": Object {
241+
"background": "#c1c54d",
231242
"padding-left": "-40px",
232243
},
233244
"20%": Object {
@@ -261,6 +272,7 @@ Object {
261272
"padding-left": "66px",
262273
},
263274
"3%": Object {
275+
"background": "#c5b848",
264276
"padding-left": "-34px",
265277
},
266278
"30%": Object {
@@ -294,72 +306,117 @@ Object {
294306
"padding-left": "52px",
295307
},
296308
"4%": Object {
309+
"background": "#c9aa42",
297310
"padding-left": "-28px",
298311
},
299312
"40%": Object {
313+
"background": "#ff0000",
300314
"padding-left": "51px",
301315
},
302316
"41%": Object {
317+
"background": "#ff0100",
303318
"padding-left": "50px",
304319
},
305320
"42%": Object {
321+
"background": "#fe0201",
306322
"padding-left": "49px",
307323
},
308324
"43%": Object {
325+
"background": "#fe0402",
309326
"padding-left": "48px",
310327
},
311328
"44%": Object {
329+
"background": "#fd0502",
312330
"padding-left": "48px",
313331
},
314332
"45%": Object {
333+
"background": "#fd0602",
315334
"padding-left": "47px",
316335
},
317336
"46%": Object {
337+
"background": "#fd0703",
318338
"padding-left": "47px",
319339
},
320340
"47%": Object {
341+
"background": "#fd0803",
321342
"padding-left": "46px",
322343
},
344+
"48%": Object {
345+
"background": "#fc0803",
346+
},
323347
"5%": Object {
348+
"background": "#ce9a3c",
324349
"padding-left": "-21px",
325350
},
326351
"52%": Object {
352+
"background": "#fc0803",
327353
"padding-left": "46px",
328354
},
329355
"53%": Object {
356+
"background": "#fd0803",
330357
"padding-left": "47px",
331358
},
359+
"54%": Object {
360+
"background": "#fd0703",
361+
},
362+
"55%": Object {
363+
"background": "#fd0703",
364+
},
332365
"56%": Object {
366+
"background": "#fd0602",
333367
"padding-left": "47px",
334368
},
335369
"57%": Object {
370+
"background": "#fd0502",
336371
"padding-left": "48px",
337372
},
373+
"58%": Object {
374+
"background": "#fe0502",
375+
},
376+
"59%": Object {
377+
"background": "#fe0402",
378+
},
338379
"6%": Object {
380+
"background": "#d38a36",
339381
"padding-left": "-13px",
340382
},
341383
"60%": Object {
384+
"background": "#fe0301",
342385
"padding-left": "48px",
343386
},
344387
"61%": Object {
388+
"background": "#fe0301",
345389
"padding-left": "49px",
346390
},
391+
"62%": Object {
392+
"background": "#fe0201",
393+
},
347394
"63%": Object {
395+
"background": "#ff0201",
348396
"padding-left": "49px",
349397
},
350398
"64%": Object {
399+
"background": "#ff0100",
351400
"padding-left": "50px",
352401
},
402+
"65%": Object {
403+
"background": "#ff0100",
404+
},
405+
"66%": Object {
406+
"background": "#ff0000",
407+
},
353408
"69%": Object {
354409
"padding-left": "50px",
355410
},
356411
"7%": Object {
412+
"background": "#d9792f",
357413
"padding-left": "-5px",
358414
},
359415
"70%": Object {
360416
"padding-left": "51px",
361417
},
362418
"8%": Object {
419+
"background": "#de6828",
363420
"padding-left": "3px",
364421
},
365422
"82%": Object {
@@ -369,6 +426,7 @@ Object {
369426
"padding-left": "50px",
370427
},
371428
"9%": Object {
429+
"background": "#e45722",
372430
"padding-left": "10px",
373431
},
374432
}

src/__tests__/index.test.js

+2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ describe('css-spring', () => {
1515
spring(
1616
{
1717
'padding-left': '-50px',
18+
background: '#bada55',
1819
},
1920
{
2021
'padding-left': '50px',
22+
background: '#f00',
2123
},
2224
{
2325
preset: 'wobbly',

src/__tests__/parse.test.js

+39-13
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,47 @@
11
import {
22
combine,
3+
parseHexColor,
34
parseStyles,
45
parseValues,
56
split,
67
} from '../parse'
78

89
describe('parse', () => {
910
describe('combine', () => {
10-
test('returns value for unknown properties', () => {
11+
test('returns value for unknown props', () => {
1112
expect(combine('foo', 'bar')).toEqual('bar')
1213
})
1314
test('returns value for non-array values', () => {
1415
expect(combine('border', 'bar')).toEqual('bar')
1516
})
1617
test('returns combined value for array values', () => {
17-
expect(combine('border', [ '1px', 'solid', '#f00' ])).toEqual('1px solid #f00')
18+
expect(combine('border', [ '1px', 'solid', '#f00' ]))
19+
.toEqual('1px solid #f00')
1820
})
1921
})
2022

2123
describe('parseStyles', () => {
22-
test('only parses properties that exist in both start and end styles', () => {
24+
test('only parses props that exist in both start and end styles', () => {
2325
expect(
2426
parseStyles({ margin: '0 0 10px 10px' }, { padding: '10px 10px 0 0' })
2527
).toEqual([])
2628
})
27-
test('only parses properties that have the same number of values', () => {
29+
test('only parses props that have the same number of values', () => {
2830
expect(
2931
parseStyles({ margin: '0 0 10px 10px' }, { margin: '10px 0 0' })
3032
).toEqual([])
3133
})
32-
test('only parses properties where every start/end combination can be parsed', () => {
34+
test('fails parse on equal number of props but different type', () => {
3335
expect(
3436
parseStyles({ margin: '0 0 10px 10px' }, { margin: '10px foo 0 0' })
3537
).toEqual([])
3638
})
3739
test('returns parsed information object', () => {
3840
expect(
39-
parseStyles({ border: '0 solid #f00', opacity: 0 }, { border: '10px solid #f00', opacity: 1 })
41+
parseStyles(
42+
{ border: '0 solid #f00', opacity: 0 },
43+
{ border: '10px solid #f00', opacity: 1 }
44+
)
4045
).toEqual([
4146
{ prop: 'border', start: 0, end: 10, unit: 'px' },
4247
{ prop: 'border', fixed: 'solid' },
@@ -50,27 +55,48 @@ describe('parse', () => {
5055
test('returns fixed when both values are equal', () => {
5156
expect(parseValues('solid', 'solid')).toEqual({ fixed: 'solid' })
5257
})
53-
test('returns undefined when one of the values is not numeric', () => {
58+
test('returns rgb colors when both values are colors', () => {
59+
expect(parseValues('#f00', '#00f'))
60+
.toEqual({ rgb: [[ 255, 0, 0 ], [ 0, 0, 255 ]]})
61+
})
62+
test('returns undefined otherwise', () => {
5463
expect(parseValues('solid', '1px')).toEqual(undefined)
5564
expect(parseValues(0, '#f00')).toEqual(undefined)
5665
})
5766
test('returns start, end and unit otherwise', () => {
58-
expect(parseValues('1px', '10px')).toEqual({ start: 1, end: 10, unit: 'px' })
59-
expect(parseValues(0, 1)).toEqual({ start: 0, end: 1, unit: '' })
60-
expect(parseValues(0, '10rem')).toEqual({ start: 0, end: 10, unit: 'rem' })
61-
expect(parseValues('0px', 1)).toEqual({ start: 0, end: 1, unit: 'px' })
67+
expect(parseValues('1px', '10px'))
68+
.toEqual({ start: 1, end: 10, unit: 'px' })
69+
expect(parseValues(0, 1))
70+
.toEqual({ start: 0, end: 1, unit: '' })
71+
expect(parseValues(0, '10rem'))
72+
.toEqual({ start: 0, end: 10, unit: 'rem' })
73+
expect(parseValues('0px', 1))
74+
.toEqual({ start: 0, end: 1, unit: 'px' })
6275
})
6376
})
6477

6578
describe('split', () => {
66-
test('returns value for unknown properties', () => {
79+
test('returns value for unknown props', () => {
6780
expect(split('foo', 'bar')).toEqual('bar')
6881
})
6982
test('returns value for non-splittable values', () => {
7083
expect(split('border', 'bar')).toEqual('bar')
7184
})
7285
test('returns splitted value otherwise', () => {
73-
expect(split('border', '1px solid #f00')).toEqual([ '1px', 'solid', '#f00' ])
86+
expect(split('border', '1px solid #f00'))
87+
.toEqual([ '1px', 'solid', '#f00' ])
88+
})
89+
})
90+
91+
describe('parseHexColor', () => {
92+
test('parses hex color shorthand', () => {
93+
expect(parseHexColor('#f00')).toEqual([ 255, 0, 0 ])
94+
})
95+
test('parses hex color', () => {
96+
expect(parseHexColor('#00ff00')).toEqual([ 0, 255, 0 ])
97+
})
98+
test('doesnt parse when not a hex color', () => {
99+
expect(parseHexColor('wat')).toEqual(undefined)
74100
})
75101
})
76102
})

src/__tests__/util.test.js

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
calculateObsoleteValues,
66
getInterpolator,
77
omitEmptyValues,
8+
rgbFloatToHex,
89
toString,
910
} from '../util'
1011

@@ -108,4 +109,13 @@ describe('util', () => {
108109
.toEqual('0%{opacity:0;left:20px;}100%{opacity:1;left:200px;}')
109110
})
110111
})
112+
113+
describe('rgbFloatToHex', () => {
114+
test('converts floats to hex, prepends 0 to single digit results', () => {
115+
expect(rgbFloatToHex(15)).toEqual('0f')
116+
expect(rgbFloatToHex(254)).toEqual('fe')
117+
expect(rgbFloatToHex(500)).toEqual('ff')
118+
expect(rgbFloatToHex(-6)).toEqual('00')
119+
})
120+
})
111121
})

src/index.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
calculateObsoleteValues,
1616
getInterpolator,
1717
omitEmptyValues,
18+
rgbFloatToHex,
1819
toString,
1920
} from './util'
2021

@@ -57,7 +58,7 @@ export const spring = (startStyles, endStyles, options = {}) => {
5758
const parsed = parseStyles(startStyles, endStyles)
5859

5960
// build keyframe styles based on parsed properties
60-
parsed.forEach(({ prop, unit, start, end, fixed }) => {
61+
parsed.forEach(({ prop, unit, start, end, rgb, fixed }) => {
6162
// if start and end values differ, interpolate between them
6263
if (!isNil(start) && !isNil(end)) {
6364
interpolate(start, end).forEach((interpolated, i) => {
@@ -67,6 +68,17 @@ export const spring = (startStyles, endStyles, options = {}) => {
6768
value = value === 0 || !unit ? value : `${value}${unit}`
6869
result[i] = addValueToProperty(result[i], prop, value)
6970
})
71+
// if hex representations of rgb colors are found
72+
} else if (!isNil(rgb)) {
73+
// interpolate each color component separately
74+
const r = interpolate(rgb[0][0], rgb[1][0])
75+
const g = interpolate(rgb[0][1], rgb[1][1])
76+
const b = interpolate(rgb[0][2], rgb[1][2])
77+
r.forEach((interpolated, i) => {
78+
const toRgb = rgbFloatToHex
79+
result[i] = addValueToProperty(result[i], prop,
80+
`#${toRgb(r[i])}${toRgb(g[i])}${toRgb(b[i])}`)
81+
})
7082
// otherwise the value is fixed and can directly be appended to the
7183
// resulting keyframe styles
7284
} else if (!isNil(fixed)) {
@@ -81,7 +93,10 @@ export const spring = (startStyles, endStyles, options = {}) => {
8193
const obsoleteValues = calculateObsoleteValues(result)
8294
result = mapValues(result, (value, i) => {
8395
const result = mapValues(value, (value, key) => combine(key, value))
84-
return pickBy(result, (_, property) => obsoleteValues[property].indexOf(Number(i)) < 0)
96+
return pickBy(
97+
result,
98+
(_, property) => obsoleteValues[property].indexOf(Number(i)) < 0,
99+
)
85100
})
86101
result = omitEmptyValues(result)
87102
result = appendToKeys(result, '%')
@@ -109,7 +124,7 @@ export const spring = (startStyles, endStyles, options = {}) => {
109124
// console.time('interpolate 2')
110125
// spring(
111126
// { 'margin-left': `250px`, border: '1px solid #f00' },
112-
// { 'margin-left': 0, border: '10px solid #f00' },
127+
// { 'margin-left': 0, border: '10px solid #bada55' },
113128
// { preset: 'gentle' },
114129
// )
115130
// console.timeEnd('interpolate 2')

0 commit comments

Comments
 (0)