【发布时间】:2015-10-03 06:30:40
【问题描述】:
离子模态带有slide-in-up的标准动画。能不能把动画改成fade-in?
【问题讨论】:
标签: angularjs animation ionic-framework
离子模态带有slide-in-up的标准动画。能不能把动画改成fade-in?
【问题讨论】:
标签: angularjs animation ionic-framework
为了为 Ionic Modal 添加自定义转换,我们将使用 Ionic Modal Options enterAnimation 和 leaveAnimationfrom ModalOptions 接口。对于一个模态有过渡状态:当我们关闭它时,模态的 On Enter 和模态的 On Leave。如果您查看 Ionic Modal 选项界面,您会发现 2 个选项可以为两种状态添加动画。
export interface ModalOptions {
showBackdrop?: boolean;
enableBackdropDismiss?: boolean;
enterAnimation?: string;
leaveAnimation?: string;
cssClass?: string;
}
我们将在模态中使用这些选项来指定我们使用来自ionic-angular 的动画类创建的过渡类。那么让我们来看看我们如何一步一步地创建和自定义动画。
为进入和离开创建2个转换类:
on-enter-translate.transition.ts
import { Animation, PageTransition } from 'ionic-angular';
export class ModalTranslateEnterTransition extends PageTransition {
public init() {
const ele = this.enteringView.pageRef().nativeElement;
const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
wrapper.beforeStyles({ 'transform': 'translateX(100%);', 'opacity': 1 });
wrapper.fromTo('transform', 'translateX(100%)', 'translateX(0)');
wrapper.fromTo('opacity', 1, 1);
this
.element(this.enteringView.pageRef())
.duration(500)
.easing('cubic-bezier(.1, .7, .1, 1)')
.add(wrapper);
}
}
on-leave-translate.transition.ts
import { Animation, PageTransition } from 'ionic-angular';
export class ModalTranslateLeaveTransition extends PageTransition {
public init() {
const ele = this.leavingView.pageRef().nativeElement;
const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
const contentWrapper = new Animation(this.plt, ele.querySelector('.wrapper'));
wrapper.beforeStyles({ 'transform': 'translateX(100%)', 'opacity': 1 });
wrapper.fromTo('transform', 'translateX(0)', 'translateX(100%)');
wrapper.fromTo('opacity', 1, 1);
contentWrapper.fromTo('opacity', 1, 0);
this
.element(this.leavingView.pageRef())
.duration(500)
.easing('cubic-bezier(.1, .7, .1, 1)')
.add(contentWrapper)
.add(wrapper);
}
}
然后在 app.module.ts
中导入这些模块export class AppModule {
constructor(public config: Config) {
this.setCustomTransitions();
}
private setCustomTransitions() {
this.config.setTransition('modal-translate-up-enter', ModalTranslateEnterTransition);
this.config.setTransition('modal-translate-up-leave', ModalTranslateLeaveTransition);
}
}
并使用以下选项创建模态:
var modal = this.modalCtrl.create(AddToCartModalPage, {
productId: this.productId,
skuId: this.skuId,
zipcode: this.zipcode,
sellerProfileId: this.sellerProfileId,
branchId: this.branchId,
changeSeller: this.changeSeller
}, {
showBackdrop: false,
enableBackdropDismiss: false,
cssClass: 'add-to-cart-modal',
enterAnimation: 'modal-translate-up-enter',
leaveAnimation: 'modal-translate-up-leave'
});
在此处查找我的文章的更多信息: Blog
在此处查找完整的演示存储库: Github
【讨论】:
您可以添加自己的动画 css,例如:
.slide-in-right {
-webkit-transform: translateX(100%);
transform: translateX(100%); }
.slide-in-right.ng-enter, .slide-in-right > .ng-enter {
-webkit-transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 400ms;
transition: all cubic-bezier(0.1, 0.7, 0.1, 1) 400ms; }
.slide-in-right.ng-enter-active, .slide-in-right > .ng-enter-active {
-webkit-transform: translateX(0);
transform: translateX(0); }
.slide-in-right.ng-leave, .slide-in-right > .ng-leave {
-webkit-transition: all ease-in-out 250ms;
transition: all ease-in-out 250ms; }
以及“向右滑动”的用法
“淡入”也一样
https://forum.ionicframework.com/t/slide-in-right-animation-for-ionicmodal/18882
【讨论】:
no ionic 不提供淡入动画。 但是你可以通过使用animate.css 来做到这一点
【讨论】:
你可以使用这个开源的准备好的动画:
Ionic v3 的模态/弹出框过渡类here
【讨论】:
import { AnimationController } from '@ionic/angular';
export const SwipeToCloseDefaults = {
MIN_PRESENTING_SCALE: 0.93,
};
export const enterFromRightAnimation = (baseEl, presentingEl) => {
const backdropAnimation = new AnimationController().create()
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 0.01, 'var(--backdrop-opacity)')
.beforeStyles({
'pointer-events': 'none'
})
.afterClearStyles(['pointer-events']);
const wrapperAnimation = new AnimationController().create()
.addElement(baseEl.querySelectorAll('.modal-wrapper, .modal-shadow'))
.beforeStyles({ 'opacity': 1 })
.fromTo('transform', 'translateX(100vh)', 'translateX(0vh)');
const baseAnimation = new AnimationController().create()
.addElement(baseEl)
.easing('cubic-bezier(0.32,0.72,0,1)')
.duration(500)
.addAnimation(wrapperAnimation);
if (presentingEl) {
const isMobile = window.innerWidth < 768;
const hasCardModal = (presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined);
const presentingAnimation = new AnimationController().create()
.beforeStyles({
'transform': 'translateX(0)',
'transform-origin': 'top center',
'overflow': 'hidden'
});
const bodyEl = document.body;
if (isMobile) {
/**
* Fallback for browsers that does not support `max()` (ex: Firefox)
* No need to worry about statusbar padding since engines like Gecko
* are not used as the engine for standlone Cordova/Capacitor apps
*/
const transformOffset = (!CSS.supports('width', 'max(0px, 1px)')) ? '30px' : 'max(30px, var(--ion-safe-area-top))';
const modalTransform = hasCardModal ? '-10px' : transformOffset;
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
const finalTransform = `translateX(${modalTransform}) scale(${toPresentingScale})`;
presentingAnimation
.afterStyles({
'transform': finalTransform
})
.beforeAddWrite(() => bodyEl.style.setProperty('background-color', 'black'))
.addElement(presentingEl)
.keyframes([
{ offset: 0, filter: 'contrast(1)', transform: 'translateX(0px) scale(1)', borderRadius: '0px' },
{ offset: 1, filter: 'contrast(0.85)', transform: finalTransform, borderRadius: '10px 10px 0 0' }
]);
baseAnimation.addAnimation(presentingAnimation);
}
else {
baseAnimation.addAnimation(backdropAnimation);
if (!hasCardModal) {
wrapperAnimation.fromTo('opacity', '0', '1');
}
else {
const toPresentingScale = (hasCardModal) ? SwipeToCloseDefaults.MIN_PRESENTING_SCALE : 1;
const finalTransform = `translateX(-10px) scale(${toPresentingScale})`;
presentingAnimation
.afterStyles({
'transform': finalTransform
})
.addElement(presentingEl.querySelector('.modal-wrapper'))
.keyframes([
{ offset: 0, filter: 'contrast(1)', transform: 'translateX(0) scale(1)' },
{ offset: 1, filter: 'contrast(0.85)', transform: finalTransform }
]);
const shadowAnimation =new AnimationController().create()
.afterStyles({
'transform': finalTransform
})
.addElement(presentingEl.querySelector('.modal-shadow'))
.keyframes([
{ offset: 0, opacity: '1', transform: 'translateX(0) scale(1)' },
{ offset: 1, opacity: '0', transform: finalTransform }
]);
baseAnimation.addAnimation([presentingAnimation, shadowAnimation]);
}
}
}
else {
baseAnimation.addAnimation(backdropAnimation);
}
return baseAnimation;
};
export const leaveToRightAnimation = (baseEl, presentingEl, duration = 500) => {
const backdropAnimation = new AnimationController().create()
.addElement(baseEl.querySelector('ion-backdrop'))
.fromTo('opacity', 'var(--backdrop-opacity)', 0.0);
const wrapperAnimation = new AnimationController().create()
.addElement(baseEl.querySelectorAll('.modal-wrapper, .modal-shadow'))
.beforeStyles({ 'opacity': 1 })
.fromTo('transform', 'translateX(0vh)', 'translateX(100vh)');
const baseAnimation = new AnimationController().create()
.addElement(baseEl)
.easing('cubic-bezier(0.32,0.72,0,1)')
.duration(duration)
.addAnimation(wrapperAnimation);
if (presentingEl) {
const isMobile = window.innerWidth < 768;
const hasCardModal = (presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined);
const presentingAnimation = new AnimationController().create()
.beforeClearStyles(['transform'])
.afterClearStyles(['transform'])
.onFinish(currentStep => {
// only reset background color if this is the last card-style modal
if (currentStep !== 1) {
return;
}
presentingEl.style.setProperty('overflow', '');
const numModals = Array.from(bodyEl.querySelectorAll('ion-modal')).filter(m => m.presentingElement !== undefined).length;
if (numModals <= 1) {
bodyEl.style.setProperty('background-color', '');
}
});
const bodyEl = document.body;
if (isMobile) {
const transformOffset = (!CSS.supports('width', 'max(0px, 1px)')) ? '30px' : 'max(30px, var(--ion-safe-area-top))';
const modalTransform = hasCardModal ? '-10px' : transformOffset;
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
const finalTransform = `translateX(${modalTransform}) scale(${toPresentingScale})`;
presentingAnimation
.addElement(presentingEl)
.keyframes([
{ offset: 0, filter: 'contrast(0.85)', transform: finalTransform, borderRadius: '10px 10px 0 0' },
{ offset: 1, filter: 'contrast(1)', transform: 'translateX(0px) scale(1)', borderRadius: '0px' }
]);
baseAnimation.addAnimation(presentingAnimation);
}
else {
baseAnimation.addAnimation(backdropAnimation);
if (!hasCardModal) {
wrapperAnimation.fromTo('opacity', '1', '0');
}
else {
const toPresentingScale = (hasCardModal) ? SwipeToCloseDefaults.MIN_PRESENTING_SCALE : 1;
const finalTransform = `translateX(-10px) scale(${toPresentingScale})`;
presentingAnimation
.addElement(presentingEl.querySelector('.modal-wrapper'))
.afterStyles({
'transform': 'translate3d(0, 0, 0)'
})
.keyframes([
{ offset: 0, filter: 'contrast(0.85)', transform: finalTransform },
{ offset: 1, filter: 'contrast(1)', transform: 'translateX(0) scale(1)' }
]);
const shadowAnimation = new AnimationController().create()
.addElement(presentingEl.querySelector('.modal-shadow'))
.afterStyles({
'transform': 'translateX(0) scale(1)'
})
.keyframes([
{ offset: 0, opacity: '0', transform: finalTransform },
{ offset: 1, opacity: '1', transform: 'translateX(0) scale(1)' }
]);
baseAnimation.addAnimation([presentingAnimation, shadowAnimation]);
}
}
}
else {
baseAnimation.addAnimation(backdropAnimation);
}
return baseAnimation;
};
【讨论】: