#45 : Promisify

This commit is contained in:
Vadim
2021-07-17 18:58:00 +03:00
parent e9805e373c
commit 1b314a530a
3 changed files with 109 additions and 110 deletions

View File

@@ -13,30 +13,19 @@
} from '../../utils/event'
import { getAdjacentIndexes } from '../../utils/page'
import { get } from '../../utils/object'
import { ProgressManager } from '../../utils/ProgressManager.js'
import { ProgressManager } from '../../utils/ProgressManager'
import { wait } from '../../utils/interval'
const dispatch = createEventDispatcher()
const autoplayDirectionFnDescription = {
[NEXT]: () => {
progressManager.start(() => {
showNextPage()
})
},
[PREV]: () => {
progressManager.start(() => {
showPrevPage()
})
}
[NEXT]: async () => await progressManager.start(showNextPage),
[PREV]: async () => await progressManager.start(showPrevPage)
}
const directionFnDescription = {
[NEXT]: () => {
showNextPage()
},
[PREV]: () => {
showPrevPage()
}
[NEXT]: showNextPage,
[PREV]: showPrevPage
}
/**
@@ -70,6 +59,13 @@
* Enables autoplay of pages
*/
export let autoplay = false
$: {
if (autoplay) {
applyAutoplay()
} else {
progressManager.reset()
}
}
/**
* Autoplay change interval (ms)
@@ -96,31 +92,41 @@
*/
export let dots = true
export function goTo(pageIndex, options) {
export async function goTo(pageIndex, options) {
const animated = get(options, 'animated', true)
if (typeof pageIndex !== 'number') {
throw new Error('pageIndex should be a number')
}
showPage(pageIndex + Number(infinite), { animated })
await showPage(pageIndex + Number(infinite), { animated })
}
export function goToPrev(options) {
export async function goToPrev(options) {
const animated = get(options, 'animated', true)
showPrevPage({ animated })
await showPrevPage({ animated })
}
export function goToNext(options) {
export async function goToNext(options) {
const animated = get(options, 'animated', true)
showNextPage({ animated })
await showNextPage({ animated })
}
let store = createStore()
let currentPageIndex = 0
$: originalCurrentPageIndex = currentPageIndex - Number(infinite);
$: originalCurrentPageIndex = getOriginalCurrentPageIndex(currentPageIndex, pagesCount, infinite)
$: dispatch('pageChange', originalCurrentPageIndex)
let pagesCount = 0
$: originalPagesCount = Math.max(pagesCount - (infinite ? 2 : 0), 1) // without clones
function getOriginalCurrentPageIndex(currentPageIndex, pagesCount, infinite) {
if (infinite) {
if (currentPageIndex === pagesCount - 1) return 0
if (currentPageIndex === 0) return pagesCount - 3
return currentPageIndex - 1
}
return currentPageIndex
}
let pageWidth = 0
let offset = 0
let pageWindowElement
@@ -169,27 +175,8 @@
pagesElement.append(first.cloneNode(true))
}
function applyAutoplayIfNeeded(options) {
// prevent progress change if not infinite for first and last page
if (
!infinite && (
(autoplayDirection === NEXT && currentPageIndex === pagesCount - 1) ||
(autoplayDirection === PREV && currentPageIndex === 0)
)
) {
progressManager.reset()
return
}
if (autoplay) {
const delayMs = get(options, 'delayMs', 0)
if (delayMs) {
setTimeout(() => {
autoplayDirectionFnDescription[autoplayDirection]()
}, delayMs)
} else {
autoplayDirectionFnDescription[autoplayDirection]()
}
}
async function applyAutoplay() {
await autoplayDirectionFnDescription[autoplayDirection]()
}
let cleanupFns = []
@@ -199,6 +186,16 @@
await tick()
cleanupFns.push(store.subscribe(value => {
currentPageIndex = value.currentPageIndex
// prevent progress change if not infinite for first and last page
if (
!infinite && (
(autoplayDirection === NEXT && currentPageIndex === pagesCount - 1) ||
(autoplayDirection === PREV && currentPageIndex === 0)
)
) {
progressManager.reset()
}
}))
cleanupFns.push(() => progressManager.reset())
if (pagesElement && pageWindowElement) {
@@ -211,8 +208,6 @@
applyPageSizes()
}
applyAutoplayIfNeeded()
addResizeEventListener(applyPageSizes)
})()
})
@@ -222,26 +217,30 @@
cleanupFns.filter(fn => fn && typeof fn === 'function').forEach(fn => fn())
})
function handlePageChange(pageIndex) {
showPage(pageIndex + Number(infinite))
async function handlePageChange(pageIndex) {
await showPage(pageIndex + Number(infinite))
}
function offsetPage(animated) {
// _duration is an offset animation time
_duration = animated ? duration : 0
offset = -currentPageIndex * pageWidth
return new Promise((resolve) => {
// _duration is an offset animation time
_duration = animated ? duration : 0
offset = -currentPageIndex * pageWidth
setTimeout(() => {
resolve()
}, _duration)
})
}
// makes delayed jump to 1st or last element
function jumpIfNeeded() {
async function jumpIfNeeded() {
let jumped = false
if (infinite) {
if (currentPageIndex === 0) {
// offsetDelayMs should depend on _duration, as it wait when offset finishes
showPage(pagesCount - 2, { offsetDelayMs: _duration, animated: false })
await showPage(pagesCount - 2, { animated: false })
jumped = true
} else if (currentPageIndex === pagesCount - 1) {
showPage(1, { offsetDelayMs: _duration, animated: false })
await showPage(1, { animated: false })
jumped = true
}
}
@@ -250,54 +249,43 @@
// Disable page change while animation is in progress
let disabled = false
function safeChangePage(cb, options) {
const animated = get(options, 'animated', true)
async function changePage(updateStoreFn, options) {
if (disabled) return
cb()
disabled = true
setTimeout(() => {
disabled = false
}, animated ? duration : 0)
updateStoreFn()
await offsetPage(get(options, 'animated', true))
disabled = false
const jumped = await jumpIfNeeded()
!jumped && autoplay && applyAutoplay() // no need to wait it finishes
}
function showPage(pageIndex, options) {
const animated = get(options, 'animated', true)
const offsetDelayMs = get(options, 'offsetDelayMs', 0)
safeChangePage(() => {
store.moveToPage({ pageIndex, pagesCount })
// delayed page transition, used for infinite autoplay to jump to real page
setTimeout(() => {
offsetPage(animated)
const jumped = jumpIfNeeded()
!jumped && applyAutoplayIfNeeded({ delayMs: _duration }) // while offset animation is in progress (delayMs = _duration ms) wait for it
}, offsetDelayMs)
}, { animated })
async function showPage(pageIndex, options) {
await changePage(
() => store.moveToPage({ pageIndex, pagesCount }),
options
)
}
function showPrevPage(options) {
const animated = get(options, 'animated', true)
safeChangePage(() => {
store.prev({ infinite, pagesCount })
offsetPage(animated)
const jumped = jumpIfNeeded()
!jumped && applyAutoplayIfNeeded({ delayMs: _duration })
}, { animated })
async function showPrevPage(options) {
await changePage(
() => store.prev({ infinite, pagesCount }),
options
)
}
function showNextPage(options) {
const animated = get(options, 'animated', true)
safeChangePage(() => {
store.next({ infinite, pagesCount })
offsetPage(animated)
const jumped = jumpIfNeeded()
!jumped && applyAutoplayIfNeeded({ delayMs: _duration })
}, { animated })
async function showNextPage(options) {
await changePage(
() => store.next({ infinite, pagesCount }),
options
)
}
// gestures
function handleSwipeStart() {
_duration = 0
}
function handleThreshold(event) {
directionFnDescription[event.detail.direction]()
async function handleThreshold(event) {
await directionFnDescription[event.detail.direction]()
}
function handleSwipeMove(event) {
offset += event.detail.dx

View File

@@ -17,25 +17,28 @@ export class ProgressManager {
}
start(onFinish) {
this.reset()
return new Promise((resolve) => {
this.reset()
const stepMs = Math.min(STEP_MS, this.#autoplayDuration)
let progress = -stepMs
const stepMs = Math.min(STEP_MS, this.#autoplayDuration)
let progress = -stepMs
this.#interval = setIntervalImmediate(() => {
if (this.#paused) {
return
}
progress += stepMs
this.#interval = setIntervalImmediate(async () => {
if (this.#paused) {
return
}
progress += stepMs
const value = progress / this.#autoplayDuration
this.#onProgressValueChange(value)
const value = progress / this.#autoplayDuration
this.#onProgressValueChange(value)
if (value > 1) {
this.reset()
onFinish()
}
}, stepMs)
if (value > 1) {
this.reset()
await onFinish()
resolve()
}
}, stepMs)
})
}
pause() {

View File

@@ -2,3 +2,11 @@ export const setIntervalImmediate = (fn, ms) => {
fn();
return setInterval(fn, ms);
}
export const wait = (ms) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, ms)
})
}