svelte-viewport-info
Version:
informs about viewport size and orientation
208 lines (168 loc) • 7.46 kB
text/typescript
//----------------------------------------------------------------------------//
// Svelte Viewport Info //
//----------------------------------------------------------------------------//
let MediaMatcher = (
window.matchMedia ||
// @ts-ignore
window['webkitMatchmedia'] || window['mozMatchmedia'] || window['oMatchmedia']
)
function MediaQuery (query:string):boolean {
return (MediaMatcher != null) && MediaMatcher(query).matches
}
function DocumentIsReady ():boolean {
return (
(document.readyState === 'interactive') ||
(document.readyState === 'complete')
)
}
/**** determineViewportSize ****/
// Internet Explorer and MS/Edge are NOT supported
let ViewportWidth:number = 0 // given in px, explicit initialization...
let ViewportHeight:number = 0 // ...is needed to satisfy the compiler
function determineViewportSize ():void {
ViewportWidth = Math.max(
document.documentElement.clientWidth || 0, window.innerWidth || 0
)
ViewportHeight = Math.max(
document.documentElement.clientHeight || 0, window.innerHeight || 0
)
}
// see https://stackoverflow.com/questions/1248081/get-the-browser-viewport-dimensions-with-javascript
determineViewportSize()
/**** determineScreenOrientation ****/
type Orientation = 'portrait'|'landscape'
type detailledOrientation = 'portrait-primary'|'portrait-secondary'|'landscape-primary'|'landscape-secondary'
let ScreenOrientation:Orientation|undefined = undefined
let detailledScreenOrientation:detailledOrientation|undefined = undefined
// explicit initialization is needed to satisfy compiler
function determineScreenOrientation ():void {
let Orientation
if ('orientation' in window.Screen) {
Orientation = window.screen.orientation.type
}
switch (Orientation) {
case 'portrait-primary':
case 'portrait-secondary':
ScreenOrientation = 'portrait'
detailledScreenOrientation = Orientation
break
case 'landscape-primary':
case 'landscape-secondary':
ScreenOrientation = 'landscape'
detailledScreenOrientation = Orientation
break
default:
switch (true) {
case MediaQuery('(orientation:portrait)'): ScreenOrientation = 'portrait'; break
case MediaQuery('(orientation:landscape)'):
case ViewportWidth > ViewportHeight: ScreenOrientation = 'landscape'; break
default: ScreenOrientation = 'portrait'
}
detailledScreenOrientation = undefined
}
if (DocumentIsReady()) {
document.body.classList.remove(
'Portrait','Landscape','Portrait-primary','Portrait-secondary',
'Landscape-primary','Landscape-secondary'
)
switch (ScreenOrientation) {
case 'portrait': document.body.classList.add('Portrait'); break
case 'landscape': document.body.classList.add('Landscape'); break
}
if (detailledScreenOrientation != null) {
const capitalized = (Name:string) => Name[0].toUpperCase() + Name.slice(1)
document.body.classList.add(capitalized(detailledScreenOrientation))
}
}
}
determineScreenOrientation()
if (! DocumentIsReady()) {
window.addEventListener('DOMContentLoaded', determineScreenOrientation)
} // after document is loaded, classes will be applied as foreseen
/**** handle problem that "orientationchange" is fired too soon ****/
let oldViewportWidth = ViewportWidth
let oldViewportHeight = ViewportHeight
let oldScreenOrientation:Orientation|undefined = ScreenOrientation
let oldDetailledScreenOrientation:detailledOrientation|undefined = detailledScreenOrientation
function rememberSettings ():void {
oldViewportWidth = ViewportWidth
oldViewportHeight = ViewportHeight
oldScreenOrientation = ScreenOrientation
oldDetailledScreenOrientation = detailledScreenOrientation
}
function submitEvents ():void {
if (! DocumentIsReady()) { return }
if ((oldViewportWidth !== ViewportWidth) || (oldViewportHeight !== ViewportHeight)) {
document.body.dispatchEvent(
new Event('viewportchanged', { bubbles:true, cancelable:true })
)
}
if (
(oldScreenOrientation !== ScreenOrientation) ||
(oldDetailledScreenOrientation !== detailledScreenOrientation)
) {
document.body.dispatchEvent(
new Event('orientationchangeend', { bubbles:true, cancelable:true })
)
}
}
let Poller:any // right now, it's difficult to determine the proper type
let PollCounter = 0
const PollCounterLimit = 10 // i.e., stop polling after 1000ms
function stopPolling ():void {
clearInterval(Poller)
Poller = undefined
PollCounter = 0
}
function pollForViewportAfterOrientationChange ():void {
Poller = setInterval(function () {
determineViewportSize()
if ( // no update of screen size yet? => continue polling
(oldViewportWidth === ViewportWidth) &&
(oldViewportHeight === ViewportHeight)
) {
PollCounter += 1
if (PollCounter <= PollCounterLimit) { return }
} // nota bene: sometimes viewport does not change (e.g., in iframe)
stopPolling()
determineScreenOrientation() // uses ViewportWidth/Height as fallback
submitEvents()
rememberSettings()
}, 100)
}
/**** handler for "orientationchange" event ****/
function determineViewportSizeAndScreenOrientation ():void {
determineViewportSize()
determineScreenOrientation() // uses screen_width/height as final fallback
if (Poller != null) { // we are still polling because of former event
stopPolling()
submitEvents()
rememberSettings()
}
if (
(oldViewportWidth === ViewportWidth) &&
(oldViewportHeight === ViewportHeight)
) { // screen size did not (yet) change => start polling for change
pollForViewportAfterOrientationChange()
} else { // viewport size changed in time => do not poll
submitEvents()
rememberSettings()
}
}
// see https://github.com/gajus/orientationchangeend
/**** update on changes ****/
window.addEventListener('orientationchange', function () {
setTimeout(determineViewportSizeAndScreenOrientation,10)
})// seen on iOS 12: "orientationchange" fired before orientation is updated
window.addEventListener('resize', determineViewportSizeAndScreenOrientation)
if ('orientation' in screen) {
screen.orientation.addEventListener('change', function () {
setTimeout(determineViewportSizeAndScreenOrientation,10)
})
}
export default {
get Width () { return ViewportWidth },
get Height () { return ViewportHeight },
get Orientation () { return ScreenOrientation },
get detailledOrientation () { return detailledScreenOrientation },
}