Created
July 30, 2020 19:25
-
-
Save wycats/a87b650d591863d3fc3668ec6661b3ba to your computer and use it in GitHub Desktop.
alternative observer design
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// observed popover is interested in changes to `width` and `height` relative to the | |
// page's origin, and normalized by rounding them to the nearest integer. | |
let observedPopover = new RectObserver( | |
popoverElement, | |
{ | |
width: Math.round, | |
height: Math.round | |
}, | |
{ | |
relativeTo: 'origin' // not viewport | |
} | |
); | |
// observedViewport is interested in changes to the top and height of the viewport, relative | |
// to the page's origin, and normalized by rounding. These changes can occur if the page scrolls | |
// or is resized. | |
let observedViewport = new ViewportObserver({ | |
top: Math.round, | |
height: Math.round | |
}); | |
// observedTarget is interested in changes to all four measurements, relative to the page's | |
// origin, and also normalized by rounding. | |
let observedTarget = new RectObserver(targetElement, { | |
top: Math.round, | |
left: Math.round, | |
width: Math.round, | |
height: Math.round | |
}, { | |
relativeTo: 'origin' | |
}); | |
// the beforePaint callback runs after the entire rendering pipeline, but before painting. Just like ResizeObserver, | |
// it may (and often will) result in a repetition of earlier steps in the pipeline. | |
// | |
// The callback runs if any of the specified coordinates have changed, after accounting for normalization. | |
Observer.all([observedPopover, observedViewport, observedTarget]).beforePaint((popover, viewport, target) => { | |
// do some math to compute the location of the popover | |
let { top, left } = compute(popover, viewport, target); | |
popover.target.style.top = `${top}px`; | |
popover.target.style.left = `${left}px`; | |
}); | |
function compute(popover, viewport, target) { | |
// ... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare const popoverElement: HTMLElement; | |
declare const targetElement: HTMLElement; | |
interface ObserverCoordinates { | |
top?: (value: number) => number; | |
left?: (value: number) => number; | |
width?: (value: number) => number; | |
height?: (value: number) => number; | |
} | |
interface ObserverOptions { | |
relativeTo: 'origin' | 'viewport' | 'offset-parent' | HTMLElement; | |
} | |
interface ObservedBox { | |
target: HTMLElement; | |
top: number; | |
left: number; | |
width: number; | |
height: number; | |
} | |
declare class RectObserver { | |
declare value: ObservedBox; | |
constructor( | |
element: HTMLElement, | |
coordinates: ObserverCoordinates, | |
options: ObserverOptions | |
); | |
} | |
declare class ViewportObserver { | |
declare value: ObservedBox; | |
constructor(coordinates: ObserverCoordinates); | |
} | |
type DOMObserver = RectObserver | ViewportObserver; | |
type ValueForDOMObservers<T extends readonly DOMObserver[]> = { | |
[P in keyof T]: P extends number ? T[P]["value"] : never; | |
}; | |
declare class Observer<T extends readonly DOMObserver[]> { | |
static all<T extends readonly DOMObserver[]>(observers: T): Observer<T>; | |
beforePaint(callback: (...args: ValueForDOMObservers<T>) => void): void; | |
} | |
let observedPopover = new RectObserver( | |
popoverElement, | |
{ | |
width: Math.round, | |
height: Math.round | |
}, | |
{ | |
relativeTo: 'origin' // not viewport | |
} | |
); | |
let observedViewport = new ViewportObserver({ | |
top: Math.round, | |
height: Math.round | |
}); | |
let observedTarget = new RectObserver(targetElement, { | |
top: Math.round, | |
left: Math.round, | |
width: Math.round, | |
height: Math.round | |
}, { | |
relativeTo: 'origin' | |
}); | |
Observer.all([observedPopover, observedViewport, observedTarget]).beforePaint((popover, viewport, target) => { | |
// do some math to compute the location of the popover | |
let { top, left } = compute(popover, viewport, target); | |
popover.target.style.top = `${top}px`; | |
popover.target.style.left = `${left}px`; | |
}); | |
declare function compute(popover: ObservedBox, viewport: ObservedBox, target: ObservedBox): { top: number, left: number }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment