Skip to content

Commit

Permalink
Implement #202.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidjerleke committed Jan 7, 2025
1 parent ed25d77 commit be65559
Show file tree
Hide file tree
Showing 18 changed files with 331 additions and 132 deletions.
16 changes: 9 additions & 7 deletions packages/embla-carousel/src/components/Animations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type AnimationsUpdateType = (engine: EngineType) => void
export type AnimationsRenderType = (engine: EngineType, alpha: number) => void

export type AnimationsType = {
init: () => void
init: (ownerWindow: WindowType) => void
destroy: () => void
start: () => void
stop: () => void
Expand All @@ -15,19 +15,21 @@ export type AnimationsType = {
}

export function Animations(
ownerDocument: Document,
ownerWindow: WindowType,
update: () => void,
render: (alpha: number) => void
): AnimationsType {
const documentVisibleHandler = EventStore()
const fixedTimeStep = 1000 / 60

let windowInstance: WindowType
let lastTimeStamp: number | null = null
let accumulatedTime = 0
let animationId = 0

function init(): void {
function init(ownerWindow: WindowType): void {
const ownerDocument = ownerWindow.document
windowInstance = ownerWindow

documentVisibleHandler.add(ownerDocument, 'visibilitychange', () => {
if (ownerDocument.hidden) reset()
})
Expand Down Expand Up @@ -59,17 +61,17 @@ export function Animations(
render(alpha)

if (animationId) {
animationId = ownerWindow.requestAnimationFrame(animate)
animationId = windowInstance.requestAnimationFrame(animate)
}
}

function start(): void {
if (animationId) return
animationId = ownerWindow.requestAnimationFrame(animate)
animationId = windowInstance.requestAnimationFrame(animate)
}

function stop(): void {
ownerWindow.cancelAnimationFrame(animationId)
windowInstance.cancelAnimationFrame(animationId)
lastTimeStamp = null
accumulatedTime = 0
animationId = 0
Expand Down
2 changes: 1 addition & 1 deletion packages/embla-carousel/src/components/Axis.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NodeRectType } from './NodeRects'
import { NodeRectType } from './NodeHandler'

export type AxisOptionType = 'x' | 'y'
export type AxisDirectionOptionType = 'ltr' | 'rtl'
Expand Down
13 changes: 9 additions & 4 deletions packages/embla-carousel/src/components/DragHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from './utils'

export type DragHandlerType = {
init: () => void
init: (windowInstance: WindowType) => void
destroy: () => void
pointerDown: () => boolean
}
Expand All @@ -30,8 +30,6 @@ export function DragHandler(
active: boolean,
axis: AxisType,
rootNode: HTMLElement,
ownerDocument: Document,
ownerWindow: WindowType,
target: Vector1DType,
dragTracker: DragTrackerType,
location: Vector1DType,
Expand All @@ -58,6 +56,8 @@ export function DragHandler(
const freeForceBoost = { mouse: 500, touch: 600 }
const baseSpeed = dragFree ? 43 : 25

let ownerDocument: Document
let ownerWindow: WindowType
let isMoving = false
let startScroll = 0
let startCross = 0
Expand All @@ -66,9 +66,14 @@ export function DragHandler(
let preventClick = false
let isMouse = false

function init(): void {
function init(windowInstance: WindowType): void {
if (!active) return

ownerDocument = windowInstance.document
ownerWindow = windowInstance

dragTracker.init(windowInstance)

const node = rootNode
initEvents
.add(node, 'dragstart', (evt) => evt.preventDefault(), nonPassiveEvent)
Expand Down
12 changes: 8 additions & 4 deletions packages/embla-carousel/src/components/DragTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ type PointerCoordType = keyof Touch | keyof MouseEvent
export type PointerEventType = TouchEvent | MouseEvent

export type DragTrackerType = {
init: (windowInstance: WindowType) => void
pointerDown: (evt: PointerEventType) => number
pointerMove: (evt: PointerEventType) => number
pointerUp: (evt: PointerEventType) => number
readPoint: (evt: PointerEventType, evtAxis?: AxisOptionType) => number
}

export function DragTracker(
axis: AxisType,
ownerWindow: WindowType
): DragTrackerType {
export function DragTracker(axis: AxisType): DragTrackerType {
const logInterval = 170

let ownerWindow: WindowType
let startEvent: PointerEventType
let lastEvent: PointerEventType

function init(windowInstance: WindowType): void {
ownerWindow = windowInstance
}

function readTime(evt: PointerEventType): number {
return evt.timeStamp
}
Expand Down Expand Up @@ -57,6 +60,7 @@ export function DragTracker(
}

const self: DragTrackerType = {
init,
pointerDown,
pointerMove,
pointerUp,
Expand Down
86 changes: 52 additions & 34 deletions packages/embla-carousel/src/components/EmblaCarousel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { EventStore } from './EventStore'
import { WatchHandler, WatchHandlerType } from './WatchHandler'
import { EventHandler, EventHandlerType } from './EventHandler'
import { defaultOptions, EmblaOptionsType, OptionsType } from './Options'
import { NodeHandler, NodeHandlerType } from './NodeHandler'
import { OptionsHandler } from './OptionsHandler'
import { PluginsHandler } from './PluginsHandler'
import { EmblaPluginsType, EmblaPluginType } from './Plugins'
import { isNumber, isString, WindowType } from './utils'
import { ScrollToDirectionType } from './ScrollTo'
import { isNumber } from './utils'
import { SsrHandler, SsrHandlerType } from './SsrHandler'

export type EmblaCarouselType = {
canScrollNext: () => boolean
Expand Down Expand Up @@ -43,16 +45,17 @@ export type EmblaCarouselType = {
jump?: boolean,
direction?: ScrollToDirectionType
) => void
ssrStyles: () => string
}

function EmblaCarousel(
root: HTMLElement,
userOptions?: EmblaOptionsType,
userPlugins?: EmblaPluginType[]
userPlugins?: EmblaPluginType[],
headless?: boolean
): EmblaCarouselType {
const ownerDocument = root.ownerDocument
const ownerWindow = <WindowType>ownerDocument.defaultView
const optionsHandler = OptionsHandler(ownerWindow)
const isSsr = !!headless
const optionsHandler = OptionsHandler()
const pluginsHandler = PluginsHandler(optionsHandler)
const mediaHandlers = EventStore()
const watchHandler = WatchHandler()
Expand All @@ -64,43 +67,33 @@ function EmblaCarousel(

let destroyed = false
let engine: EngineType
let nodeHandler: NodeHandlerType
let ssrHandler: SsrHandlerType
let optionsBase = mergeOptions(defaultOptions, EmblaCarousel.globalOptions)
let options = mergeOptions(optionsBase)
let pluginList: EmblaPluginType[] = []
let pluginApis: EmblaPluginsType

let container: HTMLElement
let slides: HTMLElement[]

function storeElements(): void {
const { container: userContainer, slides: userSlides } = options

const customContainer = isString(userContainer)
? root.querySelector(userContainer)
: userContainer
container = <HTMLElement>(customContainer || root.children[0])

const customSlides = isString(userSlides)
? container.querySelectorAll(userSlides)
: userSlides
slides = <HTMLElement[]>[].slice.call(customSlides || container.children)
}

function createEngine(options: OptionsType): EngineType {
function createEngine(
options: OptionsType,
container: HTMLElement,
slides: HTMLElement[]
): EngineType {
const engine = Engine(
root,
container,
slides,
ownerDocument,
ownerWindow,
options,
nodeHandler,
eventHandler,
watchHandler
)

if (options.loop && !engine.slideLooper.canLoop()) {
const optionsWithoutLoop = Object.assign({}, options, { loop: false })
return createEngine(optionsWithoutLoop)
return createEngine(optionsWithoutLoop, container, slides)
}
return engine
}
Expand All @@ -111,32 +104,51 @@ function EmblaCarousel(
): void {
if (destroyed) return

nodeHandler = NodeHandler(root, isSsr)
const { ownerWindow } = nodeHandler

optionsHandler.init(ownerWindow)
optionsBase = mergeOptions(optionsBase, withOptions)
options = optionsAtMedia(optionsBase)
pluginList = withPlugins || pluginList

storeElements()
const nodes = nodeHandler.getNodes(options)
container = nodes.container
slides = nodes.slides
engine = createEngine(options, container, slides)

engine = createEngine(options)
ssrHandler = SsrHandler(
isSsr,
container,
engine.axis,
nodeHandler,
optionsBase,
mergeOptions,
createEngine
)

optionsMediaQueries([
optionsBase,
...pluginList.map(({ options }) => options)
]).forEach((query) => mediaHandlers.add(query, 'change', reActivate))

if (!options.active) return
if (!ownerWindow) return
if (isSsr) return

engine.translate.to(engine.location.get())
engine.animation.init()
engine.slidesInView.init()
engine.slideFocus.init()
engine.resizeHandler.init()
engine.slidesHandler.init()
engine.animation.init(ownerWindow)
engine.resizeHandler.init(ownerWindow)
engine.slidesInView.init(ownerWindow)
engine.slidesHandler.init(ownerWindow)
engine.eventHandler.init(self)
engine.watchHandler.init(self)
engine.slideFocus.init()

if (engine.options.loop) engine.slideLooper.loop()
if (container.offsetParent && slides.length) engine.dragHandler.init()
if (container.offsetParent && slides.length) {
engine.dragHandler.init(ownerWindow)
}

pluginApis = pluginsHandler.init(self, pluginList)
}
Expand Down Expand Up @@ -180,6 +192,7 @@ function EmblaCarousel(
direction?: ScrollToDirectionType
): void {
if (!options.active || destroyed) return

engine.scrollBody
.useBaseFriction()
.useDuration(jump === true ? 0 : options.duration)
Expand Down Expand Up @@ -245,6 +258,10 @@ function EmblaCarousel(
return pluginApis
}

function ssrStyles(): string {
return ssrHandler.getStyles()
}

function internalEngine(): EngineType {
return engine
}
Expand Down Expand Up @@ -286,11 +303,12 @@ function EmblaCarousel(
slideNodes,
slidesInView,
slidesNotInView,
snapList
snapList,
ssrStyles
}

activate(userOptions, userPlugins)
setTimeout(() => eventHandler.emit('init', null), 0)
setTimeout(() => eventHandler.emit('init', null), 0) // TODO: Won't work in SSR
return self
}

Expand Down
Loading

0 comments on commit be65559

Please sign in to comment.