6

使用 AngularJS 动画实现翻转效果的最佳方法是什么?

我希望在点击时发生翻转效果。每次单击它时,它都应该翻转到另一侧。

理想情况下,我想,我正在寻找一个使用 Angular 动画的指令实现。

4

8 回答 8

13

PLNKR - 这是一个提供 3d 翻转功能的可配置角度指令的种子。我看不出有什么好的理由使用ngAnimate它。

基本用法

<flip flip-width="200px" flip-height="100px">
   <flip-panel>
     content-front
   </flip-panel>
   <flip-panel>
     content-back
   </flip-panel>
</flip>

注释

  1. 它自己附加 css 样式,以完全独立。
  2. 在适当的通用名称中,directive所有名称都应该是可配置的。
  3. flip-widthflip-height设置风格flip和两者flip-panels
  4. 指令进行一些基本检查,如果两者frontback设置了。
  5. 第一个flip-panelfront,第二个是back
  6. 由于使用的transclusion内容flip-panel可能是任意的html。(你说得对,Misha 不需要嵌入)
  7. 它仅适用于-webkit. (更新以使其在 Firefox 中工作,只需复制所有-webkit不带前缀的部分 - 你不需要-moz

更新

PLNKR - 这是一个更新和扩展的版本。它显示了我通过使指令可配置的意思。详细信息:

  1. flipConfig通过引入provider,允许设置app.config
    • 默认尺寸
    • css 类名
    • 过渡速度
    • 如果翻转动作是通过点击面板触发的
  2. 引入flip-show的属性指定显示哪一面。
  3. 更改flip-show我们可以从指令外部触发翻转动作。
  4. 它适用于 Firefox 和 IE11 中的 [几乎:-)]。

(顺便说一句:它只是一个种子,可以通过很多方式进行改进。例如:指定轴,指定变换的原点,指定面板的半径和边距,允许悬停翻转,默认颜色,边距等)

于 2014-04-17T17:04:23.477 回答
7

我最近有一个相同的用例,用于角度记忆游戏。

我的实现与其他答案的想法相同。我还发布了翻转代码和一个DEMO

Github:https ://github.com/zwacky/angular-flippy

Ps:看来我迟到了;)

于 2014-04-18T12:00:06.130 回答
2

单击翻转容器时,您可以使用ng-clickand添加一个类。ng-class

<div class="flip-container" ng-click="flip = !flip" ng-class="{'flip': flip}">
  <div class="flipper">
    <div class="front" style="background: lightblue;">
      front
    </div>
    <div class="back" style="background: lightgreen;">
      back
    </div>
  </div>
</div>

这本质上是执行Walsh在他的文章中建议的角度方式:

将翻转类添加到容器元素将使用 JavaScript 翻转卡片——无需用户悬停。像 document.querySelector("#myCard").classList.toggle("flip") 这样的 JavaScript 注释会进行翻转!

对David Walsh 的 css的唯一更改是删除了:hover选择器 - html 结构没有改变。它在 chrome 和 firefox 中运行良好。但翻转在 IE 中不那么漂亮。

这是一个工作演示:http ://plnkr.co/edit/0dn775vpuoOeh2PS1T6k?p=preview

更新
我创建了一个简单的指令来封装这个基本技术。它允许您翻转一张黑卡,以显示另一面的图片。

app.directive("flipReveal", function() {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'template.html',
    scope: {
      url: '=',
      flip: '='
    }
  }
})

这是一个新演示的链接:http: //plnkr.co/X4pSav

演示

于 2014-04-18T03:23:33.467 回答
2

免责声明基于@artur 的回答https://stackoverflow.com/a/23139242/1319998,但希望既简化又变得更灵活。

自定义指令是要走的路,可以用作:

<flip flip-side="{{side}}">
  <flip-front>
    Front side contents
  </flip-front>
  <flip-back>
    Rear contents
  </flip-back>
</flip>

我认为它应该具有某些属性:

  • 由属性以编程方式控制。在这种情况下,等于 'front' 或 'back' 的字符串

    <flip flip-side="{{side}}">....</flip>
    

    这将允许通过周围范围进行编程访问。

  • ngAnimate与/集成$animate。具体来说,如果ngAnimate被移除或禁用,动画不应该发生,但对方的显示会立即发生。使用$animate.addClass/$animate.removeClass可以实现这一点,添加/删除一个flip-visibledisplay:block以及display:none样式,以确保在禁用动画时右侧可见/隐藏。

    flip > flip-front, flip > flip-back {
      display: none;
    }
    flip > .flip-visible {
      display: block;
    }
    
  • 由 CSS 控制,具有默认值。所以如果你想改变翻转的持续时间,它是一个 CSS,而不是一个 Javascript,加法。

    因此它将有一个样式表来添加$animate.addClass/ $animate.removeClassCSS 动画的各个阶段所需的样式,在Year of Moo$animate docs中进行了解释。该类将是flip-visible,因此额外的类将是.flip-visible-add.flip-visible-add-active.flip-visible-remove.flip-visible-remove-active类。

    完整的样式可以在http://plnkr.co/edit/bbYbMhiURnm6FqC9patp?p=preview看到,但是主要的结构是这样的:

    .flip-visible-add {
      // Initial setup: time and initial, pre-animation, transform
    }
    .flip-visible-add.flip-visible-add-active {
      // Target transform
    }
    

将所有这些放在一起,该指令非常短:

app.directive("flip", function($animate) {
  return {
    restrict : "E",
    controller: function($scope, $element, $attrs) {
      var elements = {
        'front': $element.find('flip-front'),
        'back': $element.find('flip-back')
      };
      $attrs.$observe('flipSide', function(visibleSide) {
        visibleSide = visibleSide || 'front';
        var otherSide = visibleSide == 'front' ? 'back' : 'front';
        $animate.removeClass(elements[otherSide], 'flip-visible');
        $animate.addClass(elements[visibleSide], 'flip-visible');
      });
    }
  }
});

这一切都可以在一个示例中看到,以及使其全部工作的样式表,位于http://plnkr.co/edit/bbYbMhiURnm6FqC9patp?p=preview

于 2014-04-18T12:25:45.173 回答
2

我意识到$animate服务集成并拥有纯粹基于类的解决方案是有好处的。

如果你使用$animatewith addClassremoveClass但中断动画(例如,通过快速重复单击元素),动画将“猛拉”到其结束/起点,然后从该位置开始动画,至少在 Chrome 上是这样。使用纯 CSS 解决方案可以避免这个问题,并且总是从确切的当前点开始制作动画,从而产生更平滑的效果。

另一个好处是解决方案也更简单,并且您不需要自定义指令。

例如,HTML 可以如下所示:

<flip class="{{side === 'front' ? 'flip-front' : 'flip-back'}}">
  <flip-front>
    Front side contents
  </flip-front>
  <flip-back>
    Rear contents
  </flip-back>
</flip>

我使用自定义元素,但它们不需要附加任何指令:它们只是为了让 CSS 挂钩:

flip > flip-front, flip > flip-back { 
  -webkit-backface-visibility: hidden; 
  backface-visibility: hidden; 
  /* Time can be overriden */
  transition: -webkit-transform 0.5s; 
  transition: transform 0.5s;
}

/* Front visible */
flip > flip-front {
  -webkit-transform:  perspective(800px) rotateY(0); 
  transform:  perspective(800px) rotateY(0);  
}
flip > flip-back {
 -webkit-transform:  perspective(800px) rotateY(180deg); 
 transform:  perspective(800px) rotateY(180deg);   
}

/* Back visible */
flip.flip-back > flip-front {
  -webkit-transform:  perspective(800px) rotateY(-180deg); 
  transform:  perspective(800px) rotateY(-180deg);  
}
flip.flip-back > flip-back {
 -webkit-transform:  perspective(800px) rotateY(0); 
 transform:  perspective(800px) rotateY(0);   
}

这可以在http://plnkr.co/edit/A7IeGa1JEsZishmTDTaK?p=preview的演示中看到

于 2014-04-18T13:31:53.127 回答
1

我只会在点击时添加/删除一个类。

如果你想挂钩到角度动画系统,那么看看$animate 服务,特别是add/remove/setClass()。该服务通常用于指令中。您可能想要创建一个对单击事件做出反应并触发动画的指令。您甚至会在动画完成时收到通知。

很有可能它不值得;)

于 2014-04-15T10:38:32.613 回答
1

这是artur答案的略微修改版本:

演示

angular.module('FlipDemo', []).directive("flip", function() {  
  return {
    restrict : "A",
    scope: true,
    link: function(scope, element) {
      var $panels = element.css({ position: 'relative' }).children().addClass("flip-panel");  
      var frontPanel = $panels.eq(0);
      var backPanel = $panels.eq(1);
      
      scope.showFrontPanel = function() {
        frontPanel.removeClass("flip-hide-front-panel");
        backPanel.addClass("flip-hide-back-panel");
      };
      
      scope.showBackPanel = function() {
        backPanel.removeClass("flip-hide-back-panel");
        frontPanel.addClass("flip-hide-front-panel");
      };
            
      scope.showFrontPanel();
    }
  }
});
.flip-panel {
  position: absolute;
  width: 100%;
  height: 100%;
  
  -webkit-backface-visibility: hidden;
  -moz-backface-visibility: hidden;
  
  -webkit-transition: -webkit-transform .4s;
  -moz-transition: -moz-transform .4s;
  
  -webkit-transform: perspective(800px) rotateY(0deg);
  -moz-transform: perspective(800px) rotateY(0deg);
}
.flip-hide-back-panel {
  -webkit-transform: perspective(800px) rotateY(180deg);
  -moz-transform: perspective(800px) rotateY(180deg);
}
.flip-hide-front-panel {
  -webkit-transform: perspective(800px) rotateY(-180deg);
  -moz-transform: perspective(800px) rotateY(-180deg);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.1/angular.min.js"></script>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body ng-app="FlipDemo">
  <div style="width: 100px; height: 150px">
    <div flip style="width: 100%; height: 100%">
      <div style="background-color: green">
        <div>Front</div>
        <button ng-click="showBackPanel()">Show Back</button>
      </div>
      <div style="background-color: blue">
        <div>Back</div>
        <button ng-click="showFrontPanel()">Show Front</button>
      </div>
    </div>
  </div>
  <br>
  <div style="width: 150px; height: 100px">
    <div flip style="width: 100%; height: 100%">
      <div style="background-color: green">
        <div>Front</div>
        <button ng-click="showBackPanel()">Show Back</button>
      </div>
      <div style="background-color: blue">
        <div>Back</div>
        <button ng-click="showFrontPanel()">Show Front</button>
      </div>
    </div>
  </div>
</body>
</html>

主要区别:

  • 适用于 Chrome 和 Firefox。
  • 翻转发生时具有更大的灵活性。
  • 只有一个指令,而不是两个。更少的代码。
  • 为了清楚起见,我将 CSS 放在指令之外。
于 2014-04-18T04:20:09.477 回答
1

您将要创建 3 个 div。

<div class="wrapper">
    <div class="front"></div>
    <div class="back"></div>
</div>

然后,您使用 z-index 将其定位在前面,并使用 rotateX(-180 度左右)将其倒置。在包装器上也设置一个过渡。

然后,点击包装,旋转X(+180度)。这将几乎无限地翻转它。

** 更新:对于角度,绑定到单击并使用 setClass 在包装器上的两个类之间切换,一个在 rotateX(0deg) ,另一个在 rotateX(180deg)

于 2014-04-17T12:26:48.893 回答