├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── bower.json ├── demo ├── demo-app.js └── index.html ├── dist ├── css │ ├── angular-flippy-fancy.css │ ├── angular-flippy-fancy.min.css │ ├── angular-flippy.css │ └── angular-flippy.min.css └── js │ ├── angular-flippy.js │ ├── angular-flippy.min.js │ └── angular-flippy.min.js.map ├── gulpfile.js ├── js └── src │ └── flippy-directive.js ├── less ├── flippy-fancy.less └── flippy.less └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 6, 3 | 4 | "bitwise": true, 5 | "curly": true, 6 | "es3": false, 7 | "eqnull": true, 8 | "forin": true, 9 | "freeze": true, 10 | "immed": true, 11 | "indent": 4, 12 | "latedef": "nofunc", 13 | "newcap": true, 14 | "noarg": true, 15 | "noempty": true, 16 | "nonbsp": true, 17 | "nonew": true, 18 | "plusplus": false, 19 | "undef": true, 20 | "unused": false, 21 | "strict": false, 22 | "maxdepth": 5, 23 | 24 | "asi": false, 25 | "boss": false, 26 | "camelcase": false, 27 | "debug": false, 28 | "eqeqeq": false, 29 | "evil": false, 30 | "expr": false, 31 | "funcscope": false, 32 | "globalstrict": false, 33 | "iterator": false, 34 | "lastsemic": false, 35 | "laxbreak": false, 36 | "laxcomma": false, 37 | "loopfunc": true, 38 | "maxcomplexity": false, 39 | "maxerr": false, 40 | "maxparams": false, 41 | "maxstatements": false, 42 | "moz": false, 43 | "multistr": false, 44 | "notypeof": false, 45 | "proto": false, 46 | "quotmark": false, 47 | "scripturl": false, 48 | "shadow": false, 49 | "sub": true, 50 | "supernew": false, 51 | "validthis": false, 52 | "noyield": false, 53 | 54 | "browser": true, 55 | "node": true, 56 | 57 | "globals": { 58 | "angular": false, 59 | "$": false, 60 | "ionic": false, 61 | "_": false 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Simon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular-flippy 2 | 3 | AngularJS directive implementation with a CSS3 flip animation. 4 | 5 | With v2.0 you'll find more customization within the directive itself. You are free to hook into any events like `ng-click` to fire the flip event. 6 | 7 | ## Demo 8 | 9 | * Try the demo here: http://output.jsbin.com/panizijuka 10 | * Clone the repo and `gulp watch` to start the demo page locally 11 | 12 | ## Install 13 | 14 | * npm: `npm install angular-flippy` 15 | * bower: `bower install angular-flippy` 16 | * add *angular-flippy* to your `angular.module('your-webapp', ['angular-flippy', ...)` dependencies 17 | 18 | ## Flippy Directive Parameters 19 | 20 | ```html 21 | 27 | 28 | ``` 29 | * `class`: fancy is an optional class to show some 3D-ness. (include `./css/flippy-fancy.min.css` for this exemplary style) 30 | * `flip`: events that trigger the first flip. will only trigger if flip state is in opposite flip state. 31 | * `flip-back`: events that trigger the flip back. will only trigger if flip state is in opposite flip state. 32 | * `duration`: the time it takes to flip in ms 33 | * `timing-function`: timing functions (see https://developer.mozilla.org/de/docs/Web/CSS/transition-timing-function) 34 | 35 | ## Events 36 | 37 | There are two types of events accepted for the `flip` (first flip) and `flip-back` (flip back): 38 | * Every [DOM event](https://en.wikipedia.org/wiki/DOM_events) e.g. click, mouseenter, mouseleave, dblclick, ... 39 | * `custom:XXX` where XXX is the name of the broadcast event 40 | 41 | 42 | ### Custom Events Example 43 | ```html 44 | // somewhere in your webapp 45 | function buttonClicked() { 46 | $rootScope.$broadcast('FLIP_EVENT_IN'); 47 | } 48 | 49 | // your directive 50 | 55 | 56 | ``` 57 | 58 | ## Contribute 59 | 60 | Just use `gulp watch` and hack away! 61 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-flippy", 3 | "version": "2.0.5", 4 | "description": "AngularJS directive implementation with a CSS3 flip animation", 5 | "homepage": "https://github.com/zwacky/angular-flippy", 6 | "license": "MIT", 7 | "main": [ 8 | "./dist/js/angular-flippy.js", 9 | "./dist/css/angular-flippy.css", 10 | "./dist/css/angular-flippy-fancy.css" 11 | ], 12 | "ignore": [], 13 | "keywords": [ 14 | "angular", 15 | "css3", 16 | "flip", 17 | "animation" 18 | ], 19 | "author": { 20 | "name": "Simon Wicki", 21 | "web": "https://github.com/zwacky" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demo/demo-app.js: -------------------------------------------------------------------------------- 1 | angular.module('angular-flippy-demo', [ 2 | 'angular-flippy' 3 | ]); 4 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

angular-flippy Demo Page

13 |

14 | Please refer to the README.md for instructions on how to use angular-flippy. 15 |

16 |





17 |
18 |

Item #{{$index+1}} (horizontal)

19 | 25 | 26 | 27 | the front 28 | 29 | 30 | 31 | the back 32 | 33 | 34 | 35 |
36 |
37 |

Item #{{$index+1}} (vertical)

38 | 44 | 45 | 46 | the front 47 | 48 | 49 | 50 | the back 51 | 52 | 53 | 54 |
55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /dist/css/angular-flippy-fancy.css: -------------------------------------------------------------------------------- 1 | flippy.fancy { 2 | float: left; 3 | margin: 0 10px 10px 0; 4 | position: relative; 5 | font-size: .8em; 6 | cursor: pointer; 7 | width: 250px; 8 | height: 250px; 9 | } 10 | flippy.fancy img { 11 | height: 100%; 12 | width: 100%; 13 | } 14 | flippy.fancy flippy-front { 15 | float: none; 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | z-index: 900; 20 | width: inherit; 21 | height: inherit; 22 | border: 1px solid #ccc; 23 | background: white; 24 | text-align: center; 25 | box-shadow: 0 1px 5px rgba(0, 0, 0, 0.9); 26 | } 27 | flippy.fancy flippy-front.flipped { 28 | border-color: #eee; 29 | /*background: #333;*/ 30 | box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2); 31 | } 32 | flippy.fancy flippy-back { 33 | float: none; 34 | position: absolute; 35 | top: 0; 36 | left: 0; 37 | z-index: 800; 38 | width: inherit; 39 | height: inherit; 40 | border: 1px solid #ccc; 41 | background: white; 42 | text-align: center; 43 | box-shadow: 0 1px 5px rgba(0, 0, 0, 0.9); 44 | } 45 | flippy.fancy flippy-back.flipped { 46 | /*background: #80868d;*/ 47 | box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2); 48 | } 49 | -------------------------------------------------------------------------------- /dist/css/angular-flippy-fancy.min.css: -------------------------------------------------------------------------------- 1 | flippy.fancy{float:left;margin:0 10px 10px 0;position:relative;font-size:.8em;cursor:pointer;width:250px;height:250px}flippy.fancy img{height:100%;width:100%}flippy.fancy flippy-back,flippy.fancy flippy-front{float:none;position:absolute;top:0;left:0;width:inherit;height:inherit;background:#fff;text-align:center}flippy.fancy flippy-front{z-index:900;border:1px solid #ccc;box-shadow:0 1px 5px rgba(0,0,0,.9)}flippy.fancy flippy-front.flipped{border-color:#eee;box-shadow:0 15px 50px rgba(0,0,0,.2)}flippy.fancy flippy-back{z-index:800;border:1px solid #ccc;box-shadow:0 1px 5px rgba(0,0,0,.9)}flippy.fancy flippy-back.flipped{box-shadow:0 15px 50px rgba(0,0,0,.2)} -------------------------------------------------------------------------------- /dist/css/angular-flippy.css: -------------------------------------------------------------------------------- 1 | flippy { 2 | float: left; 3 | -webkit-perspective: 600px; 4 | perspective: 600px; 5 | -ms-perspective: none; 6 | -moz-perspective: none; 7 | } 8 | flippy flippy-front, 9 | flippy flippy-back { 10 | position: absolute; 11 | width: inherit; 12 | height: inherit; 13 | -webkit-transform-style: preserve-3d; 14 | transform-style: preserve-3d; 15 | -webkit-backface-visibility: hidden; 16 | backface-visibility: hidden; 17 | } 18 | flippy flippy-front { 19 | z-index: 900; 20 | -webkit-transform: rotate3d(0, 0, 0, 0deg); 21 | transform: rotate3d(0, 0, 0, 0deg); 22 | -ms-transform: rotateY(0deg); 23 | } 24 | flippy flippy-back { 25 | z-index: 800; 26 | -webkit-transform: rotate3d(0, 1, 0, -180deg); 27 | transform: rotate3d(0, 1, 0, -180deg); 28 | -ms-transform: rotateY(180deg); 29 | } 30 | flippy.flipped flippy-front { 31 | z-index: 900; 32 | -webkit-transform: rotate3d(0, 1, 0, 180deg); 33 | transform: rotate3d(0, 1, 0, 180deg); 34 | -ms-transform: rotateY(180deg); 35 | } 36 | flippy.flipped flippy-back { 37 | z-index: 1000; 38 | -webkit-transform: rotate3d(0, 0, 0, 0deg); 39 | transform: rotate3d(0, 0, 0, 0deg); 40 | -ms-transform: rotateY(0deg); 41 | } 42 | flippy[vertical] flippy-front { 43 | z-index: 900; 44 | -webkit-transform: rotate3d(0, 0, 0, 0deg); 45 | transform: rotate3d(0, 0, 0, 0deg); 46 | -ms-transform: rotateY(0deg); 47 | } 48 | flippy[vertical] flippy-back { 49 | z-index: 800; 50 | -webkit-transform: rotate3d(1, 0, 0, -180deg) !important; 51 | transform: rotate3d(1, 0, 0, -180deg) !important; 52 | -ms-transform: rotateY(180deg); 53 | } 54 | flippy[vertical].flipped flippy-front { 55 | z-index: 900; 56 | -webkit-transform: rotate3d(1, 0, 0, 180deg) !important; 57 | transform: rotate3d(1, 0, 0, 180deg) !important; 58 | -ms-transform: rotateY(180deg); 59 | } 60 | flippy[vertical].flipped flippy-back { 61 | z-index: 1000; 62 | -webkit-transform: rotate3d(0, 0, 0, 0deg) !important; 63 | transform: rotate3d(0, 0, 0, 0deg) !important; 64 | } 65 | -------------------------------------------------------------------------------- /dist/css/angular-flippy.min.css: -------------------------------------------------------------------------------- 1 | flippy{float:left;-webkit-perspective:600px;perspective:600px;-ms-perspective:none;-moz-perspective:none}flippy flippy-back,flippy flippy-front{position:absolute;width:inherit;height:inherit;-webkit-transform-style:preserve-3d;transform-style:preserve-3d;-webkit-backface-visibility:hidden;backface-visibility:hidden}flippy flippy-front{z-index:900;-webkit-transform:rotate3d(0,0,0,0deg);transform:rotate3d(0,0,0,0deg);-ms-transform:rotateY(0)}flippy flippy-back{z-index:800;-webkit-transform:rotate3d(0,1,0,-180deg);transform:rotate3d(0,1,0,-180deg);-ms-transform:rotateY(180deg)}flippy.flipped flippy-front{z-index:900;-webkit-transform:rotate3d(0,1,0,180deg);transform:rotate3d(0,1,0,180deg);-ms-transform:rotateY(180deg)}flippy.flipped flippy-back{z-index:1000;-webkit-transform:rotate3d(0,0,0,0deg);transform:rotate3d(0,0,0,0deg);-ms-transform:rotateY(0)}flippy[vertical] flippy-front{z-index:900;-webkit-transform:rotate3d(0,0,0,0deg);transform:rotate3d(0,0,0,0deg);-ms-transform:rotateY(0)}flippy[vertical] flippy-back{z-index:800;-webkit-transform:rotate3d(1,0,0,-180deg)!important;transform:rotate3d(1,0,0,-180deg)!important;-ms-transform:rotateY(180deg)}flippy[vertical].flipped flippy-front{z-index:900;-webkit-transform:rotate3d(1,0,0,180deg)!important;transform:rotate3d(1,0,0,180deg)!important;-ms-transform:rotateY(180deg)}flippy[vertical].flipped flippy-back{z-index:1000;-webkit-transform:rotate3d(0,0,0,0deg)!important;transform:rotate3d(0,0,0,0deg)!important} -------------------------------------------------------------------------------- /dist/js/angular-flippy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * handles the behaviour of flipping card. 5 | */ 6 | angular.module('angular-flippy', []).directive('flippy', function () { 7 | return { 8 | restrict: 'E', 9 | scope: { 10 | flip: '=', 11 | flipBack: '=', 12 | duration: '@', 13 | timingFunction: '@' 14 | }, 15 | link: function link($scope, $elem, $attrs) { 16 | 17 | var CUSTOM_PREFIX = 'custom:'; 18 | var state = { 19 | flipped: false 20 | }; 21 | var options = { 22 | duration: 400, 23 | timingFunction: 'ease-in-out' 24 | }; 25 | 26 | // assign new options 27 | angular.forEach(['duration', 'timingFunction'], function (item) { 28 | options[item] = $scope[item] ? $scope[item] : options[item]; 29 | }); 30 | 31 | angular.forEach({ flip: flip, flipBack: flipBack }, function (flipFunc, evt) { 32 | angular.forEach($scope[evt], function (eventName) { 33 | if (eventName.indexOf(CUSTOM_PREFIX) === -1) { 34 | // directly register event listener to avoid having to start off angular's digest cycle 35 | angular.element($elem)[0].addEventListener(eventName, flipFunc); 36 | } else { 37 | $scope.$on(eventName.substr(CUSTOM_PREFIX.length), flipFunc); 38 | } 39 | }); 40 | }); 41 | 42 | // set flip duration 43 | angular.forEach(['flippy-front', 'flippy-back'], function (name) { 44 | var el = $elem.find(name); 45 | if (el.length == 1) { 46 | angular.forEach(['', '-ms-', '-webkit-'], function (prefix) { 47 | angular.element(el[0]).css(prefix + 'transition', 'all ' + options.duration / 1000 + 's ' + options.timingFunction); 48 | }); 49 | } 50 | }); 51 | 52 | /** 53 | * flips the card. 54 | * will be ignored, if the state is already the same as the target state. 55 | * 56 | * @param boolean isBack 57 | */ 58 | function _flip() { 59 | var isBack = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; 60 | 61 | if (!isBack && !state.flipped || isBack && state.flipped) { 62 | // to avoid toggling it right back if flip-back is the same event 63 | setTimeout(function () { 64 | $elem.toggleClass('flipped'); 65 | state.flipped = !state.flipped; 66 | }, 0); 67 | } 68 | } 69 | 70 | function flip() { 71 | _flip(); 72 | } 73 | 74 | function flipBack() { 75 | _flip(true); 76 | } 77 | } 78 | }; 79 | }); -------------------------------------------------------------------------------- /dist/js/angular-flippy.min.js: -------------------------------------------------------------------------------- 1 | "use strict";angular.module("angular-flippy",[]).directive("flippy",function(){return{restrict:"E",scope:{flip:"=",flipBack:"=",duration:"@",timingFunction:"@"},link:function(n,i,t){function a(){var n=arguments.length<=0||void 0===arguments[0]?!1:arguments[0];(!n&&!o.flipped||n&&o.flipped)&&setTimeout(function(){i.toggleClass("flipped"),o.flipped=!o.flipped},0)}function u(){a()}function e(){a(!0)}var l="custom:",o={flipped:!1},r={duration:400,timingFunction:"ease-in-out"};angular.forEach(["duration","timingFunction"],function(i){r[i]=n[i]?n[i]:r[i]}),angular.forEach({flip:u,flipBack:e},function(t,a){angular.forEach(n[a],function(a){-1===a.indexOf(l)?angular.element(i)[0].addEventListener(a,t):n.$on(a.substr(l.length),t)})}),angular.forEach(["flippy-front","flippy-back"],function(n){var t=i.find(n);1==t.length&&angular.forEach(["","-ms-","-webkit-"],function(n){angular.element(t[0]).css(n+"transition","all "+r.duration/1e3+"s "+r.timingFunction)})})}}}); 2 | //# sourceMappingURL=angular-flippy.min.js.map 3 | -------------------------------------------------------------------------------- /dist/js/angular-flippy.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["flippy-directive.js"],"names":["angular","module","directive","restrict","scope","flip","flipBack","duration","timingFunction","link","$scope","$elem","$attrs","_flip","isBack","arguments","length","undefined","state","flipped","setTimeout","toggleClass","CUSTOM_PREFIX","options","forEach","item","flipFunc","evt","eventName","indexOf","element","addEventListener","$on","substr","name","el","find","prefix","css"],"mappings":"AAAA,YAGAA,SAAQC,OAAO,qBACbC,UAAU,SAAU,WACpB,OACCC,SAAU,IACVC,OACCC,KAAM,IACNC,SAAU,IACVC,SAAU,IACVC,eAAgB,KAEjBC,KAAM,SAACC,EAAQC,EAAOC,GAAW,QA4CvBC,KACT,GADeC,GAAAC,UAAAC,QAAA,GAAAC,SAAAF,UAAA,IAAS,EAAAA,UAAA,KACjBD,IAAWI,EAAMC,SAAaL,GAAUI,EAAMC,UAEnDC,WAAW,WACVT,EAAMU,YAAY,WAClBH,EAAMC,SAAWD,EAAMC,SACrB,GAIL,QAASd,KACRQ,IAGD,QAASP,KACRO,GAAM,GAzDP,GAAMS,GAAgB,UAChBJ,GACLC,SAAS,GAEJI,GACLhB,SAAU,IACVC,eAAgB,cAReR,SAYxBwB,SAAS,WAAY,kBAAmB,SAACC,GAChDF,EAAQE,GAAQf,EAAQe,GAASf,EAAOe,GAAQF,EAAQE,KAGzDzB,QAAQwB,SAASnB,KAAMA,EAAMC,SAAUA,GAAW,SAACoB,EAAUC,GAC5D3B,QAAQwB,QAAQd,EAAOiB,GAAM,SAACC,GACY,KAArCA,EAAUC,QAAQP,GAErBtB,QAAQ8B,QAAQnB,GAAO,GAAGoB,iBAAiBH,EAAWF,GAEtDhB,EAAOsB,IAAIJ,EAAUK,OAAOX,EAAcN,QAASU,OAtBtB1B,QA4BxBwB,SAAS,eAAgB,eAAgB,SAACU,GACjD,GAAMC,GAAKxB,EAAMyB,KAAKF,EACL,IAAbC,EAAGnB,QACNhB,QAAQwB,SAAS,GAAI,OAAQ,YAAa,SAACa,GAC1CrC,QAAQ8B,QAAQK,EAAG,IAAIG,IAAID,EAAS,aAAc,OAASd,EAAQhB,SAAS,IAAO,KAAOgB,EAAQf","file":"angular-flippy.min.js","sourcesContent":["/**\n * handles the behaviour of flipping card.\n */\nangular.module('angular-flippy', [])\n\t.directive('flippy', () => {\n\t\treturn {\n\t\t\trestrict: 'E',\n\t\t\tscope: {\n\t\t\t\tflip: '=',\n\t\t\t\tflipBack: '=',\n\t\t\t\tduration: '@',\n\t\t\t\ttimingFunction: '@'\n\t\t\t},\n\t\t\tlink: ($scope, $elem, $attrs) => {\n\n\t\t\t\tconst CUSTOM_PREFIX = 'custom:';\n\t\t\t\tconst state = {\n\t\t\t\t\tflipped: false\n\t\t\t\t};\n\t\t\t\tconst options = {\n\t\t\t\t\tduration: 400,\n\t\t\t\t\ttimingFunction: 'ease-in-out'\n\t\t\t\t};\n\n\t\t\t\t// assign new options\n\t\t\t\tangular.forEach(['duration', 'timingFunction'], (item) => {\n\t\t\t\t\toptions[item] = ($scope[item]) ? $scope[item] : options[item];\n\t\t\t\t});\n\n\t\t\t\tangular.forEach({flip: flip, flipBack: flipBack}, (flipFunc, evt) => {\n\t\t\t\t\tangular.forEach($scope[evt], (eventName) => {\n\t\t\t\t\t\tif (eventName.indexOf(CUSTOM_PREFIX) === -1) {\n\t\t\t\t\t\t\t// directly register event listener to avoid having to start off angular's digest cycle\n\t\t\t\t\t\t\tangular.element($elem)[0].addEventListener(eventName, flipFunc);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t$scope.$on(eventName.substr(CUSTOM_PREFIX.length), flipFunc);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\t// set flip duration\n\t\t\t\tangular.forEach(['flippy-front', 'flippy-back'], (name) => {\n\t\t\t\t\tconst el = $elem.find(name);\n\t\t\t\t\tif (el.length == 1) {\n\t\t\t\t\t\tangular.forEach(['', '-ms-', '-webkit-'], (prefix) => {\n\t\t\t\t\t\t\tangular.element(el[0]).css(prefix + 'transition', 'all ' + options.duration/1000 + 's ' + options.timingFunction);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\n\t\t\t\t/**\n\t\t\t\t * flips the card.\n\t\t\t\t * will be ignored, if the state is already the same as the target state.\n\t\t\t\t *\n\t\t\t\t * @param boolean isBack\n\t\t\t\t */\n\t\t\t\tfunction _flip(isBack = false) {\n\t\t\t\t\tif ((!isBack && !state.flipped) || (isBack && state.flipped)) {\n\t\t\t\t\t\t// to avoid toggling it right back if flip-back is the same event\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\t$elem.toggleClass('flipped');\n\t\t\t\t\t\t\tstate.flipped = !state.flipped;\n\t\t\t\t\t\t}, 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfunction flip() {\n\t\t\t\t\t_flip();\n\t\t\t\t}\n\n\t\t\t\tfunction flipBack() {\n\t\t\t\t\t_flip(true);\n\t\t\t\t}\n\n\t\t\t}\n\t\t};\n\t});\n"],"sourceRoot":"/source/"} -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var plugins = require('gulp-load-plugins')(); 3 | var del = require('del'); 4 | var merge = require('merge-stream'); 5 | var runSequence = require('run-sequence'); 6 | var browserSync = require('browser-sync').create(); 7 | var history = require('connect-history-api-fallback'); 8 | 9 | var paths = { 10 | less: ['./less/*.less'], 11 | js: ['./js/src/**/*.js'], 12 | dist: { 13 | css: './dist/css/', 14 | js: './dist/js/', 15 | } 16 | }; 17 | 18 | 19 | 20 | /** 21 | * removes css- and js-dist folder. 22 | */ 23 | gulp.task('clean', function() { 24 | return del(['./dist']); 25 | }); 26 | 27 | /** 28 | * compiles less files into css. 29 | */ 30 | gulp.task('style', function() { 31 | var streams = {}; 32 | streams.flippy = gulp.src('./less/flippy.less') 33 | .pipe(plugins.less()) 34 | .pipe(plugins.autoprefixer(['> 1%','last 3 versions'])) 35 | .pipe(plugins.concat('angular-flippy.css')) 36 | .pipe(gulp.dest(paths.dist.css)) 37 | .pipe(plugins.minifyCss()) 38 | .pipe(plugins.concat('angular-flippy.min.css')) 39 | .pipe(gulp.dest(paths.dist.css)); 40 | 41 | // fancy 42 | streams.flippyFancy = gulp.src('./less/flippy-fancy.less') 43 | .pipe(plugins.less()) 44 | .pipe(plugins.autoprefixer(['> 1%','last 3 versions'])) 45 | .pipe(plugins.concat('angular-flippy-fancy.css')) 46 | .pipe(gulp.dest(paths.dist.css)) 47 | .pipe(plugins.minifyCss()) 48 | .pipe(plugins.concat('angular-flippy-fancy.min.css')) 49 | .pipe(gulp.dest(paths.dist.css)); 50 | 51 | return merge(streams.flippy, streams.flippyFancy); 52 | }); 53 | 54 | gulp.task('js', ['lint'], function() { 55 | return gulp.src(paths.js) 56 | .pipe(plugins.sourcemaps.init()) 57 | .pipe(plugins.babel({ 58 | presets: ['es2015'] 59 | })) 60 | .pipe(plugins.ngAnnotate()) 61 | .pipe(plugins.concat('angular-flippy.js')) 62 | .pipe(gulp.dest(paths.dist.js)) 63 | .pipe(plugins.uglify()) 64 | .pipe(plugins.concat('angular-flippy.min.js')) 65 | .pipe(plugins.sourcemaps.write('.')) 66 | .pipe(gulp.dest(paths.dist.js)); 67 | }); 68 | 69 | gulp.task('lint', function() { 70 | return gulp.src(paths.js) 71 | .pipe(plugins.jshint()) 72 | .pipe(plugins.jshint.reporter('jshint-stylish')) 73 | .pipe(plugins.jshint.reporter('fail')); 74 | }) 75 | 76 | gulp.task('watch', ['build'], function() { 77 | gulp.watch(paths.less, ['watch-style']); 78 | gulp.watch(paths.js, ['watch-js']); 79 | 80 | browserSync.init({ 81 | server: { 82 | baseDir: '', 83 | middleware: [ history() ] 84 | }, 85 | startPath: '/demo/' 86 | }); 87 | }); 88 | 89 | gulp.task('_browser-reload', function(cb) { 90 | browserSync.reload(); 91 | cb(); 92 | }); 93 | 94 | gulp.task('watch-style', function(cb) { 95 | return runSequence(['style'], ['_browser-reload'], cb); 96 | }); 97 | 98 | gulp.task('watch-js', function(cb) { 99 | return runSequence(['js'], ['_browser-reload'], cb); 100 | }); 101 | 102 | 103 | gulp.task('build', function() { 104 | return runSequence(['clean'], ['style', 'js']); 105 | }); 106 | gulp.task('default', ['build']); 107 | -------------------------------------------------------------------------------- /js/src/flippy-directive.js: -------------------------------------------------------------------------------- 1 | /** 2 | * handles the behaviour of flipping card. 3 | */ 4 | angular.module('angular-flippy', []) 5 | .directive('flippy', () => { 6 | return { 7 | restrict: 'E', 8 | scope: { 9 | flip: '=', 10 | flipBack: '=', 11 | duration: '@', 12 | timingFunction: '@' 13 | }, 14 | link: ($scope, $elem, $attrs) => { 15 | 16 | const CUSTOM_PREFIX = 'custom:'; 17 | const state = { 18 | flipped: false 19 | }; 20 | const options = { 21 | duration: 400, 22 | timingFunction: 'ease-in-out' 23 | }; 24 | 25 | // assign new options 26 | angular.forEach(['duration', 'timingFunction'], (item) => { 27 | options[item] = ($scope[item]) ? $scope[item] : options[item]; 28 | }); 29 | 30 | angular.forEach({flip: flip, flipBack: flipBack}, (flipFunc, evt) => { 31 | angular.forEach($scope[evt], (eventName) => { 32 | if (eventName.indexOf(CUSTOM_PREFIX) === -1) { 33 | // directly register event listener to avoid having to start off angular's digest cycle 34 | angular.element($elem)[0].addEventListener(eventName, flipFunc); 35 | } else { 36 | $scope.$on(eventName.substr(CUSTOM_PREFIX.length), flipFunc); 37 | } 38 | }); 39 | }); 40 | 41 | // set flip duration 42 | angular.forEach(['flippy-front', 'flippy-back'], (name) => { 43 | const el = $elem.find(name); 44 | if (el.length == 1) { 45 | angular.forEach(['', '-ms-', '-webkit-'], (prefix) => { 46 | angular.element(el[0]).css(prefix + 'transition', 'all ' + options.duration/1000 + 's ' + options.timingFunction); 47 | }); 48 | } 49 | }); 50 | 51 | 52 | /** 53 | * flips the card. 54 | * will be ignored, if the state is already the same as the target state. 55 | * 56 | * @param boolean isBack 57 | */ 58 | function _flip(isBack = false) { 59 | if ((!isBack && !state.flipped) || (isBack && state.flipped)) { 60 | // to avoid toggling it right back if flip-back is the same event 61 | setTimeout(() => { 62 | $elem.toggleClass('flipped'); 63 | state.flipped = !state.flipped; 64 | }, 0); 65 | } 66 | } 67 | 68 | function flip() { 69 | _flip(); 70 | } 71 | 72 | function flipBack() { 73 | _flip(true); 74 | } 75 | 76 | } 77 | }; 78 | }); 79 | -------------------------------------------------------------------------------- /less/flippy-fancy.less: -------------------------------------------------------------------------------- 1 | @card-width: 250px; 2 | @card-height: @card-width; 3 | 4 | flippy { 5 | &.fancy { 6 | float: left; 7 | margin: 0 10px 10px 0; 8 | position: relative; 9 | font-size: .8em; 10 | cursor: pointer; 11 | width: @card-width; 12 | height: @card-height; 13 | 14 | img { 15 | height: 100%; 16 | width: 100%; 17 | } 18 | 19 | flippy-front { 20 | float: none; 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | z-index: 900; 25 | width: inherit; 26 | height: inherit; 27 | border: 1px solid #ccc; 28 | background: white; 29 | text-align: center; 30 | box-shadow: 0 1px 5px rgba(0,0,0,0.9); 31 | 32 | &.flipped { 33 | border-color: #eee; 34 | /*background: #333;*/ 35 | box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2); 36 | } 37 | } 38 | 39 | flippy-back { 40 | float: none; 41 | position: absolute; 42 | top: 0; 43 | left: 0; 44 | z-index: 800; 45 | width: inherit; 46 | height: inherit; 47 | border: 1px solid #ccc; 48 | background: white; 49 | text-align: center; 50 | box-shadow: 0 1px 5px rgba(0,0,0,0.9); 51 | 52 | &.flipped { 53 | /*background: #80868d;*/ 54 | box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2); 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /less/flippy.less: -------------------------------------------------------------------------------- 1 | flippy { 2 | float: left; 3 | perspective: 600px; 4 | -ms-perspective: none; 5 | -moz-perspective: none; 6 | flippy-front, flippy-back { 7 | position: absolute; 8 | width: inherit; 9 | height: inherit; 10 | transform-style: preserve-3d; 11 | backface-visibility: hidden; 12 | } 13 | flippy-front { 14 | z-index: 900; 15 | transform: rotate3d( 0, 0, 0, 0deg); 16 | -ms-transform: rotateY(0deg); 17 | // will be set in the directive 18 | // transition: all @flip-duration ease-in-out; 19 | } 20 | flippy-back { 21 | z-index: 800; 22 | transform: rotate3d( 0, 1, 0, -180deg); 23 | -ms-transform: rotateY(180deg); 24 | // will be set in the directive 25 | // transition: all @flip-duration ease-in-out; 26 | } 27 | &.flipped { 28 | flippy-front { 29 | z-index: 900; 30 | transform: rotate3d( 0, 1, 0, 180deg); 31 | -ms-transform: rotateY(180deg); 32 | } 33 | flippy-back { 34 | z-index: 1000; 35 | transform: rotate3d( 0, 0, 0, 0deg); 36 | -ms-transform: rotateY(0deg); 37 | } 38 | } 39 | } 40 | 41 | flippy[vertical] { 42 | flippy-front { 43 | z-index: 900; 44 | -webkit-transform: rotate3d(0, 0, 0, 0deg); 45 | transform: rotate3d(0, 0, 0, 0deg); 46 | -ms-transform: rotateY(0deg); 47 | } 48 | flippy-back { 49 | z-index: 800; 50 | -webkit-transform: rotate3d(1, 0, 0, -180deg)!important; 51 | transform: rotate3d(1, 0, 0, -180deg)!important; 52 | -ms-transform: rotateY(180deg); 53 | } 54 | &.flipped { 55 | flippy-front { 56 | z-index: 900; 57 | -webkit-transform: rotate3d(1, 0, 0, 180deg)!important; 58 | transform: rotate3d(1, 0, 0, 180deg)!important; 59 | -ms-transform: rotateY(180deg); 60 | } 61 | flippy-back { 62 | z-index: 1000; 63 | -webkit-transform: rotate3d(0, 0, 0, 0deg)!important; 64 | transform: rotate3d(0, 0, 0, 0deg)!important; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-flippy", 3 | "version": "2.1.1", 4 | "description": "AngularJS directive implementation with a CSS3 flip animation.", 5 | "main": "dist/js/angular-flippy.min.js", 6 | "scripts": {}, 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/zwacky/angular-flippy.git" 10 | }, 11 | "author": "Simon Wicki ", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "angular": "^1.5.0", 15 | "babel-preset-es2015": "^6.3.13", 16 | "bootstrap": "^3.3.6", 17 | "browser-sync": "^2.11.1", 18 | "connect-history-api-fallback": "^1.1.0", 19 | "del": "^2.2.0", 20 | "gulp": "^3.9.0", 21 | "gulp-autoprefixer": "^3.1.0", 22 | "gulp-babel": "^6.1.2", 23 | "gulp-concat": "^2.6.0", 24 | "gulp-conventional-changelog": "^0.7.0", 25 | "gulp-jshint": "^2.0.0", 26 | "gulp-less": "^3.0.5", 27 | "gulp-livereload": "^3.8.1", 28 | "gulp-load-plugins": "^1.2.0", 29 | "gulp-minify-css": "^1.2.3", 30 | "gulp-ng-annotate": "^1.1.0", 31 | "gulp-purifycss": "^0.2.0", 32 | "gulp-sourcemaps": "^1.6.0", 33 | "gulp-uglify": "^1.5.1", 34 | "jshint": "^2.9.1", 35 | "jshint-stylish": "^2.1.0", 36 | "merge-stream": "^1.0.0", 37 | "run-sequence": "^1.1.5" 38 | } 39 | } 40 | --------------------------------------------------------------------------------