| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 6dba52f commit 6c80db3
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,5 +1,5 @@ | |||
| 1 | 1 | import { View } from '../core/view'; | |
| 2 | - import { LinearGradient } from './linear-gradient'; | ||
| 2 | + import { LinearGradient, resolveGradientStopOffsets } from './linear-gradient'; | ||
| 3 | 3 | import { ClipPathFunction } from './clip-path-function'; | |
| 4 | 4 | import { isDataURI, isFileOrResourcePath, RESOURCE_PREFIX, FILE_PREFIX } from '../../utils'; | |
| 5 | 5 | import { CSSValue, parse } from '../../css-value/reworkcss-value'; | |
@@ -15,13 +15,10 @@ function fromBase64(source: string): android.graphics.Bitmap { | |||
| 15 | 15 | function fromGradient(gradient: LinearGradient): org.nativescript.widgets.LinearGradientDefinition { | |
| 16 | 16 | const colors = Array.create('int', gradient.colorStops.length); | |
| 17 | 17 | const stops = Array.create('float', gradient.colorStops.length); | |
| 18 | - let hasStops = false; | ||
| 18 | + const offsets = resolveGradientStopOffsets(gradient.colorStops); | ||
| 19 | 19 | gradient.colorStops.forEach((stop, index) => { | |
| 20 | 20 | colors[index] = stop.color.android; | |
| 21 | - if (stop.offset) { | ||
| 22 | - stops[index] = stop.offset.value; | ||
| 23 | - hasStops = true; | ||
| 24 | - } | ||
| 21 | + stops[index] = offsets[index]; | ||
| 25 | 22 | }); | |
| 26 | 23 | ||
| 27 | 24 | const alpha = gradient.angle / (Math.PI * 2); | |
@@ -30,7 +27,7 @@ function fromGradient(gradient: LinearGradient): org.nativescript.widgets.Linear | |||
| 30 | 27 | const endX = Math.pow(Math.sin(Math.PI * (alpha + 0.25)), 2); | |
| 31 | 28 | const endY = Math.pow(Math.sin(Math.PI * alpha), 2); | |
| 32 | 29 | ||
| 33 | - return new org.nativescript.widgets.LinearGradientDefinition(startX, startY, endX, endY, colors, hasStops ? stops : null); | ||
| 30 | + return new org.nativescript.widgets.LinearGradientDefinition(startX, startY, endX, endY, colors, stops); | ||
| 34 | 31 | } | |
| 35 | 32 | ||
| 36 | 33 | const pattern = /url\(('|")(.*?)\1\)/; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,37 @@ | |||
| 1 | + import { resolveGradientStopOffsets } from './linear-gradient'; | ||
| 2 | + | ||
| 3 | + function stops(...offsets: (number | undefined)[]) { | ||
| 4 | + return offsets.map((value) => (value === undefined ? { color: null } : { color: null, offset: { unit: '%' as const, value } })); | ||
| 5 | + } | ||
| 6 | + | ||
| 7 | + describe('resolveGradientStopOffsets', () => { | ||
| 8 | + it('fills a missing final stop with 1', () => { | ||
| 9 | + expect(resolveGradientStopOffsets(stops(0.68, undefined))).toEqual([0.68, 1]); | ||
| 10 | + }); | ||
| 11 | + | ||
| 12 | + it('spreads unpositioned stops evenly', () => { | ||
| 13 | + expect(resolveGradientStopOffsets(stops(undefined, undefined, undefined))).toEqual([0, 0.5, 1]); | ||
| 14 | + const [a, b, c] = resolveGradientStopOffsets(stops(0.3, undefined, 0.6)); | ||
| 15 | + expect([a, c]).toEqual([0.3, 0.6]); | ||
| 16 | + expect(b).toBeCloseTo(0.45, 10); | ||
| 17 | + }); | ||
| 18 | + | ||
| 19 | + it('spreads a run between its positioned neighbours', () => { | ||
| 20 | + const [a, b, c, d, e] = resolveGradientStopOffsets(stops(undefined, 0.2, undefined, undefined, 1)); | ||
| 21 | + expect([a, b, e]).toEqual([0, 0.2, 1]); | ||
| 22 | + expect(c).toBeCloseTo(0.4667, 3); | ||
| 23 | + expect(d).toBeCloseTo(0.7333, 3); | ||
| 24 | + }); | ||
| 25 | + | ||
| 26 | + it('raises a position that goes backwards', () => { | ||
| 27 | + expect(resolveGradientStopOffsets(stops(0.5, 0.2))).toEqual([0.5, 0.5]); | ||
| 28 | + }); | ||
| 29 | + | ||
| 30 | + it('keeps fully positioned stops as written', () => { | ||
| 31 | + expect(resolveGradientStopOffsets(stops(0, 0.52, 0.84, 1))).toEqual([0, 0.52, 0.84, 1]); | ||
| 32 | + }); | ||
| 33 | + | ||
| 34 | + it('handles no stops', () => { | ||
| 35 | + expect(resolveGradientStopOffsets([])).toEqual([]); | ||
| 36 | + }); | ||
| 37 | + }); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,6 +7,46 @@ export interface ColorStop { | |||
| 7 | 7 | offset?: CoreTypes.LengthPercentUnit; | |
| 8 | 8 | } | |
| 9 | 9 | ||
| 10 | + /** | ||
| 11 | + * Positions for every color stop, per CSS: a first stop without a position | ||
| 12 | + * sits at 0, a last one at 1, a run of unpositioned stops is spread evenly | ||
| 13 | + * between its positioned neighbours, and a position lower than one before it | ||
| 14 | + * is raised to that value. | ||
| 15 | + */ | ||
| 16 | + export function resolveGradientStopOffsets(colorStops: readonly ColorStop[]): number[] { | ||
| 17 | + const count = colorStops.length; | ||
| 18 | + if (count === 0) { | ||
| 19 | + return []; | ||
| 20 | + } | ||
| 21 | + const offsets: (number | undefined)[] = colorStops.map((stop) => (stop.offset ? stop.offset.value : undefined)); | ||
| 22 | + if (offsets[0] === undefined) { | ||
| 23 | + offsets[0] = 0; | ||
| 24 | + } | ||
| 25 | + if (offsets[count - 1] === undefined) { | ||
| 26 | + offsets[count - 1] = 1; | ||
| 27 | + } | ||
| 28 | + let highest = offsets[0]; | ||
| 29 | + for (let i = 1; i < count; i++) { | ||
| 30 | + const offset = offsets[i]; | ||
| 31 | + if (offset !== undefined) { | ||
| 32 | + offsets[i] = Math.max(offset, highest); | ||
| 33 | + highest = offsets[i]; | ||
| 34 | + } | ||
| 35 | + } | ||
| 36 | + let start = 0; | ||
| 37 | + for (let i = 1; i < count; i++) { | ||
| 38 | + if (offsets[i] === undefined) { | ||
| 39 | + continue; | ||
| 40 | + } | ||
| 41 | + const gap = i - start; | ||
| 42 | + for (let k = start + 1; k < i; k++) { | ||
| 43 | + offsets[k] = offsets[start] + ((offsets[i] - offsets[start]) * (k - start)) / gap; | ||
| 44 | + } | ||
| 45 | + start = i; | ||
| 46 | + } | ||
| 47 | + return offsets as number[]; | ||
| 48 | + } | ||
| 49 | + | ||
| 10 | 50 | export class LinearGradient { | |
| 11 | 51 | public angle: number; | |
| 12 | 52 | public colorStops: ColorStop[]; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,6 @@ | |||
| 1 | 1 | import { Screen } from '../platform'; | |
| 2 | 2 | import * as utils from '../utils'; | |
| 3 | - import { LinearGradient } from './styling/linear-gradient'; | ||
| 3 | + import { LinearGradient, resolveGradientStopOffsets } from './styling/linear-gradient'; | ||
| 4 | 4 | import { NativeScriptUIView } from './utils'; | |
| 5 | 5 | ||
| 6 | 6 | export namespace ios { | |
@@ -52,21 +52,14 @@ export namespace ios { | |||
| 52 | 52 | ||
| 53 | 53 | const iosColors = NSMutableArray.alloc().initWithCapacity(gradient.colorStops.length); | |
| 54 | 54 | const iosStops = NSMutableArray.alloc<number>().initWithCapacity(gradient.colorStops.length); | |
| 55 | - let hasStops = false; | ||
| 56 | - | ||
| 57 | - gradient.colorStops.forEach((stop) => { | ||
| 55 | + const offsets = resolveGradientStopOffsets(gradient.colorStops); | ||
| 56 | + gradient.colorStops.forEach((stop, index) => { | ||
| 58 | 57 | iosColors.addObject(stop.color.ios.CGColor); | |
| 59 | - if (stop.offset) { | ||
| 60 | - iosStops.addObject(stop.offset.value); | ||
| 61 | - hasStops = true; | ||
| 62 | - } | ||
| 58 | + iosStops.addObject(offsets[index]); | ||
| 63 | 59 | }); | |
| 64 | 60 | ||
| 65 | 61 | gradientLayer.colors = iosColors; | |
| 66 | - | ||
| 67 | - if (hasStops) { | ||
| 68 | - gradientLayer.locations = iosStops; | ||
| 69 | - } | ||
| 62 | + gradientLayer.locations = iosStops; | ||
| 70 | 63 | ||
| 71 | 64 | const alpha = gradient.angle / (Math.PI * 2); | |
| 72 | 65 | const startX = Math.pow(Math.sin(Math.PI * (alpha + 0.75)), 2); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments