-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathflipperText.html
265 lines (219 loc) · 12.3 KB
/
flipperText.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
<!DOCTYPE html>
<html>
<head>
<title>Animated flip tile using Angular JS</title>
</head>
<body>
<h2>Animated Flip Tile Using AngularJS</h2>
<p>
Today, I'm going to explain how to create a flipping tile using CSS3 animations and Angular JS. If you've never used Angular JS,
you're missing out. You can check it out <a target="_blank" href="https://angularjs.org/">here</a>. I'm going to be using LESS CSS for some of the more
advanced CSS because it allows us to completely ignore vendor prefixes through the use of LESS mixins. If you've never used LESS CSS
either, I suggest you read up on it <a href="http://lesscss.org/" target="_blank">here</a>. It's definitely worth picking up a CSS language, even if it's
not LESS.
</p>
<p>
Angular JS is great. It allows the user to almost completely remove the need for DOM manipulation inside controllers, and while some
DOM manipulation is somewhat necessary, that code can be encapsulated inside directives and in many cases, can be eliminated all
together. The less DOM access, the better, as it allows for easy unit testing. The flip tile we're going to be creating today uses no
DOM manipulation at all, so mission accomplished. If you're the kind of reader who just needs to see it in action from start to finish,
I've built a demo that shows the flip tiles in action. You can check it out <a href="http://chrisharrington.github.io/demos/flipper.html" target="_blank">here</a>.
If, on the other hand, you want to see exactly how I put this whole thing together, and how it all works, read on!
</p>
<p>
I'm going to break down the write up into a couple of different sections. First, the HTML. It's pretty straightforward, but I'll go
over each of the Angular JS directives that I'm using, and what they all do. Then, we'll walk through the JavaScript code, which shows
how I'm using Angular JS to add and remove the appropriate CSS classes to perform the animation. Finally, I'll describe what I'm doing
with each off the CSS rules that actually cause the flip tiles to move around.
</p>
<p>
<b>Note:</b> The CSS shown here might not match up with exactly what you see on the demo page because I want the demo to look at least
halfway decent with some pretty styling. For the most part, though, I'm going to try to keep the CSS on the tutorial as bare as possible.
</p>
<h3>The HTML</h3>
<p>First, the HTML. It makes use of a couple of custom directives, which I'll go over shortly.</p>
<h4>Example</h4>
<pre>
<flipper flipped="flipped" horizontal>
<front">
the front!
</front>
<back>
the back!
</back>
</flipper>
<flipper flipped="flipped" vertical>
<div class="front tile">
the front!
</div>
<div class="back tile">
the back!
</div>
</flipper>
</pre>
<h4>Flipper Template</h4>
<pre>
<div class="flipper" ng-transclude ng-class="{ flipped: flipped }"></div>
</pre>
<h4>Front Template</h4>
<pre>
<div class="front tile"></div>
</pre>
<h4>Back Template</h4>
<pre>
<div class="back tile"></div>
</pre>
<p>
Ok, there's a couple of things going on here. The first is that I'm using a custom directive ("flipper") to encapsulate the flipper. Additionally,
it contains two more custom directives ("front" and "back"), within which we can put our content. I've unoriginally put "the front!" and
"the back!" for the content, but you can put whatever you like in there, obviously including more HTML tags. We'll set some configuration options
on the directive so that the content inside the front and back tags magically appears inside the rendered HTML. I'll go over that
below.
</p>
<p>
The flipper tag has a couple of attributes. The first is "flipped", indicating which Angular JS variable should be passed into the
inner flipper scope. Angular JS will watch changes to variables passed in this manner, so all we have to do is wire up our directive template
to apply a CSS class where appropriate. The second is the "horizontal" or "vertical" tag, which tells the indicates which direction the tile
should flip. This corresponds to different CSS classes being applied to the elements when the "flipped" variable changes.
</p>
<p>
The flipper directive is also straightforward, making use of a couple of Angular-built directives, namely ng-transclude and ng-class. The ng-class
directive indicates that if the "flipped" variable is truthy, the flipped class should be added to the div, and removed if not. The ng-transclude
directive signifies that the inner contents of this tag should be the child contents of the tag as written on the parent. This includes the front and
back custom directives, which are two separate custom directives. The "front" tag contains content that should go on the front of the
tile, while the "back" tag unsurprisingly contains content that will show up once the tile has flipped. You can put whatever you want in here, be
it HTML or text. In the example, I've got some indicator text in there. <b>Note:</b> While you can put almost any styled HTML in these tiles, make
sure that you're not doing any absolute positioning with nested elements. The front and back sides of the tile are absolutely positioned, so
adding more of that will complicate matters.
</p>
<h3>JavaScript</h3>
<p>
Here's where all the Angular JS magic happens. I'll paste the whole thing in here, which includes the app initialization as well as the custom
directive code.
</p>
<pre>
var app = angular.module("demo", []);
app.controller("flipperDemo", function($scope) {
$scope.flipped = false;
$scope.flip = function() {
$scope.flipped = !$scope.flipped;
};
});
app.directive("flipper", function() {
return {
restrict: "E",
template: "<div class='flipper' ng-transclude ng-class='{ flipped: flipped }'></div>",
transclude: true,
scope: {
flipped: "="
}
};
});
app.directive("front", function() {
return {
restrict: "E",
template: "<div class='front tile' ng-transclude></div>",
transclude: true
};
});
app.directive("back", function() {
return {
restrict: "E",
template: "<div class='back tile' ng-transclude></div>",
transclude: true
}
});
</pre>
<p>
I'm going to assume that the reader is at least somewhat familiar with how to set up an Angular JS app, but the code is up there anyway.
You'll also need to specify ng-app="demo" on your HTML tag (see the example page source to see this in action). The controller I've set up
contains just a single scope variable: flipped. This is the variable the flipper directive needs access to to determine its inner state,
specifically whether to display the front or the back tile. It also sets a method on the controller scope, flip, which toggles the flipped
variable. It's initially set to false, indicating that the tile should show the front by default.
</p>
<p>
Then, we've got the three directive statements. The flipper directive is the most complicated, but it's straightforward nonetheless. First,
I'm restricting this directive to an element. Angular JS directives can appear in a number of different forms, including an element (HTML tag),
an attribute and a class name. In this case, the element makes the most sense, so we indicate that with an "E". Next, we specify the template for the flipper. Because the flipper's
template is just a single line, I've opted to put this inline in the directive definition, but for more complicated directives, it's best
practice to move this to a separate file. You'll have to change "template" to "templateUrl" for that to work, however. The "transclude" option
indicates that we want to take the child elements (or text) and put it inside the element annotated with the "ng-transclude" attribute, which
you can see in the template text. We're creating an isolated scope with just a single variable, flipped. The equals sign indicates that this is a
two way binding, meaning that we can read the value from the parent scope and also set it in the event we need to.
</p>
<p>
The front and back directives are even simpler than the flipper and contain only a single difference: the front or back CSS class. Note that they're
also using transclusion to render child elements in the built HTML.
</p>
<h4>CSS</h4>
<pre>
.transition (@value1,@value2:X,...) { @value: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`; -webkit-transition: @value; -moz-transition: @value; -ms-transition: @value; -o-transition: @value; transition: @value; }
.transform (@value1,@value2:X,...) { @value: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`; transform:@value; -ms-transform:@value; -webkit-transform:@value; -o-transform:@value; -moz-transform:@value; }
.transform-style(@style:preserve-3d) { transform-style:@style; -webkit-transform-style:@style; -moz-transform-style:@style; -ms-transform-style:@style; }
.perspective(@amount: 1000px) { perspective:@amount; -webkit-perspective:@amount; -moz-perspective:@amount; -ms-perspective:@amount; }
.tile { color:white; padding:15px; box-sizing:border-box; -moz-box-sizing:border-box; }
flipper { float:left; width:250px; height:250px; margin-right:15px; display:block; .perspective;
span { color:white; }
>div.flipper { float:left; width:100%; height:100%; position:relative; .transform-style(preserve-3d);
.front, .back { float:left; display:block; width:100%; height:100%; backface-visibility:hidden; position:absolute; top:0; left:0; .transform-style(preserve-3d); .transition(transform ease 500ms, -webkit-transform ease 500ms); }
.front {
z-index:2;
background:#19489E;
/* front tile styles go here! */
}
.back {
background:#9E1919;
/* back tile styles go here! */
}
}
}
flipper[horizontal] {
.front { .transform(rotateY(0deg)); }
.back { .transform(rotateY(-180deg)); }
div.flipper.flipped {
.front { .transform(rotateY(180deg)); }
.back { .transform(rotateY(0deg)); }
}
}
flipper[vertical] {
.front { .transform(rotateX(0deg)); }
.back { .transform(rotateX(-180deg)); }
div.flipper.flipped {
.front { .transform(rotateX(180deg)); }
.back { .transform(rotateX(0deg)); }
}
}
</pre>
<p>
The first five lines of the CSS define some helpful LESS CSS mixins that we're using throughout the rest of the flipper CSS code. They're
mainly there to allow us to quickly build CSS rules without having to type out each vendor prefix, as some of the animation and transform
methods are not an official standard yet. You'll note that I'm referencing my custom directives in the CSS, which is no problem; the browser
doesn't care what HTML tag we're using and will apply styles regardless.
</p>
<p>
There are a couple of fancy CSS rules that are in play here. I'll go through them all and describe what they're doing.
<ul>
<li>
<b>transform-style</b>
<p>The transform-style rule unsurprisingly indicates a style for the transform. In this example, we're only interested in the preserve-3d value,
which indicates that any nested elements should preserve their rendering style in the third dimension. It means nothing without the transform
CSS rule, which we're also using. This is what gives the illusion of the tile flipping. Without it, the tile would appear to shrink and grow
instead of flipping in 3D.</p>
</li>
<li>
<b>perspective</b>
<p>This defines where the orientation and distance the viewer has of the 3D transformation. That sounds a little strange, but basically, it
defines the number of pixels the rendered element is from the view.</p>
</li>
<li>
<b>backface-visibility</b>
<p>This property indicates the visibility status of the reverse side of elements. This makes no sense for static elements on a page, but
in our case, it's important to hide the reverse side so we don't see a flipped version of the tile. In our example, the text "the front!"
is printed on both the front and back of the tile. If we set this property to hidden, only the front side is visible.</p>
</li>
</ul>
</p>
<h3>Conclusion</h4>
<p>And that's it! Thanks for reading. Once again, if you'd like to view these flip tiles in action, you can take a look at the demo page <a href="http://chrisharrington.github.io/demos/flipper.html" target="_blank">here</a>.</p>
</body>
</html>