Skip to content

Commit

Permalink
Refactor scrollTarget, rename method for scrollBy
Browse files Browse the repository at this point in the history
  • Loading branch information
davidcetinkaya committed Mar 25, 2020
1 parent 579d3d8 commit 0732a5c
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 113 deletions.
2 changes: 1 addition & 1 deletion docs/index.js

Large diffs are not rendered by default.

60 changes: 32 additions & 28 deletions src/__tests__/scrollBy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { Vector1D } from '../components/vector1d'
const snapSizes = [80, 40, 30, 40, 60]
const scrollSnaps = [10, -50, -85, -120, -170]
const contentSize = snapSizes.reduce((a, s) => a + s, 0)
const progressBelowScrollLength = 0.25
const progressAboveScrollLength = 2
const progressLessThanScrollLength = 0.25
const progressMoreThanScrollLength = 2

const getScrollLimit = (loop: boolean): Limit => {
const scrollLimit = ScrollLimit({ contentSize, loop })
Expand All @@ -20,65 +20,69 @@ const getScrollBy = (loop: boolean, target: Vector1D): ScrollBy => {
}

describe('ScrollBy', () => {
describe('Loop False', () => {
describe('When loop is false, distance is', () => {
const loop = false
const scrollLimit = getScrollLimit(loop)
const scrollLength = scrollLimit.min - scrollLimit.max

test('Distance adds to current scroll location', () => {
test('Added to current scroll location', () => {
const scrollBy = getScrollBy(loop, Vector1D(scrollLimit.min))
const progress = -progressBelowScrollLength
const progress = -progressLessThanScrollLength
const distance = scrollLength * progress
expect(scrollBy.distance(progress)).toBe(distance)
expect(scrollBy.progress(progress)).toBe(distance)
})
test('Distance subtracts from current scroll location', () => {

test('Subtracted from current scroll location', () => {
const scrollBy = getScrollBy(loop, Vector1D(scrollLimit.max))
const progress = progressBelowScrollLength
const progress = progressLessThanScrollLength
const distance = scrollLength * progress
expect(scrollBy.distance(progress)).toBe(distance)
expect(scrollBy.progress(progress)).toBe(distance)
})

test('Distance does not exceed the Limit min bound', () => {
test('Limited to limit min', () => {
const scrollBy = getScrollBy(loop, Vector1D(scrollLimit.min))
const progress = progressAboveScrollLength
expect(scrollBy.distance(progress)).toBe(0)
const progress = progressMoreThanScrollLength
expect(scrollBy.progress(progress)).toBe(0)
})
test('Distance does not exceed the Limit max bound', () => {

test('Limited to limit max', () => {
const scrollBy = getScrollBy(loop, Vector1D(scrollLimit.max))
const progress = -progressAboveScrollLength
expect(scrollBy.distance(progress)).toBe(0)
const progress = -progressMoreThanScrollLength
expect(scrollBy.progress(progress)).toBe(0)
})
})

describe('Loop True', () => {
describe('When loop is true, distance is', () => {
const loop = true
const scrollLimit = getScrollLimit(loop)
const scrollLength = scrollLimit.min - scrollLimit.max

test('Distance adds to current scroll location', () => {
test('Added to current scroll location', () => {
const scrollBy = getScrollBy(loop, Vector1D(scrollLimit.min))
const progress = -progressBelowScrollLength
const progress = -progressLessThanScrollLength
const distance = scrollLength * progress
expect(scrollBy.distance(progress)).toBe(distance)
expect(scrollBy.progress(progress)).toBe(distance)
})
test('Distance subtracts from current scroll location', () => {

test('Subtracted from current scroll location', () => {
const scrollBy = getScrollBy(loop, Vector1D(scrollLimit.max))
const progress = progressBelowScrollLength
const progress = progressLessThanScrollLength
const distance = scrollLength * progress
expect(scrollBy.distance(progress)).toBe(distance)
expect(scrollBy.progress(progress)).toBe(distance)
})

test('Distance is allowed to exceed the Limit min bound', () => {
test('Allowed to exceed limit min', () => {
const scrollBy = getScrollBy(loop, Vector1D(scrollLimit.max))
const progress = progressAboveScrollLength
const progress = progressMoreThanScrollLength
const distance = scrollLength * progress
expect(scrollBy.distance(progress)).toBe(distance)
expect(scrollBy.progress(progress)).toBe(distance)
})
test('Distance is allowed to exceed the Limit max bound', () => {

test('Allowed to exceed limit max', () => {
const scrollBy = getScrollBy(loop, Vector1D(scrollLimit.min))
const progress = -progressAboveScrollLength
const progress = -progressMoreThanScrollLength
const distance = scrollLength * progress
expect(scrollBy.distance(progress)).toBe(distance)
expect(scrollBy.progress(progress)).toBe(distance)
})
})
})
7 changes: 2 additions & 5 deletions src/__tests__/scrollSnap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ const snapSizes = [80, 24, 61, 55, 76, 15]
const snapIndexes = arrayKeys(snapSizes)

const getScrollSnap = (align: Alignments): ScrollSnap => {
return ScrollSnap({
alignment: Alignment({ align, viewSize }),
loop: false,
snapSizes,
})
const alignment = Alignment({ align, viewSize })
return ScrollSnap({ alignment, snapSizes, loop: false })
}

describe('ScrollSnap', () => {
Expand Down
1 change: 0 additions & 1 deletion src/components/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ export function Engine(
limit,
loop,
scrollSnaps,
snapSizes,
target,
}),
target,
Expand Down
20 changes: 10 additions & 10 deletions src/components/scrollBy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@ type Params = {
}

export type ScrollBy = {
distance: (n: number) => number
progress: (n: number) => number
}

export function ScrollBy(params: Params): ScrollBy {
const { loop, limit, target } = params
const { min, max, reachedMin, reachedMax } = limit
const scrollLength = min - max

function withinBounds(n: number): number {
const desired = target.get() + n
if (reachedMax(desired)) return max - target.get()
if (reachedMin(desired)) return min - target.get()
return n
function withinBounds(distance: number): number {
const desiredTarget = target.get() + distance
if (reachedMax(desiredTarget)) return max - target.get()
if (reachedMin(desiredTarget)) return min - target.get()
return distance
}

function distance(n: number): number {
const progress = scrollLength * n
return loop ? progress : withinBounds(progress)
function progress(n: number): number {
const distance = scrollLength * n
return loop ? distance : withinBounds(distance)
}

const self: ScrollBy = {
distance,
progress,
}
return Object.freeze(self)
}
114 changes: 47 additions & 67 deletions src/components/scrollTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,12 @@ type Params = {
align: Alignments
index: Counter
loop: boolean
snapSizes: number[]
scrollSnaps: number[]
contentSize: number
limit: Limit
target: Vector1D
}

type Bound = {
start: number
end: number
}

export type Target = {
distance: number
index: number
Expand All @@ -32,78 +26,64 @@ export type ScrollTarget = {
export function ScrollTarget(params: Params): ScrollTarget {
const { loop, limit, scrollSnaps, contentSize } = params
const { reachedMin, reachedMax, reachedAny } = limit
const snapBounds = calculateSnapBounds()

function calculateSnapBounds(): Bound[] {
const { align, snapSizes } = params
const counter = params.index.clone()

return snapSizes.reduce((bounds: Bound[], size, i) => {
const next = counter.set(i).add(align === 'end' ? 1 : 0)
const end = scrollSnaps[i] - snapSizes[next.get()] / 2
const start = !i ? scrollSnaps[0] : bounds[i - 1].end
return bounds.concat([{ start, end }])
}, [])

function minDistance(d1: number, d2: number): number {
return Math.abs(d1) < Math.abs(d2) ? d1 : d2
}

function offsetToSnap(target: Target): number {
const { distance, index } = target
const lastSnap = scrollSnaps[params.index.max]
const addOffset = loop && distance < lastSnap && index === 0
const offset = addOffset ? distance + contentSize : distance
return scrollSnaps[index] - offset
function findTargetSnap(target: number): Target {
while (reachedMin(target)) target += contentSize
while (reachedMax(target)) target -= contentSize

const ascDiffsToSnaps = scrollSnaps
.map(scrollSnap => scrollSnap - target)
.map(diffToSnap => shortestWay(diffToSnap, 0))
.map((diff, i) => ({ diff, index: i }))
.sort((d1, d2) => Math.abs(d1.diff) - Math.abs(d2.diff))

const { index } = ascDiffsToSnaps[0]
return { index, distance: target }
}

function findTargetSnapAt(distance: number): Target {
while (reachedMax(distance)) distance -= contentSize
while (reachedMin(distance)) distance += contentSize
const foundIndex = snapBounds.reduce((a, b, i) => {
return distance <= b.start && distance > b.end ? i : a
}, 0)
return { distance, index: foundIndex }
function shortestWay(target: number, direction: number): number {
const t1 = target
const t2 = target + contentSize
const t3 = target - contentSize

if (!loop) return t1
if (!direction) return minDistance(minDistance(t1, t2), t3)

const shortest = minDistance(t1, direction === 1 ? t2 : t3)
return Math.abs(shortest) * direction
}

function minDistance(d1: number, d2: number): number {
return Math.abs(d1) < Math.abs(d2) ? d1 : d2
function findTargetIndex(target: number, index: number): number {
const reachedBound = !loop && reachedAny(target)
if (!reachedBound) return index

const { min, max } = params.index
return reachedMax(target) ? min : max
}

function byIndex(index: number, direction: number): Target {
const targetVector = params.target.get()
const distanceToSnap = scrollSnaps[index] - targetVector
const target = { distance: distanceToSnap, index }

if (loop) {
const d1 = distanceToSnap
const d2 = contentSize + distanceToSnap
const d3 = distanceToSnap - contentSize

if (direction && params.index.max === 1) {
const shortest = minDistance(d1, direction === 1 ? d2 : d3)
target.distance = Math.abs(shortest) * direction
} else {
target.distance = minDistance(minDistance(d1, d2), d3)
}
}
return target
const diffToSnap = scrollSnaps[index] - params.target.get()
const distance = shortestWay(diffToSnap, direction)
return { index, distance }
}

function byDistance(force: number, snap: boolean): Target {
const { target, index } = params
const distance = target.get() + force
const targetSnap = findTargetSnapAt(distance)
const reachedEdge = !loop && reachedAny(distance)

if (reachedEdge || !snap) {
const { min, max } = index
const edgeIndex = reachedMax(distance) ? min : max
const targetIndex = reachedEdge ? edgeIndex : targetSnap.index
return { distance: force, index: targetIndex }
} else {
const currentSnap = { distance, index: index.get() }
const snapPoint = force === 0 ? currentSnap : targetSnap
const snapDistance = force + offsetToSnap(snapPoint)
return { distance: snapDistance, index: snapPoint.index }
}
function byDistance(distance: number, snap: boolean): Target {
const target = params.target.get() + distance
const targetSnap = findTargetSnap(target)
const targetIndex = findTargetIndex(target, targetSnap.index)
const index = snap && !distance ? params.index.get() : targetIndex
const reachedBound = !loop && reachedAny(target)

if (!snap || reachedBound) return { index, distance }

const diffToSnap = scrollSnaps[index] - targetSnap.distance
const snapDistance = distance + shortestWay(diffToSnap, 0)

return { index, distance: snapDistance }
}

const self: ScrollTarget = {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export function EmblaCarousel(
}

function scrollBy(progress: number): void {
const distance = engine.scrollBy.distance(progress)
const distance = engine.scrollBy.progress(progress)
engine.scrollBody.useDefaultMass().useDefaultSpeed()
engine.scrollTo.distance(distance, false)
}
Expand Down

0 comments on commit 0732a5c

Please sign in to comment.