Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat]: Autoplay - Delay between transitions per slide + Possibility to create a progress bar #1062

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Implement #743, #1059.
  • Loading branch information
davidjerleke committed Nov 19, 2024
commit debe3c54663ab695086faa709f965ed84bed1681
131 changes: 73 additions & 58 deletions packages/embla-carousel-auto-scroll/src/components/AutoScroll.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { OptionsType, defaultOptions } from './Options'
import { getAutoScrollRootNode } from './utils'
import {
CreatePluginType,
OptionsHandlerType,
Expand Down Expand Up @@ -34,10 +35,10 @@ function AutoScroll(userOptions: AutoScrollOptionsType = {}): AutoScrollType {
let options: OptionsType
let emblaApi: EmblaCarouselType
let destroyed: boolean
let playing = false
let resume = true
let timer = 0
let startDelay: number
let timerId = 0
let autoScrollActive = false
let mouseIsOver = false
let defaultScrollBehaviour: ScrollBodyType

function init(
Expand All @@ -58,89 +59,77 @@ function AutoScroll(userOptions: AutoScrollOptionsType = {}): AutoScrollType {
defaultScrollBehaviour = emblaApi.internalEngine().scrollBody

const { eventStore } = emblaApi.internalEngine()
const emblaRoot = emblaApi.rootNode()
const root = (options.rootNode && options.rootNode(emblaRoot)) || emblaRoot
const container = emblaApi.containerNode()
const isDraggable = !!emblaApi.internalEngine().options.watchDrag
const root = getAutoScrollRootNode(emblaApi, options.rootNode)

emblaApi.on('pointerDown', stopScroll)
if (isDraggable) {
emblaApi.on('pointerDown', pointerDown)
}

if (!options.stopOnInteraction) {
emblaApi.on('pointerUp', startScrollOnSettle)
if (isDraggable && !options.stopOnInteraction) {
emblaApi.on('pointerUp', pointerUp)
}

if (options.stopOnMouseEnter) {
eventStore.add(root, 'mouseenter', () => {
resume = false
stopScroll()
})

if (!options.stopOnInteraction) {
eventStore.add(root, 'mouseleave', () => {
resume = true
startScroll()
})
}
eventStore.add(root, 'mouseenter', mouseEnter)
}

if (options.stopOnMouseEnter && !options.stopOnInteraction) {
eventStore.add(root, 'mouseleave', mouseLeave)
}

if (options.stopOnFocusIn) {
emblaApi.on('slideFocusStart', stopScroll)
emblaApi.on('slideFocusStart', stopAutoScroll)
}

if (!options.stopOnInteraction) {
eventStore.add(container, 'focusout', startScroll)
}
if (options.stopOnFocusIn && !options.stopOnInteraction) {
eventStore.add(emblaApi.containerNode(), 'focusout', startAutoScroll)
}

if (options.playOnInit) startScroll()
if (options.playOnInit) startAutoScroll()
}

function destroy(): void {
emblaApi
.off('pointerDown', stopScroll)
.off('pointerUp', startScrollOnSettle)
.off('slideFocusStart', stopScroll)
.off('settle', onSettle)
stopScroll()
.off('pointerDown', pointerDown)
.off('pointerUp', pointerUp)
.off('slideFocusStart', stopAutoScroll)
.off('settle', settle)

stopAutoScroll()
destroyed = true
playing = false
autoScrollActive = false
}

function startScroll(): void {
if (destroyed || playing) return
if (!resume) return
function startAutoScroll(): void {
if (destroyed) return
if (autoScrollActive) return
emblaApi.emit('autoScroll:play')

const engine = emblaApi.internalEngine()
const { ownerWindow } = engine

timer = ownerWindow.setTimeout(() => {
timerId = ownerWindow.setTimeout(() => {
engine.scrollBody = createAutoScrollBehaviour(engine)
engine.animation.start()
}, startDelay)

playing = true
autoScrollActive = true
}

function stopScroll(): void {
if (destroyed || !playing) return
function stopAutoScroll(): void {
if (destroyed) return
if (!autoScrollActive) return
emblaApi.emit('autoScroll:stop')

const engine = emblaApi.internalEngine()
const { ownerWindow } = engine

engine.scrollBody = defaultScrollBehaviour
ownerWindow.clearTimeout(timer)
timer = 0

playing = false
}
ownerWindow.clearTimeout(timerId)
timerId = 0

function onSettle(): void {
if (resume) startScroll()
emblaApi.off('settle', onSettle)
}

function startScrollOnSettle(): void {
emblaApi.on('settle', onSettle)
autoScrollActive = false
}

function createAutoScrollBehaviour(engine: EngineType): ScrollBodyType {
Expand Down Expand Up @@ -196,7 +185,7 @@ function AutoScroll(userOptions: AutoScrollOptionsType = {}): AutoScrollType {
const constrainedLocation = constrain(location.get())
location.set(constrainedLocation)
target.set(location)
stopScroll()
stopAutoScroll()
}

return self
Expand All @@ -216,27 +205,53 @@ function AutoScroll(userOptions: AutoScrollOptionsType = {}): AutoScrollType {
return self
}

function pointerDown(): void {
if (!mouseIsOver) stopAutoScroll()
}

function pointerUp(): void {
if (!mouseIsOver) startAutoScrollOnSettle()
}

function mouseEnter(): void {
mouseIsOver = true
stopAutoScroll()
}

function mouseLeave(): void {
mouseIsOver = false
startAutoScroll()
}

function settle(): void {
emblaApi.off('settle', settle)
startAutoScroll()
}

function startAutoScrollOnSettle(): void {
emblaApi.on('settle', settle)
}

function play(startDelayOverride?: number): void {
if (typeof startDelayOverride !== 'undefined') {
startDelay = startDelayOverride
}
resume = true
startScroll()
startAutoScroll()
}

function stop(): void {
if (playing) stopScroll()
if (autoScrollActive) stopAutoScroll()
}

function reset(): void {
if (playing) {
stopScroll()
startScrollOnSettle()
if (autoScrollActive) {
stopAutoScroll()
startAutoScrollOnSettle()
}
}

function isPlaying(): boolean {
return playing
return autoScrollActive
}

const self: AutoScrollType = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { CreateOptionsType } from 'embla-carousel'

export type RootNodeType =
| null
| ((emblaRoot: HTMLElement) => HTMLElement | null)

export type OptionsType = CreateOptionsType<{
direction: 'forward' | 'backward'
speed: number
Expand All @@ -8,7 +12,7 @@ export type OptionsType = CreateOptionsType<{
stopOnFocusIn: boolean
stopOnInteraction: boolean
stopOnMouseEnter: boolean
rootNode: ((emblaRoot: HTMLElement) => HTMLElement | null) | null
rootNode: RootNodeType
}>

export const defaultOptions: OptionsType = {
Expand Down
10 changes: 10 additions & 0 deletions packages/embla-carousel-auto-scroll/src/components/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { EmblaCarouselType } from 'embla-carousel/components/EmblaCarousel'
import { RootNodeType } from './Options'

export function getAutoScrollRootNode(
emblaApi: EmblaCarouselType,
rootNode: RootNodeType
): HTMLElement {
const emblaRootNode = emblaApi.rootNode()
return (rootNode && rootNode(emblaRootNode)) || emblaRootNode
}
Loading
Loading