Refactor #4682
parent
12333edd7c
commit
249cfbd68f
|
@ -23,6 +23,25 @@ export interface AnimateOnScrollOptions {
|
||||||
* AnimateOnScroll scroll to add when item begins to get hidden.
|
* AnimateOnScroll scroll to add when item begins to get hidden.
|
||||||
*/
|
*/
|
||||||
leaveClass?: string | undefined;
|
leaveClass?: string | undefined;
|
||||||
|
/**
|
||||||
|
* Specifies the `root` option of the IntersectionObserver API
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/IntersectionObserver/root)
|
||||||
|
*/
|
||||||
|
root?: Element | Document | null;
|
||||||
|
/**
|
||||||
|
* Specifies the `rootMargin` option of the IntersectionObserver API
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/IntersectionObserver/rootMargin)
|
||||||
|
*/
|
||||||
|
rootMargin?: string;
|
||||||
|
/**
|
||||||
|
* Specifies the `threshold` option of the IntersectionObserver API
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/IntersectionObserver/thresholds)
|
||||||
|
*/
|
||||||
|
threshold?: ReadonlyArray<number>;
|
||||||
|
/**
|
||||||
|
* Whether the `enterClass` animation will run if the target is in the viewport when the page is loaded.
|
||||||
|
*/
|
||||||
|
animateOnLoad?: boolean;
|
||||||
/**
|
/**
|
||||||
* Used to pass attributes to DOM elements inside the component.
|
* Used to pass attributes to DOM elements inside the component.
|
||||||
* @type {AnimateOnScrollDirectivePassThroughOptions}
|
* @type {AnimateOnScrollDirectivePassThroughOptions}
|
||||||
|
|
|
@ -2,57 +2,102 @@ import { DomHandler } from 'primevue/utils';
|
||||||
import BaseAnimateOnScroll from './BaseAnimateOnScroll';
|
import BaseAnimateOnScroll from './BaseAnimateOnScroll';
|
||||||
|
|
||||||
const AnimateOnScroll = BaseAnimateOnScroll.extend('animateonscroll', {
|
const AnimateOnScroll = BaseAnimateOnScroll.extend('animateonscroll', {
|
||||||
mounted(el, binding) {
|
created() {
|
||||||
el.setAttribute('data-pd-animateonscroll', true);
|
this.$value = this.$value || {};
|
||||||
!this.isUnstyled() && DomHandler.addClass(el, 'p-animate');
|
this.$el.style.opacity = this.$value.enterClass ? '0' : '';
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$el.setAttribute('data-pd-animateonscroll', true);
|
||||||
|
|
||||||
this.bindIntersectionObserver(el, binding);
|
this.bindIntersectionObserver();
|
||||||
},
|
},
|
||||||
unmounted(el) {
|
unmounted() {
|
||||||
this.unbindIntersectionObserver(el);
|
this.unbindAnimationEvents();
|
||||||
clearTimeout(this.timeout);
|
this.unbindIntersectionObserver();
|
||||||
},
|
},
|
||||||
timeout: null,
|
observer: undefined,
|
||||||
observer: null,
|
resetObserver: undefined,
|
||||||
|
isObserverActive: false,
|
||||||
|
animationState: undefined,
|
||||||
|
animationEndListener: undefined,
|
||||||
methods: {
|
methods: {
|
||||||
bindIntersectionObserver(el, binding) {
|
bindAnimationEvents() {
|
||||||
const options = {
|
if (!this.animationEndListener) {
|
||||||
root: null,
|
this.animationEndListener = () => {
|
||||||
rootMargin: '0px',
|
DomHandler.removeMultipleClasses(this.$el, [this.$value.enterClass, this.$value.leaveClass]);
|
||||||
threshold: 1.0
|
!this.$modifiers.once && this.resetObserver.observe(this.$el);
|
||||||
};
|
this.unbindAnimationEvents();
|
||||||
|
};
|
||||||
|
|
||||||
this.observer = new IntersectionObserver((element) => this.isVisible(element, el, binding), options);
|
this.$el.addEventListener('animationend', this.animationEndListener);
|
||||||
this.observer.observe(el);
|
|
||||||
},
|
|
||||||
isVisible(target, el, binding) {
|
|
||||||
const [intersectionObserverEntry] = target;
|
|
||||||
|
|
||||||
intersectionObserverEntry.isIntersecting ? this.enter(el, binding) : this.leave(el, binding);
|
|
||||||
},
|
|
||||||
enter(el, binding) {
|
|
||||||
el.style.visibility = 'visible';
|
|
||||||
DomHandler.addMultipleClasses(el, binding.value.enterClass);
|
|
||||||
|
|
||||||
binding.modifiers.once && this.unbindIntersectionObserver(el);
|
|
||||||
},
|
|
||||||
leave(el, binding) {
|
|
||||||
DomHandler.removeClass(el, binding.value.enterClass);
|
|
||||||
|
|
||||||
if (binding.value.leaveClass) {
|
|
||||||
DomHandler.addMultipleClasses(el, binding.value.leaveClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const animationDuration = el.style.animationDuration || 500;
|
|
||||||
|
|
||||||
this.timeout = setTimeout(() => {
|
|
||||||
el.style.visibility = 'hidden';
|
|
||||||
}, animationDuration);
|
|
||||||
},
|
},
|
||||||
unbindIntersectionObserver(el) {
|
bindIntersectionObserver() {
|
||||||
if (this.observer) {
|
const { root, rootMargin, threshold = 0.5 } = this.$value;
|
||||||
this.observer.unobserve(el);
|
const options = { root, rootMargin, threshold };
|
||||||
|
|
||||||
|
// States
|
||||||
|
this.observer = new IntersectionObserver(([entry]) => {
|
||||||
|
if (this.isObserverActive) {
|
||||||
|
if (entry.boundingClientRect.top > 0) {
|
||||||
|
entry.isIntersecting ? this.enter() : this.leave();
|
||||||
|
}
|
||||||
|
} else if (entry.isIntersecting) {
|
||||||
|
this.$value.animateOnLoad ? this.enter() : (this.$el.style.opacity = '');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isObserverActive = true;
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
setTimeout(() => this.observer.observe(this.$el), 0);
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
this.resetObserver = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
if (entry.boundingClientRect.top > 0 && !entry.isIntersecting) {
|
||||||
|
this.$el.style.opacity = this.$value.enterClass ? '0' : '';
|
||||||
|
DomHandler.removeMultipleClasses(this.$el, [this.$value.enterClass, this.$value.leaveClass]);
|
||||||
|
|
||||||
|
this.resetObserver.unobserve(this.$el);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.animationState = undefined;
|
||||||
|
},
|
||||||
|
{ ...options, threshold: 0 }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enter() {
|
||||||
|
if (this.animationState !== 'enter' && this.$value.enterClass) {
|
||||||
|
this.$el.style.opacity = '';
|
||||||
|
DomHandler.removeMultipleClasses(this.$el, this.$value.leaveClass);
|
||||||
|
DomHandler.addMultipleClasses(this.$el, this.$value.enterClass);
|
||||||
|
|
||||||
|
this.$modifiers.once && this.unbindIntersectionObserver(this.$el);
|
||||||
|
|
||||||
|
this.bindAnimationEvents();
|
||||||
|
this.animationState = 'enter';
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
leave() {
|
||||||
|
if (this.animationState !== 'leave' && this.$value.leaveClass) {
|
||||||
|
this.$el.style.opacity = this.$value.enterClass ? '0' : '';
|
||||||
|
DomHandler.removeMultipleClasses(this.$el, this.$value.enterClass);
|
||||||
|
DomHandler.addMultipleClasses(this.$el, this.$value.leaveClass);
|
||||||
|
|
||||||
|
this.bindAnimationEvents();
|
||||||
|
this.animationState = 'leave';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unbindAnimationEvents() {
|
||||||
|
if (this.animationEndListener) {
|
||||||
|
this.$el.removeEventListener('animationend', this.animationEndListener);
|
||||||
|
this.animationEndListener = undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unbindIntersectionObserver() {
|
||||||
|
this.observer?.unobserve(this.$el);
|
||||||
|
this.resetObserver?.unobserve(this.$el);
|
||||||
|
this.isObserverActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -94,7 +94,9 @@ const BaseDirective = {
|
||||||
$name: name,
|
$name: name,
|
||||||
$host: el,
|
$host: el,
|
||||||
$binding: binding,
|
$binding: binding,
|
||||||
$el: $prevInstance['$el'] || undefined,
|
$modifiers: binding?.modifiers,
|
||||||
|
$value: binding?.value,
|
||||||
|
$el: $prevInstance['$el'] || el || undefined,
|
||||||
$style: { classes: undefined, inlineStyles: undefined, loadStyle: () => {}, ...options?.style },
|
$style: { classes: undefined, inlineStyles: undefined, loadStyle: () => {}, ...options?.style },
|
||||||
$config: config,
|
$config: config,
|
||||||
/* computed instance variables */
|
/* computed instance variables */
|
||||||
|
|
|
@ -126,9 +126,21 @@ export default {
|
||||||
return -1;
|
return -1;
|
||||||
},
|
},
|
||||||
|
|
||||||
addMultipleClasses(element, className) {
|
addMultipleClasses(element, classNames) {
|
||||||
if (element && className) {
|
if (element && classNames) {
|
||||||
className.split(' ').forEach((style) => this.addClass(element, style));
|
[classNames]
|
||||||
|
.flat()
|
||||||
|
.filter(Boolean)
|
||||||
|
.forEach((cNames) => cNames.split(' ').forEach((className) => this.addClass(element, className)));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removeMultipleClasses(element, classNames) {
|
||||||
|
if (element && classNames) {
|
||||||
|
[classNames]
|
||||||
|
.flat()
|
||||||
|
.filter(Boolean)
|
||||||
|
.forEach((cNames) => cNames.split(' ').forEach((className) => this.removeClass(element, className)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ export declare class DomHandler {
|
||||||
static getViewport(): { width: number; height: number };
|
static getViewport(): { width: number; height: number };
|
||||||
static getOffset(el: HTMLElement): { top: any; left: any };
|
static getOffset(el: HTMLElement): { top: any; left: any };
|
||||||
static index(el: HTMLElement): number;
|
static index(el: HTMLElement): number;
|
||||||
static addMultipleClasses(el: HTMLElement, className: string): void;
|
static addMultipleClasses(el: HTMLElement, classNames: string | string[]): void;
|
||||||
|
static addRemoveClasses(el: HTMLElement, classNames: string | string[]): void;
|
||||||
static addClass(el: HTMLElement, className: string): void;
|
static addClass(el: HTMLElement, className: string): void;
|
||||||
static removeClass(el: HTMLElement, className: string): void;
|
static removeClass(el: HTMLElement, className: string): void;
|
||||||
static hasClass(el: HTMLElement, className: string): boolean;
|
static hasClass(el: HTMLElement, className: string): boolean;
|
||||||
|
|
Loading…
Reference in New Issue