【技巧】ionic3如何实现优雅的弹窗动画

关于这篇文章,很早前就准备写了,只是内容属于我了解的非官方资料,怕有Bug风险误导别人,又怕我表述不清楚一直没动笔,后来群里有人专门找我问这个,那我还是写一下,仅作为参考……

image.png

在了解弹窗动画前,我们先了解下CSS3中动画的基本内容:

CSS3 transition 属性

描述

transition-property

规定设置过渡效果的 CSS 属性的名称。

transition-duration

规定完成过渡效果需要多少秒或毫秒。

transition-timing-function

规定速度效果的速度曲线。

transition-delay

定义过渡效果何时开始。

其中transition-timing-function 属性规定过渡效果的速度曲线,曲线函数说明如下表所示:

描述

linear

规定以相同速度开始至结束的过渡效果(等于 cubic-bezier(0,0,1,1))。

ease

规定慢速开始,然后变快,然后慢速结束的过渡效果(cubic-bezier(0.25,0.1,0.25,1))。

ease-in

规定以慢速开始的过渡效果(等于 cubic-bezier(0.42,0,1,1))。

ease-out

规定以慢速结束的过渡效果(等于 cubic-bezier(0,0,0.58,1))。

ease-in-out

规定以慢速开始和结束的过渡效果(等于 cubic-bezier(0.42,0,0.58,1))。

cubic-bezier(n,n,n,n)

在 cubic-bezier 函数中定义自己的值。可能的值是 0 至 1 之间的数值。

我比较了这几个效果,感觉没那么明显,建议点transition-timing-function此连接执行下里面实例感受下效果。


无事不登三宝殿,介绍到Transition,自然是因为ionic应用到它,而我这里主要说的是模态窗口modal。首先模态窗口内置了4个transitions: modal-transitions.ts,截取部分代码如下:

/**
 * Animations for modals
 */
export class ModalSlideIn extends PageTransition {
  init() {
    super.init();
    const ele: HTMLElement = this.enteringView.pageRef().nativeElement;
    const backdropEle = ele.querySelector('ion-backdrop');
    const backdrop = new Animation(this.plt, backdropEle);
    const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));

    wrapper.beforeStyles({ 'opacity': 1 });
    wrapper.fromTo('translateY', '100%', '0%');

    backdrop.fromTo('opacity', 0.01, 0.4);

    this
      .element(this.enteringView.pageRef())
      .easing('cubic-bezier(0.36,0.66,0.04,1)')
      .duration(400)
      .add(backdrop)
      .add(wrapper);
  }
}

然后配置是在该源文件:modal-impl.ts,截取关键代码如下:

config.setTransition('modal-slide-in', ModalSlideIn);
config.setTransition('modal-slide-out', ModalSlideOut);
config.setTransition('modal-md-slide-in', ModalMDSlideIn);
config.setTransition('modal-md-slide-out', ModalMDSlideOut);

从上述两份源代码可以分析得出自定义PageTransition的步骤:

1、实现一个继承PageTransition的类; 2、把该类添加到配置中。

那具体怎么实现这个类?在应用中配置和源码配置有哪些差异?配置完后怎么用呢?可以看看小军此文:ionic2实战-自定义modal过渡动画

但是小军这篇文章有个风险的,它少了个基类的方法,我可以基本肯定他看了此链接:Ionic 2 Modal animations and custom animations?

我们应该点进去了解提供方法的作者:onderceylan,可以看到有意思的是,该作者回复了几个类似的问题:

image.png

再其中,包含上述链接在内,最有价值的链接是这个: Adding custom transitions/custom modal transition,里面有人针对风险做了回复。是的,结合源码,以及常规继承的原理,我们补充调用下基类的init方法:

 super.init();

最后,基于我先前提出的几个问题,小军博客和上述链接都说明的比较清楚了,我不再说明,只是补充解析一下几个点: 1、下面部分完全是CSS3的transition内容,我们可以根据自己想要的效果来修改,其中easing里的方法个人觉得差不多,可以随便挑个用:

      .element(this.enteringView.pageRef())
      .easing('cubic-bezier(0.36,0.66,0.04,1)')
      .duration(400)

2、下面部分是CSS3的transform内容,同样可以按需修改:

wrapper.fromTo('translateY', '100%', '0%');

3、其它点,如wrapper可以调整它的透明度、宽度、高度等样式:

 wrapper.beforeStyles({ 'opacity': 1 });

4、基于上述内容,提供简单示范代码如下:

import { Animation, PageTransition } from 'ionic-angular';

/**
 * write by IT_晴天(woodstream)
 */
export class ModalFromLeftEnter extends PageTransition {
    public init() {
        super.init();
        const ele = this.enteringView.pageRef().nativeElement;
        const backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop'));
        backdrop.beforeStyles({ 'z-index': 0, 'opacity': 0.3, 'visibility': 'visible' });
        const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
        wrapper.fromTo('transform', 'translateX(-100%)', 'translateX(0)');
        const contentWrapper = new Animation(this.plt, ele.querySelector('ion-content.content'));
        contentWrapper.beforeStyles({ 'width': '80%' });
        this
            .element(this.enteringView.pageRef())
            .duration(300)
            .easing('ease')
            .add(backdrop)
            .add(wrapper)
            .add(contentWrapper);
    }
}

export class ModalFromLeftLeave extends PageTransition {
    public init() {
        super.init();
        const ele = this.leavingView.pageRef().nativeElement;
        const backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop'));
        backdrop.beforeStyles({ 'visibility': 'hidden' });
        const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
        wrapper.fromTo('transform', 'translateX(0)', 'translateX(-100%)');
        this
            .element(this.leavingView.pageRef())
            .duration(300)
            .easing('ease')
            .add(backdrop)
            .add(wrapper);
    }
}

export class ModalFromRightEnter extends PageTransition {
    public init() {
        super.init();
        const ele = this.enteringView.pageRef().nativeElement;
        const backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop'));
        backdrop.beforeStyles({ 'z-index': 0, 'opacity': 0.3, 'visibility': 'visible' });
        const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
        wrapper.fromTo('transform', 'translateX(100%)', 'translateX(20%)');
        const contentWrapper = new Animation(this.plt, ele.querySelector('ion-content.content'));
        contentWrapper.beforeStyles({ 'width': '80%' });
        this
            .element(this.enteringView.pageRef())
            .duration(300)
            .easing('ease')
            .add(backdrop)
            .add(wrapper)
            .add(contentWrapper);
    }
}

export class ModalFromRightLeave extends PageTransition {
    public init() {
        super.init();
        const ele = this.leavingView.pageRef().nativeElement;
        const backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop'));
        backdrop.beforeStyles({ 'visibility': 'hidden' });
        const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
        wrapper.fromTo('transform', 'translateX(20%)', 'translateX(100%)');
        this
            .element(this.leavingView.pageRef())
            .duration(300)
            .easing('ease')
            .add(backdrop)
            .add(wrapper);
    }
}

export class ModalFromTopEnter extends PageTransition {
    public init() {
        super.init();
        const ele = this.enteringView.pageRef().nativeElement;
        const backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop'));
        backdrop.beforeStyles({ 'z-index': 0, 'opacity': 0.3, 'visibility': 'visible' });
        const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
        wrapper.fromTo('transform', 'translateY(-100%)', 'translateY(0)');
        const contentWrapper = new Animation(this.plt, ele.querySelector('ion-content.content'));
        contentWrapper.beforeStyles({ 'height': '60%' });
        this
            .element(this.enteringView.pageRef())
            .duration(300)
            .easing('ease')
            .add(backdrop)
            .add(wrapper)
            .add(contentWrapper);
    }
}

export class ModalFromTopLeave extends PageTransition {
    public init() {
        super.init();
        const ele = this.leavingView.pageRef().nativeElement;
        const backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop'));
        backdrop.beforeStyles({ 'visibility': 'hidden' });
        const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
        wrapper.fromTo('transform', 'translateY(0)', 'translateY(-100%)');
        this
            .element(this.leavingView.pageRef())
            .duration(300)
            .easing('ease')
            .add(backdrop)
            .add(wrapper);
    }
}

export class ModalScaleEnter extends PageTransition {
    public init() {
        super.init();
        const ele = this.enteringView.pageRef().nativeElement;
        const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
        wrapper.fromTo('transform', 'scale(0)', 'scale(1)');

        this
            .element(this.enteringView.pageRef())
            .duration(400)
            .easing('ease')
            .add(wrapper);
    }
}

export class ModalScaleLeave extends PageTransition {
    public init() {
        super.init();
        const ele = this.leavingView.pageRef().nativeElement;
        const wrapper = new Animation(this.plt, ele.querySelector('.modal-wrapper'));
        wrapper.fromTo('transform', 'scale(1)', 'scale(0)');

        this
            .element(this.leavingView.pageRef())
            .duration(400)
            .easing('ease')
            .add(wrapper);
    }
}

最后上个效果图:

animate.gif

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我和我大前端的故事

我不知道你知不知道我知道的伪元素小技巧

伪元素能做什么?我们要他有何用?它能为我们解决什么问题?和其他的方法相比她有什么有点?我们为什么要使用它?

972
来自专栏Android先生

自定义view实现超萌动感小炸弹

Hello,小伙伴们,我回来了。这些日子有的小伙伴问我怎么没有更新了。这个其实是有原因,首先,最近有点忙。其次没有看到什么觉得好玩的动画!最后,就是我更新过了!...

1132
来自专栏向治洪

html5 jqueryrotate插件实现旋转动画

CSS3 提供了多种变形效果,比如矩阵变形、位移、缩放、旋转和倾斜等等,让页面更加生动活泼有趣,不再一动不动。然后 IE10 以下版本的浏览器不支持 CSS3...

3586
来自专栏coding for love

CSS入门9-定位机制

(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

693
来自专栏小狼的世界

Silverlight学习笔记:布局之stackpanel

在用户界面设计方面的竞争多半是围绕着如何制作出有吸引力、实用、灵活的用户界面而展开的。在基于浏览器的应用中进行界面的设计则是一项更加需要技巧的工作,因为我们的客...

682
来自专栏小狼的世界

em是否到了退出的时候

今天看到 YUI CSS 框架中的时,看到用了其用来表示宽度的时候,很多地方都用了 em,在调整字体大小的时候,用了百分比。官方的说法是这样的:

702
来自专栏葡萄城控件技术团队

给萌新的Flexbox简易入门教程

近几年,CSS领域出现了一些复杂的专用布局工具,用以代替原有的诸如使用表格、浮动和绝对定位之类的各种变通方案。Flexbox,或者说是弹性盒子布局模块(Flex...

922
来自专栏IMWeb前端团队

CSS3 object-fit和object-position

本文作者:IMWeb 郭明慧 原文出处:IMWeb社区 未经同意,禁止转载 最近一直忙于将JavaScript学习的笔记整理成电子书,也没什么空闲时间...

2255
来自专栏菩提树下的杨过

水平/垂直都居中的div

用css样式使div水平/垂直居中,兼容于目前各种主流浏览器 思路: 用绝对定位将div左顶点,定位于浏览器正中心,然后再利用负的margin,将div...

2449
来自专栏青蛙要fly的专栏

Android技能树 — View小结

这次是相对View做个小结,主要是View的工作原理,绘制流程等。为什么要总结这块,因为平时自定义View的情况多多少少都会遇到,如果能深刻了解这块知识,对自定...

3172

扫码关注云+社区

领取腾讯云代金券