Skip to content

Commit

Permalink
fix: fake transition blocking events (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
paoloricciuti authored Jul 16, 2024
1 parent dda351c commit a84b46b
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 65 deletions.
2 changes: 1 addition & 1 deletion playground/src/Playground.vue
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ function commit() {
let timer: ReturnType<typeof setTimeout> | undefined
watch(
[theme, lang, code, duration, stagger, lineNumbers, rendererContainer, highlighter],
[theme, lang, code, duration, stagger, lineNumbers, rendererContainer, highlighter, loadingPromise],
(n, o) => {
if (n.every((v, i) => v === o[i]))
return
Expand Down
100 changes: 36 additions & 64 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,56 +107,27 @@ export class MagicMoveRenderer {
this.applyNodeStyle(this.container, step)
}

private checkContainerStyleChanged(step: KeyedTokensInfo) {
if (!this.options.containerStyle)
return false

// we need to clone the node and apply the properties because
// if you set the style of backgroundColor=#ffffff and try to access it via
// element.style.backgroundColor you get back rgb(255, 255, 255);
// this should also be true for other css properties so better be safe than sorry
const cloned = this.container.cloneNode() as HTMLElement

this.applyNodeStyle(cloned, step)

const bg = cloned.style.backgroundColor !== this.container.style.backgroundColor
const fg = cloned.style.color !== this.container.style.color
let rootStyle = false
if (step.rootStyle) {
const items = step.rootStyle.split(';')
for (const item of items) {
const [key, value] = item.split(':')
if (key && value) {
rootStyle = rootStyle || this.container.style.getPropertyValue(key.trim()) !== cloned.style.getPropertyValue(key.trim())
if (rootStyle)
break
}
}
}
return bg || fg || rootStyle
}

private registerTransitionEnd(el: HTMLElement, cb: () => void) {
let resolved = false
let resolve = () => { }
const promise = new Promise<void>((_resolve) => {
const finish = (e: TransitionEvent) => {
if (!e || e.target !== el)
return
resolve()
}
resolve = () => {
if (resolved)
return
resolved = true
el.removeEventListener('transitionend', finish)
cb()
_resolve()
}
el.addEventListener('transitionend', finish)
}) as PromiseWithResolve<void>
promise.resolve = resolve
return promise
return () => {
let resolved = false
let resolve = () => { }
const promise = Promise.race([
// wait for the finish of all animation and transitions on this element then invoke the callback
Promise.allSettled(el.getAnimations().map(animation => animation.finished)).then(() => cb()),
// race it with another promise so it's still resolvable separately
new Promise<void>((_resolve) => {
resolve = () => {
if (resolved)
return
resolved = true
cb()
_resolve()
}
}),
]) as PromiseWithResolve
promise.resolve = resolve
return promise
}
}

setCssVariables() {
Expand Down Expand Up @@ -213,7 +184,7 @@ export class MagicMoveRenderer {
const move: HTMLElement[] = []
const enter: HTMLElement[] = []
const leave: HTMLElement[] = []
const promises: PromiseWithResolve[] = []
const promises: (() => PromiseWithResolve)[] = []

this.previousPromises.forEach(p => p.resolve())
this.previousPromises = []
Expand Down Expand Up @@ -390,29 +361,30 @@ export class MagicMoveRenderer {
this.applyContainerStyle(step)
}
else {
if (this.checkContainerStyleChanged(step)) {
postReflow.push(() => {
this.container.classList.add(CLASS_CONTAINER_RESTYLE)
this.applyContainerStyle(step)
})

promises.push(
this.registerTransitionEnd(this.container, () => {
this.container.classList.remove(CLASS_CONTAINER_RESTYLE)
}),
)
}
postReflow.push(() => {
this.container.classList.add(CLASS_CONTAINER_RESTYLE)
this.applyContainerStyle(step)
})

promises.push(
this.registerTransitionEnd(this.container, () => {
this.container.classList.remove(CLASS_CONTAINER_RESTYLE)
}),
)
}
}

// Trigger reflow to apply the transform
forceReflow()

postReflow.forEach(cb => cb())
// we need to call the functions that return the promises after the reflow
// to to allow getAnimations to have the correct transitions
const actualPromises = promises.map(promise => promise())

this.isFirstRender = false
this.previousPromises = promises
return Promise.all(promises).then()
this.previousPromises = actualPromises
return Promise.all(actualPromises).then()
}
}

Expand Down

0 comments on commit a84b46b

Please sign in to comment.