精华内容
下载资源
问答
  • 区分旋转和翻转
    2021-05-26 12:15:16

    43890a928b76

    749674-89be3fa5b7608647.gif

    首页

    首页由正面和背面两张卡片组成, 同时, 设置点击事件flipCard.

    android:id="@+id/main_fl_container"

    xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:onClick="flipCard"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context="me.chunyu.spike.wcl_flip_anim_demo.MainActivity">

    layout="@layout/cell_card_back"/>

    layout="@layout/cell_card_front"/>

    逻辑, 初始化动画和镜头距离.

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    ButterKnife.bind(this);

    setAnimators(); // 设置动画

    setCameraDistance(); // 设置镜头距离

    }

    逻辑, 初始化动画和镜头距离.

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    ButterKnife.bind(this);

    setAnimators(); // 设置动画

    setCameraDistance(); // 设置镜头距离

    }

    动画

    初始化右出(RightOut)和左入(LeftIn)动画, 使用动画集合AnimatorSet.

    当右出动画开始时, 点击事件无效, 当左入动画结束时, 点击事件恢复.

    // 设置动画

    private void setAnimators() {

    mRightOutSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.anim_out);

    mLeftInSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.anim_in);

    // 设置点击事件

    mRightOutSet.addListener(new AnimatorListenerAdapter() {

    @Override public void onAnimationStart(Animator animation) {

    super.onAnimationStart(animation);

    mFlContainer.setClickable(false);

    }

    });

    mLeftInSet.addListener(new AnimatorListenerAdapter() {

    @Override public void onAnimationEnd(Animator animation) {

    super.onAnimationEnd(animation);

    mFlContainer.setClickable(true);

    }

    });

    }

    右出动画

    android:duration="@integer/anim_length"

    android:propertyName="rotationY"

    android:valueFrom="0"

    android:valueTo="180"/>

    android:duration="0"

    android:propertyName="alpha"

    android:startOffset="@integer/anim_half_length"

    android:valueFrom="1.0"

    android:valueTo="0.0"/>

    旋转180°, 当旋转一半时, 卡片消失.

    左入动画

    android:duration="0"

    android:propertyName="alpha"

    android:valueFrom="1.0"

    android:valueTo="0.0"/>

    android:duration="@integer/anim_length"

    android:propertyName="rotationY"

    android:valueFrom="-180"

    android:valueTo="0"/>

    android:duration="0"

    android:propertyName="alpha"

    android:startOffset="@integer/anim_half_length"

    android:valueFrom="0.0"

    android:valueTo="1.0"/>

    在开始时是隐藏, 逆向旋转, 当旋转一半时, 显示卡片.

    镜头视角

    改变视角, 涉及到旋转卡片的Y轴, 即rotationY, 需要修改视角距离.

    如果不修改, 则会超出屏幕高度, 影响视觉体验.

    // 改变视角距离, 贴近屏幕

    private void setCameraDistance() {

    int distance = 16000;

    float scale = getResources().getDisplayMetrics().density * distance;

    mFlCardFront.setCameraDistance(scale);

    mFlCardBack.setCameraDistance(scale);

    }

    旋转控制

    设置右出和左入动画的目标控件, 两个动画同步进行, 并区分正反面朝上.

    // 翻转卡片

    public void flipCard(View view) {

    // 正面朝上

    if (!mIsShowBack) {

    mRightOutSet.setTarget(mFlCardFront);

    mLeftInSet.setTarget(mFlCardBack);

    mRightOutSet.start();

    mLeftInSet.start();

    mIsShowBack = true;

    } else { // 背面朝上

    mRightOutSet.setTarget(mFlCardBack);

    mLeftInSet.setTarget(mFlCardFront);

    mRightOutSet.start();

    mLeftInSet.start();

    mIsShowBack = false;

    }

    }

    更多相关内容
  • # 获取时间戳,用于区分图像 now = time.time() tail_time = str(round(now * 1000000))[-4:] # 时间戳尾数 head_time = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time())) # 时间标签 label = str...

    元学习论文总结||小样本学习论文总结

    2017-2019年计算机视觉顶会文章收录 AAAI2017-2019 CVPR2017-2019 ECCV2018 ICCV2017-2019 ICLR2017-2019 NIPS2017-2019

    一:日志依赖

    https://blog.csdn.net/weixin_41803874/article/details/81201807

     

    数据操作完整源码自 vieo.zhu@foxmail.com 申请

    二:代码实现

    from diagnose_logging import Logger
    from PIL import Image
    import os
    
    # 声明日志
    log = Logger('img_pre.py')
    logger = log.getlog()
    
    
    class ImgPre:
        def __init__(self, rootPath, export_path_base):
            self.rootPath = rootPath  # 图像完整路径
            self.export_path_base = export_path_base
    
            # 创建输出根目录
            try:
                if not os.path.exists(export_path_base):
                    os.mkdir(export_path_base)
            except Exception as e:
                logger.error(e)
            logger.info('ImgPre: %s', rootPath)
    
        def get_savename(self, operate):
            """
            :param export_path_base: 图像输出路径
            :param operate: 脸部区域名
    
            :return: 返回图像存储名
            """
            try:
                import time
                # 获取时间戳,用于区分图像
                now = time.time()
                tail_time = str(round(now * 1000000))[-4:]  # 时间戳尾数
                head_time = time.strftime("%Y%m%d%H%M%S", time.localtime(time.time()))
                # 时间标签
                label = str(head_time + tail_time)
    
                # 输出文件夹
                export_path_base = self.export_path_base
                # 子文件夹以“操作operate”命名
                out_path = export_path_base + operate
                # 创建子文件夹
                if not os.path.exists(out_path):
                    os.mkdir(out_path)
    
                # 存储完整路径
                savename = out_path + '/' + operate + '_' + label + ".jpg"
    
                # 日志
                logger.info('save:%s', savename)
                return savename
    
            except Exception as e:
                logger.error('get_savename ERROR')
                logger.error(e)
    
        def lightness(self, light):
            """改变图像亮度.
            推荐值:
                0.87,1.07
            明亮程度
                darker < 1.0 <lighter
            """
            try:
                operate = 'lightness_' + str(light)
                # 图像完整路径
                rootPath = self.rootPath
    
                with Image.open(rootPath) as image:
                    # 图像左右翻转
                    out = image.point(lambda p: p * light)
                    # 重命名
                    savename = self.get_savename(operate)
                    # 图像存储
                    out.save(savename)
    
                # 日志
                # logger.info(operate)
            except Exception as e:
                logger.error('ERROR %s', operate)
                logger.error(e)
    
    
        def rotate(self, angle):
            """图像旋转15度、30度."""
            try:
                operate = 'rotate_' + str(angle)
                # 图像完整路径
                rootPath = self.rootPath
    
                with Image.open(rootPath) as image:
                    # 图像左右翻转
                    out = image.rotate(angle)
                    # 重命名
                    savename = self.get_savename(operate)
                    # 图像存储
                    out.save(savename, quality=100)
    
                # 日志
                # logger.info(operate)
            except Exception as e:
                logger.error('ERROR %s', operate)
                logger.error(e)
    
        def transpose(self):
            """图像左右翻转操作."""
            try:
                operate = 'transpose'
                # 图像完整路径
                rootPath = self.rootPath
    
                with Image.open(rootPath) as image:
                    # 图像左右翻转
                    out = image.transpose(Image.FLIP_LEFT_RIGHT)
                    # 重命名
                    savename = self.get_savename(operate)
                    # 图像存储
                    out.save(savename, quality=100)  # quality=100
    
                # 日志
                # logger.info(operate)
            except Exception as e:
                logger.error('ERROR %s', operate)
                logger.error(e)
    
        def deform(self):
            """图像拉伸."""
            try:
                operate = 'deform'
                # 图像完整路径
                rootPath = self.rootPath
    
                with Image.open(rootPath) as image:
                    w, h = image.size
                    w = int(w)
                    h = int(h)
                    # 拉伸成宽为w的正方形
                    out_ww = image.resize((int(w), int(w)))
                    savename = self.get_savename(operate + '_ww')
                    out_ww.save(savename, quality=100)
                    # 拉伸成宽为h的正方形
                    out_ww = image.resize((int(h), int(h)))
                    savename = self.get_savename(operate + '_hh')
                    out_ww.save(savename, quality=100)
    
                # 日志
                # logger.info(operate)
            except Exception as e:
                logger.error('ERROR %s', operate)
                logger.error(e)
    
        def crop(self):
            """提取四个角落和中心区域."""
            try:
                operate = 'crop'
                # 图像完整路径
                rootPath = self.rootPath
    
                with Image.open(rootPath) as image:
                    w, h = image.size
                    # 切割后尺寸
                    scale = 0.875
                    # 切割后长宽
                    ww = int(w * scale)
                    hh = int(h * scale)
                    # 图像起点,左上角坐标
                    x = y = 0
    
                    # 切割左上角
                    x_lu = x
                    y_lu = y
                    out_lu = image.crop((x_lu, y_lu, ww, hh))
                    savename = self.get_savename(operate + '_lu')
                    out_lu.save(savename, quality=100)
                    # logger.info(operate + '_lu')
    
                    # 切割左下角
                    x_ld = int(x)
                    y_ld = int(y + (h - hh))
                    out_ld = image.crop((x_ld, y_ld, ww, hh))
                    savename = self.get_savename(operate + '_ld')
                    out_ld.save(savename, quality=100)
                    # logger.info(operate + '_ld')
    
                    # 切割右上角
                    x_ru = int(x + (w - ww))
                    y_ru = int(y)
                    out_ru = image.crop((x_ru, y_ru, w, hh))
                    savename = self.get_savename(operate + '_ru')
                    out_ru.save(savename, quality=100)
                    # logger.info(operate + '_ru')
    
                    # 切割右下角
                    x_rd = int(x + (w - ww))
                    y_rd = int(y + (h - hh))
                    out_rd = image.crop((x_rd, y_rd, w, h))
                    savename = self.get_savename(operate + '_rd')
                    out_rd.save(savename, quality=100)
                    # logger.info(operate + '_rd')
    
                    # 切割中心
                    x_c = int(x + (w - ww) / 2)
                    y_c = int(y + (h - hh) / 2)
                    out_c = image.crop((x_c, y_c, ww, hh))
                    savename = self.get_savename(operate + '_c')
                    out_c.save(savename, quality=100)
                    # logger.info('提取中心')
            except Exception as e:
                logger.error('ERROR %s', operate)
                logger.error(e)
    
    
    def test():
        # 源地址和输出地址
        rootPath = 'E:/face/1.jpg'
        export_path_base = 'E:/face/image/'
        # 声明类对象
        imgPre = ImgPre(rootPath, export_path_base)
    
        imgPre.deform()
        imgPre.transpose()
        imgPre.rotate(15)
        imgPre.rotate(30)
        imgPre.crop()
        imgPre.lightness(1.07)
        imgPre.lightness(0.87)
        
    
    if __name__ == '__main__':
        import datetime
        print('start...')
        # 计时
        start_time = datetime.datetime.now()
    
        test()
    
        end_time = datetime.datetime.now()
        time_consume = (end_time - start_time).microseconds / 1000000
    
        logger.info('start_time: %s', start_time)
        logger.info('end_time: %s', end_time)
        logger.info('time_consume: %s(s)', time_consume)  # 0.280654(s)
    
        logger.info('main finish')
    

     

    展开全文
  • (2)置换及循环节数的计算方法:对于有n个位置的手镯,有n种旋转置换n种翻转置换.  对于旋转置换: c(fi) = gcd(n,i) i为一次转过i颗宝石( i = 0 时 c=n;);  对于翻转置换:如果n为偶数:c(f) = n/2 的置换有n...
     

    Description

    Beads of red, blue or green colors are connected together into a circular necklace of n beads ( n < 24 ). If the repetitions that are produced by rotation around the center of the circular necklace or reflection to the axis of symmetry are all neglected, how many different forms of the necklace are there? 

     


    Input

    The input has several lines, and each line contains the input data n. 
    -1 denotes the end of the input file. 

     

    Output

    The output should contain the output data: Number of different forms, in each line correspondent to the input data.

     

    Sample Input

    4
    5
    -1

     

    Sample Output

    21
    39

     

    Source

     

    1、题目类型:Polya定理、组合数学、置换群。

    2、解题思路:Polya定理:(1)设G是p个对象的一个置换群,用k种颜色突然这p个对象,若一种染色方案在群G的作用下变为另一种方案,则这两个方案当作是同一种方案,这样的不同染色方案数为:

    (2)置换及循环节数的计算方法:对于有n个位置的手镯,有n种旋转置换和n种翻转置换.
                                              对于旋转置换: c(fi) = gcd(n,i)  i为一次转过i颗宝石( i = 0 时 c=n;);
                                              对于翻转置换:如果n为偶数:c(f) = n/2 的置换有n/2个; 
                                                                                c(f) = n/2+1 的置换有n/2个;
                                                               如果n为奇数:c(f) = n/2+1.

    3、注意事项:注意对于翻转置换过程中对于奇偶数情况的区分处理。

     

    相同的gcd合并在一起计算:

     

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<map>
     5 #include<set>
     6 #include<vector>
     7 using namespace std;
     8 #define ll long long
     9 ll pow_mod(ll a,ll i){
    10     if(i==0)
    11         return 1;
    12     ll t=pow_mod(a,i/2);
    13     ll ans=t*t;
    14     if(i&1)
    15         ans=ans*a;
    16     return ans;
    17 }
    18 
    19 
    20 
    21 vector<ll> divisor(ll n){
    22     vector<ll> res;
    23     for(ll i=1;i*i<=n;i++){
    24         if(n%i==0){
    25             res.push_back(i);
    26             if(i*i!=n){
    27                 res.push_back(n/i);
    28             }
    29         }
    30     }
    31     return res;
    32 }
    33 ll eular(ll n){
    34     ll res=1;
    35     for(ll i=2;i*i<=n;i++){
    36         if(n%i==0){
    37             n/=i,res*=i-1;
    38             while(n%i==0){
    39                 n/=i;
    40                 res*=i;
    41             }
    42         }
    43     }
    44     if(n>1) res*=n-1;
    45     return res;
    46 }
    47 ll polya(ll m,ll n){
    48     //map<ll,ll> primes = prime_factor(n);
    49     vector<ll> divs = divisor(n);
    50     ll res=0;
    51     for(ll i=0;i<divs.size();i++){
    52         ll euler=eular(divs[i]);
    53         res+=euler*pow_mod(m,n/divs[i]);
    54     }
    55     res/=n;
    56     return res;
    57 }
    58 int main()
    59 {
    60     ll n,m=3;
    61     while(~scanf("%I64d",&n) && n!=-1){
    62         if(n==0){
    63             puts("0");
    64             continue;
    65         }
    66         ll count=polya(m,n)*n;//旋转情况 
    67         if(n&1){//奇数 
    68             count+=n*pow_mod(m,n/2+1);//翻转情况 
    69         }
    70         else{//偶数 
    71             count += (pow_mod(m, n / 2 + 1) + pow_mod(m, n / 2)) * (n / 2);//翻转情况 
    72         }
    73         count/=2*n;
    74         printf("%I64d\n",count);
    75     }
    76     return 0;
    77 }
    View Code

     

     

    附上大神代码:

    相同的gcd合并在一起计算:

      1 #include <iostream>
      2 #include <map>
      3 #include <vector>
      4 using namespace std;
      5  
      6 #define LL long long
      7  
      8 inline LL power(LL p, LL n)
      9 {
     10     LL sum = 1;
     11     while (n)
     12     {
     13         if (n & 1)
     14             sum *= p;
     15         p *= p;
     16         n /= 2;
     17     }
     18     return sum;
     19 }
     20  
     21 //************************************
     22 // Method:    divisor
     23 // FullName:  divisor
     24 // Access:    public 
     25 // Returns:   vector<int> 约数
     26 // Qualifier: 约数枚举
     27 // Parameter: const int & n 目标数n
     28 //************************************
     29 vector<int> divisor(const int& n)
     30 {
     31     vector<int> res;
     32     for (int i = 1; i * i <= n; ++i)
     33     {
     34         if (n % i == 0)
     35         {
     36             res.push_back(i);
     37             if (i != n / i)
     38             {
     39                 res.push_back(n / i);
     40             }
     41         }
     42     }
     43  
     44     return res;
     45 }
     46  
     47 //************************************
     48 // Method:    prime_factor
     49 // FullName:  prime_factor
     50 // Access:    public 
     51 // Returns:   map<int, int>
     52 // Qualifier: 整数分解
     53 // Parameter: int n
     54 //************************************
     55 map<int, int> prime_factor(int n)
     56 {
     57     map<int, int> res;
     58     for (int i = 2; i * i <= n; ++i)
     59     {
     60         while (n % i == 0)
     61         {
     62             ++res[i];
     63             n /= i;
     64         }
     65     }
     66     if (n != 1)
     67     {
     68         res[n] = 1;
     69     }
     70     return res;
     71 }
     72  
     73 LL polya(const int& m, const int& n)
     74 {
     75     map<int, int> primes = prime_factor(n);
     76     vector<int> divs = divisor(n);
     77     LL res = 0;
     78     for (int i = 0; i < divs.size(); ++i)
     79     {
     80         // 求divs[i]的欧拉函数值
     81         LL euler = divs[i];
     82         for (map<int, int>::iterator it = primes.begin(); it != primes.end(); ++it)
     83         {
     84             int p = it->first;
     85             if (divs[i] % p == 0) euler = euler / p * (p - 1);
     86         }
     87  
     88         res += euler * power(m, n / divs[i]);
     89     }
     90  
     91     // 最后除以n
     92     res /= n;
     93     return res;
     94 }
     95  
     96 ///SubMain//
     97 int main(int argc, char *argv[])
     98 {
     99 #ifndef ONLINE_JUDGE
    100     freopen("in.txt", "r", stdin);
    101     freopen("out.txt", "w", stdout);
    102 #endif
    103     int n; const LL m = 3;
    104     while (~scanf("%d", &n) && n != -1)
    105     {
    106         if (n == 0)
    107         {
    108             puts("0");
    109             continue;
    110         }
    111  
    112         LL count = polya(m, n) * n;
    113         if (n & 1)
    114             count += n * power(m, n / 2 + 1);
    115         else
    116             count += (power(m, n / 2 + 1) + power(m, n / 2)) * (n / 2);
    117         count /= 2 * n;
    118         printf("%lld\n", count);
    119     }
    120 #ifndef ONLINE_JUDGE
    121     fclose(stdin);
    122     fclose(stdout);
    123     system("out.txt");
    124 #endif
    125     return 0;
    126 }
    View Code

     

    还有一种暴力求法:

     1 #include <iostream>
     2 using namespace std;
     3  
     4 #define LL long long
     5  
     6 int gcd(int a, int b)
     7 {
     8     return b == 0 ? a : gcd(b, a % b);
     9 }
    10  
    11 LL power(LL p, LL n)
    12 {
    13     LL sum = 1;
    14     while (n)
    15     {
    16         if (n & 1)
    17             sum *= p;
    18         p *= p;
    19         n /= 2;
    20  
    21     }
    22     return sum;
    23 }
    24  
    25 ///SubMain//
    26 int main(int argc, char *argv[])
    27 {
    28 #ifndef ONLINE_JUDGE
    29     freopen("in.txt", "r", stdin);
    30     freopen("out.txt", "w", stdout);
    31 #endif
    32     int n; const LL m = 3;
    33     while (~scanf("%d", &n) && n != -1)
    34     {
    35         if (n == 0)
    36         {
    37             puts("0");
    38             continue;
    39         }
    40         LL count = 0;
    41         for (int i = 1; i <= n; ++i)
    42             count += power(m, gcd(i, n));
    43         if (n & 1)
    44             count += n * power(m, n / 2 + 1);
    45         else
    46             count += n / 2 * (power(m, n / 2 + 1) + power(m, n / 2));
    47         count /= n * 2;
    48         printf("%lld\n", count);
    49     }
    50 #ifndef ONLINE_JUDGE
    51     fclose(stdin);
    52     fclose(stdout);
    53     system("out.txt");
    54 #endif
    55     return 0;
    56 }
    View Code

     

    展开全文
  • Where to go —— 如何为转场增加手势交互 如果是一个VC present 到另一个VC,那么就要实现UIViewControllerTransitioningDelegate中的两个方法,UIViewControllerAnimatedTransitioning一样,分别对应 present ...

    这是自定义转场系列的第四篇。由于具有一定的连续性,我会忽略一些基础,所以如果你是第一次看这个系列,可以先过目之前的几篇 ——— UIViewControllerTransitioning的用法 、实现Keynote中的神奇移动效果、实现通过圆圈放大缩小的转场动画。

    老规矩,先端上GIF。

    How to work

    首先在StoryBoard上拖两个UIViewController。并且在第一个VC上放一个button,使用Action Segue连接到第二个VC。

    然后回到代码界面。和以往一样,我们需要创建两个文件:一个用于从第一个VC过渡到第二个VC的动画(如push),另一个这是第二个过渡到第一个VC的动画(如pop)。这里不得不说iOS7中引入的这种解耦合的方式,它的意义在于无论在哪儿需要用到转场动画的地方,直接把这两个文件扔过去就行了。

    我们创建两个文件:KYPushTransition 和 KYPopTransition 。从名字可以看出,后一个是前一个的反转动画。其实,我们完全可以把两个文件写在一起:KYTransition 。因为两个文件的代码结构几乎别无二致,不同的地方也只要用布尔值区分一下就行了。但这里为了让介绍思路清晰,我们把两个动画分开来实现。

    首先是KYPushTransition。

    先继承 UIViewControllerAnimatedTransitioning 协议。实现下面两个方法:

    - (NSTimeInterval)transitionDuration:(id )transitionContext{

    //动画的时间

    return 0.6f;

    }

    - (void)animateTransition:(id )transitionContext{

    //动画的逻辑

    ...

    }

    下面具体介绍动画的逻辑。

    - (void)animateTransition:(id )transitionContext{

    //1

    FirstViewController *fromVC = (FirstViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    SecondViewController *toVC = (SecondViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    UIView *fromView = fromVC.view;

    UIView *toView = toVC.view;

    UIView *containerView = [transitionContext containerView];

    [containerView addSubview:toView];

    [containerView sendSubviewToBack:toView];

    //2

    CATransform3D transform = CATransform3DIdentity;

    transform.m34 = -0.002;

    containerView.layer.sublayerTransform = transform;

    //3

    CGRect initialFrame = [transitionContext initialFrameForViewController:fromVC];

    fromView.frame = initialFrame;

    toView.frame = initialFrame;

    //4

    [self updateAnchorPointAndOffset:CGPointMake(0.0, 0.5) view:fromView];

    //5

    CAGradientLayer *gradient = [CAGradientLayer layer];

    gradient.frame = fromView.bounds;

    gradient.colors = @[(id)[UIColor colorWithWhite:0.0 alpha:0.5].CGColor,

    (id)[UIColor colorWithWhite:0.0 alpha:0.0].CGColor];

    gradient.startPoint = CGPointMake(0.0, 0.5);

    gradient.endPoint = CGPointMake(0.8, 0.5);

    UIView *shadow = [[UIView alloc]initWithFrame:fromView.bounds];

    shadow.backgroundColor = [UIColor clearColor];

    [shadow.layer insertSublayer:gradient atIndex:1];

    shadow.alpha = 0.0;

    [fromView addSubview:shadow];

    //6

    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{

    //旋转fromView 90度

    fromView.layer.transform = CATransform3DMakeRotation(-M_PI_2, 0, 1.0, 0);

    shadow.alpha = 1.0;

    } completion:^(BOOL finished) {

    //7

    fromView.layer.anchorPoint = CGPointMake(0.5, 0.5);

    fromView.layer.position = CGPointMake(CGRectGetMidX([UIScreen mainScreen].bounds), CGRectGetMidY([UIScreen mainScreen].bounds));

    fromView.layer.transform = CATransform3DIdentity;

    [shadow removeFromSuperview];

    [transitionContext completeTransition:YES];

    }];

    }

    解释一下。

    1)通过上下文transitionContext获得前后两个UIView,这也是发生动画的具体对象。同时还需要获得containerView,这也是动画发生的地方。我们需要把后一个视图添加上去。为了保证后一个视图加上去之后不遮住前一个视图的动画,我们还要把后一个视图放到最后:[containerView sendSubviewToBack:toView];

    2)为了保证视图产生3D的效果,我们需要设置layer的仿射变换。关于仿射变化和m34的概念,推荐一篇博客:iOS的三维透视投影。

    3)为fromView、toView设置初始frame。

    4)重置锚点。锚点就是视图旋转时候的中心,就是那个不动的点。关于锚点以及position的关系,你可以参考这一篇解释:这将是你最后一次纠结position与anchorPoint!。所以我们在设置了锚点的之后,还需要把layer的position也设置到相应位置:

    -(void)updateAnchorPointAndOffset:(CGPoint)anchorPoint view:(UIView *)view{

    view.layer.anchorPoint = anchorPoint;

    view.layer.position = CGPointMake(0, CGRectGetMidY([UIScreen mainScreen].bounds));

    }

    方便记忆,你可以理解锚点会吸附到position上。所以光改变锚点不改变position,那么结果就是锚点确实改了,但是position还是在默认的(0.5,0.5),也就是视图中心。就像这样:

    5)给fromView增加左深右浅的阴影。并且一开始的透明度为0,随着翻转的角度变大过渡到1。

    6)开始动画。这这里,我们让fromView翻转90度 fromView.layer.transform = CATransform3DMakeRotation(-M_PI_2, 0, 1.0, 0);。这里要注意向外90度是-M_PI_2,y为1.0表示绕着y轴旋转。

    7)动画结束,我们需要还原锚点的位置、恢复position的位置、恢复layer的transform为CATransform3DIdentity,并且把阴影层移除。由于一开始我没有没有恢复锚点和position的位置,而且一直没找到原因。知道我查看了视图的层级结构才恍然大悟:

    可见,使用控制台的”Debug View Hierarchy“

    是多么有用!

    当然,还有最重要最关键的一步:[transitionContext completeTransition:YES];。告诉上下文,动画已经完成。如果你这里不这么做,你将无法从后一个视图返回前一个视图。

    好了,至此已经完成所有push动画逻辑。实现pop的逻辑基本无二。

    KYPopTransition

    1、首先就是删除[containerView sendSubviewToBack:toView];,这时我们可不想让动画躲在后面。

    2、第二个不同点,需要加上

    //让toView的截图旋转90度

    toView.layer.transform = CATransform3DMakeRotation(-M_PI_2, 0.0, 1.0, 0.0);

    因为这时动画的起始应该先保持在90度的位置,然后慢慢过渡到0度。

    3、阴影层的需要加在toView上,并且起始透明度应该为1,终止时为0。

    How to Use

    1、如果你是一个VC present 到另一个VC,那么FirstViewController和SecondViewController都需要继承协议是UIViewControllerTransitioningDelegate。

    然后在FirstViewController中设置代理。

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{

    SecondViewController *secVC = (SecondViewController *)segue.destinationViewController;

    secVC.transitioningDelegate = self;

    [super prepareForSegue:segue sender:sender];

    }

    并实现协议的两个方法:分别对应present和dismiss。

    - (id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{

    KYPushTransition *flip = [KYPushTransition new];

    return flip;

    }

    - (id )animationControllerForDismissedController:(UIViewController *)dismissed{

    KYPopTransition *flip = [KYPopTransition new];

    return flip;

    }

    2、如果你用UINavigationController去控制两个VC,此时如果你什么都不做,segue会自动变成标准的导航栏推进的push、pop。要使用我们自定义的动画,需要让FirstViewController继承协议UINavigationControllerDelegate,然后设置代理为自己self.navigationController.delegate = self;。

    实现UINavigationControllerDelegate中的相关协议方法,只需要一个:

    - (id )navigationController:(UINavigationController *)navigationController

    animationControllerForOperation:(UINavigationControllerOperation)operation

    fromViewController:(UIViewController *)fromVC

    toViewController:(UIViewController *)toVC{

    if (operation == UINavigationControllerOperationPush) {

    KYPushTransition *flip = [KYPushTransition new];

    return flip;

    }else if (operation == UINavigationControllerOperationPop){

    KYPopTransition *flip = [KYPopTransition new];

    return flip;

    }else{

    return nil;

    }

    }

    搞定,现在就运行了。

    Where to go —— 如何为转场增加手势交互

    如果是一个VC present 到另一个VC,那么就要实现UIViewControllerTransitioningDelegate中的两个方法,和UIViewControllerAnimatedTransitioning一样,分别对应 present 和 dismiss的动画。

    - (id )interactionControllerForPresentation:(id )animator;

    - (id )interactionControllerForDismissal:(id )animator;

    这时,你可能已经被各种协议代理名字搞晕了,是的,我一开始也晕了,但只要多练几次还是能就熟悉的。

    既然我们之前是创建了两个文件继承UIViewControllerAnimatedTransitioning来实现过渡动画,那么是不是也应该继承UIViewControllerInteractiveTransitioning来实现百分比交互动画呢?因为在图中,两者的关系是并列的。是的,你可以这么做。但是苹果提供了一个更好的类 ———— UIPercentDrivenInteractiveTransition。

    顾名思义,我们就可以猜到这个类就是专门用来实现手势百分比交互的。怎么使用呢?

    为了遵循解耦合,我们新建一个UIPercentDrivenInteractiveTransition的子类 —— KYPopInteractiveTransition。

    创建一个方法-(void)addPopGesture:(UIViewController *)viewController;,用来给目标视图控制器添加一个边缘滑动手势:

    -(void)addPopGesture:(UIViewController *)viewController{

    presentedVC = viewController;

    UIScreenEdgePanGestureRecognizer *edgeGes = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(edgeGesPan:)];

    edgeGes.edges = UIRectEdgeLeft;

    [viewController.view addGestureRecognizer:edgeGes];

    }

    实现相应的手势方法:

    -(void)edgeGesPan:(UIScreenEdgePanGestureRecognizer *)edgeGes{

    //1

    CGFloat translation =[edgeGes translationInView:presentedVC.view].x;

    CGFloat percent = translation / (presentedVC.view.bounds.size.width);

    percent = MIN(1.0, MAX(0.0, percent));

    NSLog(@"%f",percent);

    switch (edgeGes.state) {

    case UIGestureRecognizerStateBegan:{

    //2

    self.interacting = YES;

    [presentedVC dismissViewControllerAnimated:YES completion:nil];

    //如果是navigationController控制,这里应该是[presentedVC.navigationController popViewControllerAnimated:YES];

    break;

    }

    case UIGestureRecognizerStateChanged:{

    //3

    [self updateInteractiveTransition:percent];

    break;

    }

    case UIGestureRecognizerStateEnded:{

    //4

    self.interacting = NO;

    if (percent > 0.5) {

    [self finishInteractiveTransition];

    }else{

    [self cancelInteractiveTransition];

    }

    break;

    }

    default:

    break;

    }

    }

    1)计算手指在X轴方向上的偏移距离,与屏幕的宽度的之比保存为一个百分比。也就是说,当手指划过屏幕的距离超过屏幕宽度的1/2,那么剩下的动画就自动完成;否则,取消动画。这里用了MIN和MAX把百分比始终控制在了0~1之间。

    2)滑动开始,指定要执行的操作。这里因为没有使用UINavigation控制两个VC,所以是dismissViewControllerAnimated:。如果是用UINavigation去控制的,那么这里相应的应该是navigationController popViewControllerAnimated:。 self.interacting的作用稍后揭晓。

    3)在UIGestureRecognizerStateChanged 调用 [self updateInteractiveTransition:percent]。这里我们把刚才的百分比传了过去,系统就可以通过这个0~1的竖数值实时改变动画的进度。

    4)当UIGestureRecognizerStateEnded的时候,我们需要判断此时手指是否划过屏幕大于一半的距离。如果大于一半,告诉系统完成:[self finishInteractiveTransition]; 反之,告诉系统取消操作:[self cancelInteractiveTransition],这时动画也将返回初始位置。

    特别注意,当我们使用了手势百分比交互,在相应的动画逻辑KYPopTransition中,把原来的[transitionContext completeTransition:YES] 改成 [transitionContext completeTransition:![transitionContext transitionWasCancelled]]。如果一直是YES的话,当我们手指划过小于屏幕一半,即使系统知道是取消动画,但在上下文中依然是写死的YES。

    使用手势百分比交互

    在FirstViewController中的-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender里面,创建一个KYPopInteractiveTransition的实例并把SecondVC传过去:

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{

    ...

    popInteractive = [KYPopInteractiveTransition new];

    [popInteractive addPopGesture:secVC];

    ...

    }

    然后实现UIViewControllerTransitioningDelegate里的这个关于手势百分比交互的方法:

    - (id )interactionControllerForDismissal:(id )animator{

    return popInteractive.interacting ? popInteractive : nil;

    }

    好了,这里你发现我们使用了popInteractive.interacting来判断,还记得之前买的关子吗?self.interacting的作用就在这里。因为- (id )interactionControllerForDismissal:(id )animator 这个方法在 点击dismiss和滑动dismiss时候都会调用。然而如果只是return popInteractive;的话,当我们点击dismiss的时候,程序将不会做出反应。所以,我们需要区分,到底是点击dismiss还是滑动dismiss。因此,我们需要一个布尔值来判断,就是这样。

    这个系列应该差不多到这里就结束了。本篇的源码你可以在这里获得。

    有任何疑问,欢迎在下方评论区域留言:D

    展开全文
  • Android屏幕旋转

    2021-05-26 09:02:02
    很久没有写文章了,找个时间整理下Android简单的屏幕旋转的生命周期正确的竖屏锁定设置生命周期启动Activity会执行如下方法:onCreate –> onStart –> onResume之后旋转屏幕,则Activity会被销毁并重新创建...
  • rotateY(Ydeg):沿着y轴正向旋转Y度 transform:rotateZ(Zdeg):沿着z轴正向旋转Z度 transform:rotate3d(x,y,z,deg):沿着自定义轴旋转deg为旋转度数 对于元素的旋转方向也是正负之分的,那么如何区分元素旋转方向的...
  • 其余参考Android目录.CardAndroid在Android设计中, 经常会使用卡片元素, 正面显示图片或主要信息, 背面显示详细内容, 如网易有道词典的单词翻转和海底捞的食谱展示. 实现卡片视图非常容易, 那么如何实现翻转动画呢?...
  • 第1016期 脑科学日报2021年5月15日科学时讯1,Nature | 于杰等揭示海马体中AMPAR复合物的组成以及特异性拮抗剂的抑制机理海马体AMPAR复合物组成的卡通示意图中枢神经系统的正常功能是兴奋神经元抑制神经元共同发挥...
  • 主要为大家介绍了一款基于css3麻将筛子3D翻转特效的实例教程,css3使我们能够跳出2d空间,实现3维空间的动画效果,这里给出一个自动翻转的3d色子动画效果制作过程
  • 理解欧拉角,区分ControllerRotationActorRotation
  • 3D图像旋转简单的ITK Python(3D Image Rotation Simple ITK Python)我正在尝试使用Simple ITK旋转3D图像。 这是我的代码: imagetoresize是原始图像。 图像大小为(512,512,149)targetimage = imagetoresizeorigin ...
  • 杨净 发自 凹非寺 量子位 报道 | 公众号 QbitAI过去现在的边界,到底在哪里?人类,又是如何在时间混沌中区分出过往与当下的?注意,这不是一个哲学问题。(手动狗头)而是科学家们的最...
  • 题目:单词翻转。输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序...思路:先将整个句子翻转,然后再通过空格区分每个单词的范围,再将这个单词翻转。#include#include#includevoid RotateSentence(ch...
  • 本篇文章主要介绍移动端/手机端图片的旋转、压缩、剪裁、上传 这个功能的实现已经好了几次方案流程了,对最终...图片选取功能直接用input(其实也可以用antd的upload,但是产品要求区分拍照上传/相册上传,所以只能用in
  • 实现卡片翻转的动画效果

    万次阅读 多人点赞 2016-02-27 20:12:59
    在Android设计中, 经常会使用卡片元素, 正面显示图片或主要信息, 背面显示详细内容, 如网易有道词典的单词翻转和海底捞的食谱展示. 实现卡片视图非常容易, 那么如何实现翻转动画呢? 在TB吃海底捞时, 使用Pad点餐, ...
  • 下了一个可以旋转屏幕角度的功能,当时用的是RK3128测试的,杠杠的,代码如下 /** * 获取屏幕的旋转角度 * * @param context * @return */ public static int getScreenRoate(Context context) { try { i...
  • 为简单起见,标点符号普通字母一样处理。例如输入字符串"I am a student.",则输出"student. a am I"。 这道题很多次公司拿来作为面试题 ,可以通过两次翻转字符串来实现 思路: 第一步翻转句子中所有的字符。...
  • css3使我们能够跳出2d空间,实现3维空间的动画效果,这里给出一个自动翻转的3d色子动画效果制作过程。第一步,首先进行HTML的布局,对于3D效果,布局有一定的规律,代码如下:XML/HTML Code复制内容到剪贴板...........
  • 翻转子串

    2019-05-02 10:03:42
    题目描述 假定我们都知道非常高效的算法来检查一个单词是否为其他字符串的子串。请将这个算法编写成一个...字符串中字符为英文字母空格,区分大小写,字符串长度小于等于1000。 测试样例: "Hello world","wo...
  • 假设需要围绕x轴做旋转,那么我们将x轴的正向指向我们自己,然后再看顺时针逆时针,顺时针为正的角度,逆时针为负的角度。 注意,坐标轴是这样的: 注意Y轴是朝下的,Z轴是垂直屏幕向外的! ...
  • 点击上方“计算机视觉工坊”,选择“星标”干货第一时间送达导读本文对 CVPR 2021 检测大类中的“伪装目标检测”、“旋转目标检测”领域的论文进行了盘点,将会依次阐述每篇论文的方法思路...
  • cocos2d-x实现3d翻转区分正反面

    千次阅读 2014-02-18 22:45:04
    实现原理 通过OrbitCamera实现立体翻转,当精灵翻转到90度时切换精灵纹理 代码解析 1.创建一个精灵,设定精灵的userdata用于确定切换图片 auto poker=Sprite::...2.创建一个重复动作,包含一个3d旋转动作调用
  • 15 }, } }, tooltip: { formatter: function (node) { // 区分连线节点,节点上额外显示其他数字 if (!node.value) { return node.data.name; } else { return node.data.name + ":" + node.data.showNum; } }, },...
  • css3使我们能够跳出2d空间,实现3维空间的动画效果,这里给出一个自动翻转的3d色子动画效果制作过程。第一步,首先进行HTML的布局,对于3D效果,布局有一定的规律,代码如下:.....................在body中定义一个...
  • 翻转字串

    2020-02-27 20:21:19
    题目描述 假定我们都知道非常高效的算法来检查一个单词是否为其他字符串的子串。...字符串中字符为英文字母空格,区分大小写,字符串长度小于等于1000。 测试样例: “Hello world”,"worldhe...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,853
精华内容 1,941
关键字:

区分旋转和翻转