@i-is-as-i-does/valva
Version:
Vâlvă is a small JS library for elegant transitions.
233 lines (207 loc) • 6.11 kB
JavaScript
/* Vâlvă | (c) 2021-22 I-is-as-I-does | MIT License */
/* eslint-disable no-unused-expressions */
import { deflt, profl } from './constants.js'
import { resetDisplay, isHidden } from './utils.js'
function setTransition(elm, type, duration, timing) {
elm.style.setProperty('transition-property', profl[type].target)
elm.style.setProperty('transition-timing-function', timing)
elm.style.setProperty('transition-duration', duration + 'ms')
}
function resetStyle(elm, type) {
profl[type].toReset.forEach(p => {
elm.style.setProperty(p, 0)
})
}
function removeProp(elm, type, key) {
profl[type][key].forEach(p => {
elm.style.removeProperty(p)
})
}
function changeThenAct(parent, child, placeAction, placeCallBack) {
if (!(parent instanceof Element)) {
parent = document.body
}
var tmpclass = 'm' + Math.random().toString(20).substring(2)
child.classList.add(tmpclass)
return new Promise((resolve) => {
var observer = new MutationObserver(() => {
if (parent.querySelector('.' + tmpclass)) {
child.classList.remove(tmpclass)
observer.disconnect()
placeCallBack()
resolve(true)
}
})
observer.observe(parent, {
childList: true
})
placeAction()
})
}
export function vHide(elm, type = deflt.type, duration = deflt.duration, callback = null, timing = deflt.timing) {
setTransition(elm, type, duration, timing)
if (type !== 'fade') {
elm.style.setProperty('box-sizing', 'border-box')
elm.style.setProperty('height', elm.offsetHeight + 'px')
elm.offsetHeight
elm.style.setProperty('overflow', 'hidden')
}
resetStyle(elm, type)
window.setTimeout(() => {
elm.style.setProperty('display', 'none')
removeProp(elm, type, 'removeOut')
if (typeof callback === 'function') {
callback()
}
}, duration)
}
export function vShow(elm, type = deflt.type, duration = deflt.duration, callback = null, timing = deflt.timing) {
if (type === 'fade') {
resetStyle(elm, 'fade')
resetDisplay(elm)
setTransition(elm, 'fade', duration, timing)
} else {
resetDisplay(elm)
var height = elm.offsetHeight
elm.style.setProperty('overflow', 'hidden')
resetStyle(elm, type)
elm.offsetHeight
elm.style.setProperty('box-sizing', 'border-box')
setTransition(elm, type, duration, timing)
elm.style.setProperty('height', height + 'px')
}
removeProp(elm, type, 'removeInFirst')
window.setTimeout(() => {
removeProp(elm, type, 'removeInThen')
if (typeof callback === 'function') {
callback()
}
}, duration)
}
export function vToggle(elm, type = deflt.type, duration = deflt.duration, callback = null, timing = deflt.timing) {
if (isHidden(elm)) {
vShow(elm, type, duration, callback, timing)
} else {
vHide(elm, type, duration, callback, timing)
}
}
export function vTempToggle(elm, type = deflt.type, delay = deflt.delay, duration = deflt.duration, callback = null, timing = deflt.timing) {
var methods = [vHide, vShow]
if (isHidden(elm)) {
methods.reverse()
}
var transcallback = function () {
if (typeof callback === 'function') {
callback()
}
window.setTimeout(() => {
methods[1](elm, type, duration, callback, timing)
}, delay)
}
methods[0](elm, type, duration, transcallback, timing)
}
export function vToggleResolve(
elm,
callback = null,
type = deflt.type,
duration = deflt.duration,
timing = deflt.timing,
reverse = false
) {
if (typeof callback !== 'function') {
callback = function () { }
}
var methods = [vHide, vShow]
if (reverse) {
methods.reverse()
}
methods[0](elm, type, duration, function () {
Promise.resolve(callback()).then(() => methods[1](elm, type, duration, null, timing))
}, timing)
}
export function vPlace(
parent,
child,
prepend = false,
type = deflt.type,
duration = deflt.duration,
callback = null,
timing = deflt.timing
) {
child.style.setProperty('display', 'none')
var placeAction
if (prepend) {
placeAction = function () {
parent.prepend(child)
}
} else {
placeAction = function () {
parent.append(child)
}
}
var placeCallback = function () {
vShow(child, type, duration, callback, timing)
}
changeThenAct(parent, child, placeAction, placeCallback)
}
export function vReplace(oldElm, newElm, duration = deflt.duration, callback = null, timing = deflt.timing) {
newElm.style.opacity = 0
var parent = oldElm.parentNode
var preh = oldElm.offsetHeight
var placeAction = function () {
oldElm.replaceWith(newElm)
}
var placeCallback = function () {
vShowAdapt(newElm, preh, duration, callback, timing)
}
var transcallback = function () {
changeThenAct(parent, newElm, placeAction, placeCallback)
}
vHide(oldElm, 'fade', duration, transcallback, timing)
}
export function vShowAdapt(elm, prevHeight, duration = deflt.duration, callback = null, timing = deflt.timing) {
var type = 'ease'
if (elm.offsetHeight === prevHeight) {
type = 'fade'
}
vShow(elm, type, duration, callback, timing)
}
export function vSplitFlap(elm, text, speed = 20) {
var ntext = elm.textContent.split('')
var stext = text.split('')
var prevLen = ntext.length
var newLen = stext.length
var l
var stop
var solve
if (prevLen > newLen) {
l = prevLen
stop = 0
solve = function () {
if (l > newLen) {
ntext.pop()
} else {
ntext[l - 1] = stext[l - 1]
}
l--
}
} else {
l = 0
stop = newLen
solve = function () {
if (l < prevLen) {
ntext[l] = stext[l]
} else {
ntext.push(stext[l])
}
l++
}
}
var repl = setInterval(function () {
solve()
elm.textContent = ntext.join('')
if (l === stop) {
clearInterval(repl)
}
}, speed)
}