class BannerAnimation {
#index
el
items
duration = 4000
animationDuration = 420
constructor(props) {
const { start, ...args } = props
Object.assign(this, args)
this.#index = start || 1
if (!this.items || !this.items.length) {
this.items = [...this.el.children].map((c) => c.innerHTML)
}
;[...this.el.children].forEach((c) => c.remove())
this.appendChild(this.#index - 1)
this.el.style.setProperty('--dur', `${this.animationDuration}ms`)
Object.assign(this.el.style, {
display: 'block',
height: this.el.children[0].offsetHeight + 'px'
})
setTimeout(this.start, this.duration)
}
start = () => {
const div = this.appendChild(this.#index)
const first = this.el.children[0]
first.style.marginTop = `-${first.offsetHeight}px`
this.el.style.height = div.offsetHeight + 'px'
this.el.classList.add('animating')
setTimeout(this.end, this.animationDuration)
}
end = () => {
this.el.classList.remove('animating')
this.el.children[0].remove()
this.#index = (this.items.length + this.#index + 1) % this.items.length
setTimeout(this.start, this.duration)
}
appendChild = (i) => {
const div = document.createElement('div')
div.innerHTML = this.items[i]
this.el.appendChild(div)
return div
}
}
new BannerAnimation({
el: banner,
duration: 1000,
// items: [1, 2, 3]
})
#banner {
overflow: hidden;
transition: height 0ms cubic-bezier(.4, 0, .2, 1);
transition-duration: var(--dur);
}
#banner div:first-child {
margin-top: 0px
}
#banner div {
transition: margin-top 0ms cubic-bezier(.4, 0, .2, 1);
transition-duration: var(--dur);
}
<div id="banner">
<div>One</div>
<div>
Two
<div style="height: 100px; border: 1px solid red;"></div>
</div>
<div>Three</div>
<div>Four</div>
<div>Five<br>and a half...</div>
<div>Six</div>
<div>Seven</div>
</div>
<hr>