Essentials • 1:07:44
Safari on iPhone provides a rich feature set, and Mac OS X includes a comprehensive set of first-class tools for web developers. Learn how to bring the two together as you walk through code step by step with Apple experts. Create a compelling iPhone web application that uses cutting-edge features such as DOM touch events and CSS animations and transforms. This session includes a prerequisite download.
Speakers: Antoine Quint, Chris Marrin
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it may have transcription errors.
Hi, everyone, and good morning. I appreciate all of you coming here because it is pretty early. I mean, I'm usually sleeping around that time. So my name is Antoine. I'll be joined during this session by my colleague, Chris. We both work on a great team at Apple and working on implementing these great new features that have been talked about during the week. And today, we're really going to go inside the code. It's all going to be about code, very little about theory. It's really just going to be about seeing how you make all of these new features we have in Safari and iPhone working together.
So it's really about how can you practically integrate all these new great technologies that we have for you on iPhone. So we have CSS transforms, which gives you hardware accelerated 2D and now also 3D transforms right into your browser with no plug-in or any extra, just strictly in your browser. And we also have CSS transitions, which allow you to leverage hardware acceleration for animations done implicitly by simply describing two states and the browser taking care of everything on its own.
And we also have touch events, which really, you know, the iPhone is all about multi-touch. And you have all this functionality now directly in the browser. So using JavaScript events, just like any other events you use on a web page, you'll be able to react to touches and have a really responsive and very flexible UI for your web application on iPhone.
And finally, we also introduced JavaScript media APIs so that you can control the playback of your movies on iPhone using your own UI that you will implement using HTML and CSS and use JavaScript to hook up to the media layer and play content and control it. And all of these things are brand new for Safari on iPhone 2.0. So we actually start with a demo. Today we're going to build a demo that you might have already seen this week. But I'm going to go through it quickly again. So can we switch to the phone, please?
OK, so we load up our demos called Fingertips. And Apple created a lot of videos, small tutorial videos we call Fingertips, so that you can learn about features of your iPhone in a minute. And so obviously all these videos play on the iPhone as well, which is really great. And we created, using all our technology, a 3D ring where we lay out all this content. And you can just navigate through it. And of course, given it's iPhone, you really want to flick through the content. and we can flick at various speed depending on how fast the finger's moving. We can select an item.
get a nice little animated transitions, find out more information about this content. We can go into landscape mode. The content will automatically change a ratio. We can change the layout so that it's more adapted to our content here. We have touch event support so we can do ratings straight onto the device, really responsive, really fast. We can go back to the ring, and as you see, with the power of CSS, just based on the orientation of the phone, we can actually change the direction of the ring And also, you'd have the content we see on there. So we always have the poster frames. We also have a bigger title. But we don't have as much room for a short description. So that's all we got here. Select an item again. And we have movie playback on iPhone, of course. So we can just play a movie.
Oops. And there it is playing straight away. And as you'll notice, we actually provided our own custom UI for this. CHRIS BROADFOOT: is a great way to-- ROMAIN GUY: This is our own little play button. Doesn't look like the play button you get usually by embedding a movie in your page. So that is what we're going to be building today. So back to the slides, please.
And so just to give you a quick overview of how our web application is going to be built, it's actually very traditional. So we have an HTML page, call it index. And this page is going to have all the markup to have the core components of our page. So the items in the info pane, the ring markup, and et cetera. And this HTML page loads up a style.css file, which is a CSS resource where all the CSS declarations are going to be put in that file. We have a main script where all the interactivity is going to be implemented. And finally, we have a data source that we chose to implement using a JavaScript structure. But really, in this app, you could load the data with, you know, XMH, HTTP requests, or any Ajax methodology that you would like. It is all browser-based technology, and any workflow that you've been using so far will work right out of the box with all these new technologies that we implemented.
And so we're going to build this demo step by step so that we really highlight in each step a very crucial part of our technology. So the first step will be setting the stage. It's actually pretty intense because we're going to get into 3D here. And it will be all about how you can take a flat HTML list and actually style it so that it's mapped on what looks like a 3D ring.
So we're going to be using CSS transforms here. In the second stage, we'll be adding interactivity with touch events. So using our fingers, we'll be able to flick through the content, just tap and drag and release. And that will be how we navigate. So we'll be using touch events in this case to add interactivity. The third step will be adding the functionality to slide inside the InfoView. So it will really be all about updating transforms on the fly, all through script, and having the changes transition with a nice animation.
And the fourth step, we're going to add a new behavior to the selection so that the ring rotates before it slides. So we'll be looking at how you can chain different transitions together using events in our CSS transitions technology. And the final step is actually just going to be hooking up movie playback.
And once all this is done, the demo will be complete. And I'd like to invite my colleague, Chris, who's really our main 3D guy on our team, and wizard, really. And he's going to come on stage and demonstrate how we get started and build the first stage. Thanks.
Thanks, Antoine. So Antoine showed you this really nice web application that we're going to be building today. And the central component of that is this really nice 3D ring that contains all these elements that are going to be the contents of the fingertips demo. And so what I want to show you now is how do we build that 3D ring.
3D rings, building 3D things in our CSS properties is really easy. It's something that allows us to position things in three-dimensional space, to rotate them, translate them, scale them. And what I want to show you first is a little demo I put together to show how this ring gets built up. Could we have this monitor, please?
OK, so I wrote this to run on the phone. So let me show it to you in the simulator here. If we go to the first step, we can see what we've got is just the normal xy coordinate space. This is just normal CSS coordinate space. X goes to the right, y goes down. But when we're doing 3D transforms, we add a z transform, which goes out of the screen toward the viewer for positive z values and into the screen for negative z values.
The first thing we add is we have a parent element. This is going to be the parent of all of these items that are going to be populating the ring. It's going to be the thing that we're going to be animating as we spin the ring around. So let me go ahead and get that spinning. I'm spinning it about X, which is going to spin about the central axis of that item. That's the default. Rotations go about the center of the item that you're rotating.
But you'll notice that this doesn't really look very good. It doesn't really look 3D. It really just kind of looks like a trapezoid that's getting smashed down and stretched out. And that's because we don't have perspective on it. If you went to the really great talk on CSS animations and transforms yesterday that Simon Fraser and Dean Jackson gave, you'll see that we have a lot of different properties to support the transform capabilities in CSS, perspective being one of them. Let me go ahead and add the perspective to this. And you can see now that this plane looks a lot nicer. It is foreshortened, and so it looks further away when it's more distant and larger when it's closer to you. So this gives you a lot better 3D look.
So let's go ahead then and add an item to that. This is going to be one of the items that is going to be in our ring. In the demo, it's going to be populated with text and graphics. In this case, I just have something simple on it for illustration purposes. So the first thing we want to do is we want to push this into the middle of the parent. That allows it to rotate also about its central x-axis. And that's what we want. But what we really want is for that to be at the edge of a ring. So the next thing we're going to do is we're going to push that element out in z. So now we can see, if we kind of spin it around, that that item is now pushed out in z by the distance of what we're going to use as the radius. But it's still rotating about that central point, and so it's rotating essentially at the edge of a cylinder. Next step, of course, is just to add another item. And you can see this item, it's been rotated a little bit. It's actually been rotated by 30 degrees relative to its neighbor because we're going to have 12 items around this ring, so it's 1 twelfth of the distance around that circle. So if we push this one out now, you see it ends up next to its neighbor, which is exactly what we want. Now the only thing left really is to add another 10 items so we have the ring fully populated.
And now we have essentially what we want except for one little problem. I'm a big fat liar and it's not actually this easy because I showed you a very nice 3D ring, but this is not the default behavior. If you saw the talk yesterday, you saw that the default behavior in CSS is for it to look like this.
And that doesn't look very good. It actually is still a 3D ring. As you can see, the items that are more distant are smaller and the items that are closer are larger. But it's flattened against its parent. And that's the default behavior in these new CSS properties is for items to flatten into their parent.
Well, that's obviously not what we want. And so we've added a new property, which is called WebKit transform style. And if we set that transform style to preserve 3D, then it takes away that restriction of all of the elements being on their parent plane, and it allows them to sit above and below it. So that's exactly what we want. We've got that. Now, one last thing is you'll notice that you can see the more distant objects, and you'll be able to see this more clearly when we go to the actual application, but that can be really distracting to see those distant objects, especially when there's some nice text up there. So what we want to do is we want to get rid of that using another property that is called WebKit Backface Visibility. We're going to make it so that when the items aren't facing you, when they're facing away from you, they're going to disappear. They're not going to be shown. And so that's exactly what we want. We have a nice ring. And so why don't we go ahead and build that ring right now?
Okay, so here we have in Dashcode all of our files. The three files that are interesting to us are this index.html, which is really very simple. It's got some nice setup. It's going to set up the viewport so that we have a fixed window. We're not going to be doing any scaling 'cause we want the application to look exactly, layout exactly the way we want it to. We have some other pretty standard setup stuff here. And then we have this ring. This is going to be the parent of our content.
If we look at the style, it's all pretty standard stuff. We're setting up the page. We're setting up some pretty simple style. Then we have a bunch of style here for laying out each of the items. Script also is pretty simple. There's some initialization. And then this main function here, populateRing, which is going to create each element, populate it, add its style so that it looks the way you want it to, and then it's going to add it to the ring.
And so let's look at this without any changes and see what we've got. And you can see all the items are shown there, but they're all sitting on top of each other because we haven't moved them into their proper place yet. So let's go ahead and do that. If we go here and grab some code and drop it right here, you can see what this is. It's setting the angle of each element to go around the ring. And then we are positioning each element at that angle, and we're pushing it out by the value of its radius. So just by adding that, we go from a ring where everything's sitting on top of each other to something that starts to look like what we want it to look like. First thing you might notice here is that we haven't added perspective, and so all of the items look like they're very flat. And so let's go ahead and add that. First thing we're going to do is we want to add perspective to the parent of the ring so that we keep the perspective separate from the animations that we're going to be doing on the ring. So let's go ahead and replace this hierarchy with one that's just slightly bigger. So here all we've done is we've added a ring container. This is the thing that we're going to apply the new property to. Let's actually go to the CSS.
So let's go ahead and add the style for that. So you can see this style is going to set the perspective to 1,000. 1,000 is a pretty moderate amount of perspective. It's not going to have a whole lot of foreshortening. It's going to give us about the look we want. We're turning on Preserve 3D. Like I said, through the entire hierarchy, you're going to want to have Preserve 3D so that the items can live on top and below their parent. We're also here setting WebKit perspective origin. What that's going to do is normally the perspective is centered at the center of the item that it's placed on. But since our item here doesn't have a width or a height, we're going to have to set it ourselves. We're going to set it right in the middle of the screen.
The reason we don't have a width and a height is because we don't want this element to interfere with the interaction that we're going to be doing later when Antoine shows you how to interact with this. And so now we've added those two things. Let's see what we've got. Now you can see we've got something that looks pretty 3D. Things in front are bigger than things behind. But then, of course, we can see those things behind, and that's not what we want. turn on the back face visibility to hidden. So let's go ahead and do that.
So we've just turned on back face visibility hidden here. If we look now, looks the same, except that you're not seeing those back faces. We're starting to look pretty close here. And so a couple more things that you're going to notice is that the text, especially this text right in front, looks pretty fuzzy. It doesn't really look very good. And that front item is too big.
It stretches out. And that, of course, is because we've pushed it out by the radius, and so it's closer to the viewer. And so it's going to look larger because of the perspective we've added. And so what we want to do is we want to push that back, because that item is going to look best if it's at the z equals 0 plane. We've kind of laid it out to look best there. And so we're going to push the entire ring back by the distance of the radius so that the ring is still going to rotate about the center of the ring parent, but it's going to be pushed back so the frontmost item is at the z equals 0 plane. To do that, we're going to have to, again, add another layer of hierarchy here so we have something to add the pushback to. So here, in addition to the ring container and the ring, we've added this thing called ring aligner. That's just going to be a static transform in z. So let's go ahead and add that.
Sorry. Let's try that. So here we have a style for the ring aligner. It's going to also be preserved 3D so that we can make sure that we maintain that 3D hierarchy. And all we're going to do is we're just going to translate it in Z by this amount, which happens to be our radius.
So now when we look at it one more time, now you can see that frontmost item. It looks sharp. It looks crisp. And it's filling the screen just fine. So this looks pretty close to what we want. There's one last thing that I need to check out. If you look at the ring, you can see that in the ring aligner and the ring container, I added preserve 3D. But the ring style, which was preexisting, that doesn't have preserve 3D. It looks fine because we haven't started animating. But once we start animating, you'll see that all those elements are going to be flat unless we add a Preserve 3D to the ring too. So let me do that so that when Antoine starts animating, everything looks good. You can see we've added it. We go here now. Looks the same, but when we animate it, it's going to start looking a lot better. So let's go back to the slides real quick and let's summarize what we've done.
That's what we just did. OK, so the properties that we've looked at is 3D transforms. 3D transforms are the basic property that you're going to use to position things in three dimensions. You can position things like we did, rotate them like we did. You can also scale. We didn't really have to use that today, but that's available too. You can also combine all these, as you've seen.
And in support of that, we have WebKit Perspective. We've used a WebKit Perspective that was fairly small. You can use some perspectives that make things look very, very warped or whatever kind of look you're looking for. We used one that's pretty moderate. So it looks 3D, but it doesn't kind of get in your face too much.
And then we've used this preserve 3D flag, which basically allows you to make sure that things that are positioned in Z actually live in Z. And it's hierarchical. And so you can have things that are positioned by one element in Z and then positioned differently so that you can animate around different armatures and have some pretty complex 3D hierarchies. And finally, we hid the backfaces. In a demo like this is what you want. Sometimes you don't. Sometimes you want to see the back faces, but a lot of times, this really helps out. And so let's -- we stepped into the code. Let's go to the second step, and I'll give you back to Antoine.
Thanks, Chris. So I hope you got all that, because it's not the easiest part. From now on, it's going to be a lot easier, and we're going to do a lot more stuff with a lot less code, actually, because it's really the power of what we implemented. It's really easy for you to get things done. So the second step is, like I said earlier, we can add the first bit of interactivity. We want to be able to drag around the ring. And so Chris has already done the main setup.
The ring is laid out in 3D. Preserve 3D is preserved on the ring. So if we change the rotation of the whole ring, the thing will move in 3D, and everything will look exactly like we intend to. So what we really need to do now is we need to listen to touch events. Like I said, in Safari on iPhone now, we have multi-touch events straight into your JavaScript using DOM events. And reacting to these events, we're going to figure out what the delta is between each interaction and update the rotation of the whole ring as we drag along.
So let me tell you a little bit about how touch events work with the DOM and JavaScript in your web content. So the first thing you're going to want to do is on each of our elements, we want to add an event listener and start listening to the touch start event. Touch start is one of the new events that we introduced for touch event support in Safari and iPhone. And this will tell us the user has put his finger on the screen. And at this point, we'll want to start listening to all the other events to have the dragging interaction working. You'll notice the second argument to this function, which is a standard DOM function at event listener, is an object, and this object will conform to a protocol from DOM events, which is event listener protocol, and that means that if we implement and handle event method on this object, all the events that we use will be routed to that function. So we use a single scoped method to handle all our event handling. So we'll look into that in more detail in a little bit when we get back to the code. So we're going to start by having a touch start. User touches the screen. At this point, register new events for touch move and touch end. So touch move is when the user is going to move around the screen while keeping pressure on the screen. And finally, on touch end, we'll do some cleanup after our interaction when the user lifts the finger of the screen. And all this interaction is going to go to a single function, which is handle event on a controller object, because we implement the right event listener protocol from DOM events specification. So this is basically how we're going to work all of this. So if we get into the code-- and I'd like to switch to a demo machine, please-- we can get started doing this right away. All right. So let me get rid of the previous version of the code that we have. Load up the new project in Dashcode.
OK, so we're really starting from the same amount of code as we had before with Chris. So we have our JavaScript here. We still have our HTML, which is populated by script like it used to be. And the first thing we got to do, like I said, is we need to start listening to touch events.
So while we're populating events and setting up the transform for the individual items in the ring to have the right rotation, et cetera, we're actually going to start-- We're actually going to start by adding an event listener on each of our items. So this is pretty much the code that we saw on the slide. We register an event for touch start. We pass the ring controller object as the object is going to take care of the events. And if we take a look further down in the file, we do have our ring controller already set up. Ring controller is going to really control the whole interactivity in our ring. So we have a field, a single field, to track what the current rotation is. And that field will be updated as we call setRotation, which continuously as we implement the interaction, which will track the rotation and also apply it to the ring using WebKit transform.
That's what the setRotation method does. And of course, we implement the handleEvent method, which is the method where all the events are going to go to as we register them. So what we'll do is we'll just branch those events into separate methods so that when we get a touch start, we'll call it interactionStart. Touch moves, we'll call it interactionMove, and so on.
So there's another thing we're going to want to do here is, because we're going to implement selection later on, I really want to track the index of the item that will be receiving the interaction. So we have an index as we go through the ring. And this is mapped to the data source that we have. We'll look at that a little bit more later. And we'll just track that on a custom JavaScript field directly on the node in the DOM. So when we will be done with selection behavior, we'll be able to go back and look at that index and figure out what was touched and know that this is the item that it. So we're done with the initial work here. And we're all set up to react to touch start events. So now we really have to go into interaction start and start writing the code that we need to get this dragging interaction working.
So there's a few things we're going to do here. First of all, we need to track some start stage for the interaction. So we need to start tracking where the user touched on the screen to get the interaction started. So usually with mouse events, you just go event.pageY or clientY, depending on what information you're interested in. In our case, we're dealing with touch events. And on iPhone, we have multi-touch. And so if the user uses three fingers, you'll need to get various coordinates to find the coordinates of all those fingers. So you now have a new array on the event object, which will give you access to all these touches individually. So we just care about the first touch here. We're going to do one finger dragging. And we're going to get page y, and that'll be our start position for rotation. We also need to track what the current rotation was before we started the interaction, So that as we go along, we can just build on top of that. And we also need to track what the angle difference was for object compared to the center so that we have a really accurate rotation.
So now we're done tracking this base information. We want to track now the touch move and touch end event listeners so that we can really have callbacks every time the user moves the finger. However, you notice that we do not register the events on our item in the ring. We're actually going to register the events on the whole window. And we do that because if you want to have a good drag and drop interaction, you want to be able to drag the finger anywhere on the screen and have the interaction follow along. So even if we take the finger out of the object that started the interaction, we still want the ring to keep going and rotating. So in this case, we add event listener on window so that we always get events wherever they happen on screen. We pass this as the handler object. So once again, everything will go through ring controller and the handle event method. And finally, we set capture mode in our event handling.
That means that the event is going to go straight into the window, and we'll be able to handle the event as soon as it happens. And you'll notice we'll be using that in a little while just to make sure things work very smoothly. So now we are ready to start listening to touch events. But we also need to implement, of course, what happens when a touch move comes in. So this all happens in our interaction move method, which was routed from handle event.
So let's drop the code in here. And what we're going to do here is reminiscent of what we did in touch start. we need to figure out where the current y is. Because based on that current y, we'll be able to subtract that to the previous y and figure out how much delta there was in our interaction. So what we do here is we take that start y angle, and we subtract to that the new y angle for this position so that we're going to get the exact angle delta between the first interaction and the current interaction. And once we figure out what this angle delta is, just call setRotation, subtract the new angle delta to the startRotation, and that will give us our current rotation for the whole ring.
So before we-- we're almost done here. Technically, we'll be rotating along as we touch along. But the problem is we also need to clean up when the interaction ends. So-- oops, sorry. At interaction end, we really need to just remove the event listeners that we placed dynamically. So we don't want to listen to touch move anymore, and we do not want to listen to touch end anymore.
We still want to listen to touch start, because we'll need that to start again when we drag in another point later on. So we're going to keep that intact. So at this point, it seems like we've done all that we need to do for our rotations. We're ready for touch move and touch ends, and we're updating the rotation as we go along. So I'm going to touch on this and start dragging. So that's not exactly what I want, because we do get a certain amount of rotation, but we also see the whole page panning.
So obviously, this is a web page. And web page in Safari on iPhone have a built-in behavior that when you drag along the page with your finger, you will actually be panning the whole page. So that becomes really handy in a web page. You do not want to add this whole interaction yourself in your content. This is why Safari on iPhone is so great. But we are building a web app here. And we're building pretty advanced UI for it because we have all these great new features to leverage. So we need to just tell Safari on iPhone to give us a little bit more control and let us take care of how drags on the screen are going to work. So the only thing we have to do here is get back into Interaction Move and tell Safari and iPhone, I'm going to take care of what happens. Just do not do the default behavior that you will do here.
And luckily, there is already a standard method on even object, which allows you to say, whatever the default behavior for this even is, just do not do it. So we call preventDefault on all the moves in a reaction. That tells Safari not to do the panning. So let's start again. And hopefully this time we'll have proper drag and drop going. Yep. It really looks like it this time. So that is all we need to do for step two. Can we switch back to the slides, please?
So let's take another look-- whoops, sorry. We're done with step two. So now we're done with the dragging interaction. We want to be able to tap on items in the ring and slide into the info view. So you probably noticed that current web applications actually do that kind of effect on iPhone. But to do that with really what can be considered as a big fat hack, they just scroll the whole view around. And they get decent performance with it. And in our case, we already have WebKit transform, which are great hardware-accelerated transforms. And we also have CSS transitions. And we really want to start leveraging this so that we can have smooth interaction going in and out. And we'll be able to do that just as many times as we please.
And it's actually going to be pretty simple to implement. So our goal here is we want to detect selection. And we're not going to use clicks, because we want to be a really responsive application. So we want to use touch events to figure out as soon as a tap happened on our page. And we want to be able to slide the ring out and the info pane in, and the opposite when we want to get back to the ring. And so to do that, we're going to track touch moves to figure out when a selection happens. As a tap is really going to be touch start and touch end events happening one after the other, no touch move in between.
So we're going to be tracking touch moves to make sure to identify if we have a selection or not. We're also going to want to set up CSS transitions so that when we update the transform of either the ring container or the info pin container, that we have a nicely hardware animated transition going on automatically. And finally, from the script, based on the interaction, we'll need to be able to update the transforms on each of those containers so they actually get transition in and out of screen. So let's get back into the code and switch to the demo machine, please.
All right, so we're going to quit this version of the project and load up a third step right here. So there's a bit more code that's already in here. It's not that interesting, so I thought I would spare you. But let's go and take a quick look. So we're going to introduce the InfoPane here, the InfoView. And we have all the basic HTML for it already laid out. So we have a back button. We'll have an info image, which is like the snapshot of the movie that we'll be playing. Some metadata, a wrapper for the title, duration, date, and the description. All of this is going to be populated by JavaScript in a single new function that we introduce, which is called updateInfoPane. And based on a current index that will be updating global variable, we'll get into the data source, which is a simple little JavaScript file which has little objects in an array with title, field, desk, and et cetera. Based on that, we'll be updating all the elements in the DOM to content from the data source. So this is not really interesting. That's really pretty standard functionality. And we have a little bit of script, of course, to take care of all of that. So just info title, info description, all these are going to be laid out, font size set, and et cetera. So we're not going to worry about this right now.
we have to focus on the interaction here. Because the first thing we've got to do is we've got to figure out if we have a selection or not. So like I said, we want to detect that by figuring out if we have a touch start and a touch end happening right one after the other. We do not want any touch move happening in the middle. So to do that, we're actually going to do a simple thing, is that when the interaction starts, we're going to want to-- track a new object that's going to figure out if touch moved has been set to true or false. So each time an interaction starts, we're going to say no touch moved so far. We just started interaction, so touch moved is false.
And we also want, because we're going to do selection, we want to figure out what item we touched so that we can track it at the end of a written interaction and say that was the item that was touched, get to the index on it, and figure out what item needs to be populated in the info pane. So we're just going to track the current item as well in the scope of our object, in a controller object. And that will be just the current target of the event. That means the element that was tapped into our code. So that's it for interaction start. But of course, in interaction move, you probably guessed what we're going to do here. We're just going to update touch moved.
to true. So now we know that a move had happened during our interaction. And when interaction end is called, when touch end event comes up, we'll be able to figure out if we had a selection or not. And this is only going to be based on whether the touch move is true, in which case we do not want to perform selection, or if it's false, in which case we have a selection. And we're going to call item selected. So item selected is going to be called with the new node that was selected, which we tracked when we started interaction, current item. And this function was already defined in our script. This is the callback basically saying, I've detected selection, here's the item that's selected, work your magic so that we can have the info view come into the screen with a nice little animation. So what we're going to do here is we're simply going to update the current index global variable and use the item that was selected and the index field on it. So if you remember from the previous step, when we registered touch events on each item will also track what index it was in the data source. So we're taking that now, setting in the global variable current index, and directly call update info pane so that the info pane is going to be populated with our new content. So the pane is ready.
And all we've got to do here, surely, is just to update the WebKit transform so that the ring container will be to your left of the screen. And the info container is going to be right smack in the middle of the screen. So we just update the WebKit transform on the style object, the CSS style declaration object. And we'll pass in a translateX string, which we just commoditized in a little function. It just generates translateX with X in the middle and PX for the units. And this should be all we need to do, right? So let's just run it and see what it does.
So we can still drag around the screen. And if we release, nothing happens, because we figured out this is not a selection. However, if we just tap, we got a selection. And we went straight into the InfoView. Well, OK, it looks fine. We have content based on what we just touched. Everything was updated. It looks pretty good. But what happened to our transitions? We did update the transform, but we need to say, well, we need to tell the browser, I need this transform when it changes from a state to another state to be transitioned with a nice, smooth animation.
So it's probably going to be a lot of work, right? I mean, if you did this in JavaScript, you'd take a lot of code, have to manage your own timer. It'd be pretty tedious. But fortunately, we did a lot of work for you here. And all we've got to do is declaratively in our CSS just say, when this element changes WebKit transform, animate this change of state during a certain time. So let's start with our info pane. Info pane, we're changing the transform. It has a base transform of 320 pixels, which makes it out of the screen so we don't see it. And what we're going to do here is we're just going to add two lines of CSS. And the two lines of CSS just say, I want the property WebKitTransform to be animated. And I want this to happen across 0.5 seconds. You can put in any old time you want here. So that means now every time we're going to change the transform, our info view is going to slide as opposed to just snapping.
And we didn't really do anything here. It's really about thinking what you do on the high level. You just change the value and let the browser work the magic to make it all happen, animated and very smooth, because all of this is done in the GPU. And so we also have to do it on the read container, because both elements need to slide together. So we'll do the exact same thing, dragging a little bit of code, set up WebKit transition probability with WebKit transform, and set up the duration to be 0.5 seconds.
So that's probably all we really need here. So let's run it again and see what happens. I'm going to drag around. Nothing happens. Hit Select, and things slide in. So we really didn't do that much extra work to get the animation, just a couple lines of CSS. There's still a little bit of work left to do. We want to go back. And we have an item here on screen, but we didn't hook up any behavior to it yet. So going back to the script, we have the button in our HTML, and it's called infoBackButton. We had an even listener for touchend. That means the touch was released on it. And we want to call goBack at this point.
So Go Back is all set up for us. We just need to do a little bit of magic here to do the inverse of what we just did in item selected. So item selected is here, which is update Wacky Transform and to a new translate X. And so here, we're just going to do the exact opposite.
So now the ring container is going to go back to zero. And the info pane is going to go back to screen width, which means that its left will be aligned to the right of the screen and essentially won't be seeing it anymore. So that should be pretty simple. Do it again. Select an item, and we can go in and out very easily.
And so that concludes step three. We did a lot of work in not that much code, actually. Just a little bit of script to figure out, detect the selection, just updating transform, and a little CSS to say we want the animation happening on transform. Can we jump back to the slides, please?
So to recap, the core things we need to do is set up CSS transitions so that the animations happen automatically as transform properties change. So you want to set a base state for this. A transition happens from a value to a new value. So we had translate exit to zero on the ring container when we started. And we also set the WebKit transition property to WebKit transform. And the WebKit transition duration is 0.5 seconds. If you got that, any change of WebKit transform point on an element will just animate.
And the second thing was a beta transform from script. So it's really cool how we define anything in CSS declaratively, but we still can do everything based on JavaScript. So any old context-related interaction based on things you need to evaluate in script, you can do all that with no problem, because you can change the style from the JavaScript. So in our case, that's what we do. We just change the translate to slide things in and out screen. And that concludes step three. It was really pretty straightforward.
Now we're going to want to take it a step further. When we transition things to the info view, we kind of lost context of what was activated in the first place. So we tap on an item at the top of the screen, we go into the info view, and we go back, and we're not really sure where we hit anymore. And we lose context. And it'd be really cool if we could just rotate the selected item to the center before we transition into the info view. And that is exactly what we're going to do. I'm going to tap on something, and it's going to center the ring to center the item.
And so the key thing here is that we want to be able to do two transitions one after another. First transition, rotate center. Second transition, do what we just did before. Slide the info view in and the ring container out. So it's actually a bit tricky to do that, because we give you a lot of power by just doing all the transitions automatically in our code. But to make sure the transition happened very smoothly, we actually have a lot of special code we built in. And what it does is that it's only going to start transitions when it knows that the content is going to be animated, is ready to go. And what I mean by ready to go is I mean, it's fully rendered. All the JavaScript you need to populate it is done, and et cetera. So in our case, as you remember, the info view is populated dynamically on each interaction, each selection interaction. So basically, the thing here is that even though you told the engine how long the transition is going to be, you're not sure if it's going to start right away or in 10 milliseconds or in half a second, depending on how big the tasks you have are. And also, I mean, we have a great, really accurate timer into our hardware accelerated implementation. But if you wanted to have a callback happening a certain time in JavaScript, you need to rely on a JavaScript timer. And these may not be in sync.
So what we did here to make sure that you always have synchronized behaviors based on your transition is that we added a new dummy event, which is called WebKit transition end. And based on that event, you can tell that a transition has finished, and you're ready to go to do something else. So based on that, we're going to listen to that event, and we're going to chain transition. We're going to start the first rotation through the center, and once it's done, slide the info view in. And so we're going to get back into the code here. So I'd like to go back to the demo machine, please.
And hopefully we can do that without too much trouble. All right, so, load up first step. As you notice, we're almost done here. I mean, just two steps to go. You almost have the hook. And we already have a little bit of code already set up for us here. We have a new init function on the ring controller because we're going to need to do a new thing here. And that thing is that we need to set up transition and event on our ring container.
So this function is going to be called just once. The same time we register touch event handlers at the very beginning of our code, we now need to make sure that we listen to WebKit transition end events. We only need to do that once. We don't need to do that dynamically each time we transition in and out.
To do that, we just slide in a code, and we just add a new event listener on our ring, WebKitTransitionIn, and it's always going to go back again to our ring controller, which is this object in this context. And this means that to be able to take care of it, we're going to need a new case in our switch statement. So that's pretty easy. It looks like any other switch statement, case WebKit TransitionIn. And we're going to want to call a new function called settingSelectionTransitionDone.
So a few other changes we made to this code. Because the selection behavior is going to be a bit more complex than what we did before, we're now going to call a new function called perform selection, which is empty so far. And instead of having a single callback on selection, which would automatically drag the info view straight in, we actually want to do two things. We want to have a callback for when the ring starts animating to the center and another callback when it's done with this and ready to actually end the transition and go into the info view. So we now have two functions. We have item being selected, which is going to be called as soon as we get the touch-end event and the ring starts to rotate. And we're going to have item selected as well, which is called when the rotation is done, and we're good to go for the rest of the animation. So item selected looks exactly like it looked before. That's where we change the transform of the different containers to slide things in and out. We already know how to do that.
But the code to populate the info pane has been moved to the item being selected. We already know what's going to be selected, so we want to update our content straight away so there's no delay. All right, OK. So you also notice that we get an extra parameter called isImmediate, because if we hit an item that's already in the center of the ring, we're not going to have any rotation. So we just want to go straight to itemSelected and update the infoPaint. All right, so far we haven't really done anything. We really need to get started on performSelection here. So in performSelection, we're just going to start another transition from script. Sorry, what have I done here?
OK. So in perform selection, we want to update the rotation of the whole ring. So we already know how to update the rotation of the whole ring. We have set rotation built up for that. We used that when we did drag in around the ring, which is called set rotation all the time. Because set rotation just changes the WebKit transform of the ring. So what we have to do here is we're going to figure out what the new rotation is going to be for the object that we selected.
So we look at the index of the current item, multiply it by a fraction of the full rotation, which is going to be based on the number of ports that we got. So it's really going to be-- we've got 12 ported, so it's going to be 30 degrees. And that tells us that's the rotation that we need to center that item.
So we figure out if it's an immediate selection by just checking if it's already what the current rotation is. In that case, we don't really need to do anything. We call itemBeingSelected, our callback to start the transition. That's going to populate the ring. We already saw that. And in case we do not have an immediate selection, we want to do a transition for the rotation. So that's easy enough. All we've got to do is we dynamically set a WebKit transition duration to 0.5 seconds. That means now transitions can happen on the ring, and just set the rotation. You'll notice that at this point, we did not set WebKit transition property. And that's because by default, we actually took the liberty to make all properties transition automatically.
That means that any change you do on your code is really just adding transition duration to your CSS, and then all the transition needs to happen will already happen out of the box. You don't even need to worry about what gets animated. So that should be just plenty of what we need to do here to actually get the rotation going. But of course, we want to do something special when the transition ends here. And we already set up in handle events so that we listen to Webkin transition end and call selection transition done. See, remember, we just did that right up here. Webkin transition end, call selection transition done.
And so what we're going to do here is just say, rotation selection is done. We want to go to the next step of the transition, which is sliding items in and out. And that's done by our item selected callback, just like we used to in the previous piece of code. Update ring container and info container. So it looks like we're done here.
So now when we select, hopefully it's going to rotate to the center and slide to the rest of the content. And that works perfectly. That's great. So now we can drag again. Oh dear. Things are going south here. I mean, the ring, it looks like there's a delay as we drag along the ring, huh? Isn't there?
So the problem here is that we did set the transition on the whole ring here. We did say Webkin transition duration 0.5 second. But when we drag along, we do pretty much the same thing. We update the rotation. That means from now on, every time we drag along the ring, we're actually going to have rotations going on on each change of the value. And because the transitions have a nice little easing animation behavior, it's actually going to look like there's a delay. Because it's really changing a bunch of animations, like a lot of them. Every time we change position of the finger, it's going to call the new transition. And that's not what we want to do. We want to kill transitions when we're done with the rotation transition here. So it's actually going to be pretty easy, because the way we turned on transition by setting transition to 0.5 second, we're just going to do the opposite and set it back to 0 second. So in selection transition done, when the rotation is done, we want to reset the behavior and not have transitions happening anymore.
So we just set the WebKit transition duration back to 0 second. And hopefully now we can have a nice transition, go into the InfoView, go back, and get back to dragging our ring with no side effect. And that's what we need to do for step four. We really unleashed the power of CSS transitions here by not just having declarative behavior, but also have nice little hookups into our script. So I'd like to go back to the slides, please. And just quickly recap what we just did here.
So we're changing transitions. And to do that, we just set up an even listener to begin with, WebKit transition end, transition is over. That's what we got here. And just call code based on that callback to do whatever it needs to do when transition is over. It'd be really convenient, for example, if you want to fade something out on your page and remove it completely from the page to set display none or remove it from the tree when the transition over. Stuff like that really gives you control to exactly what you want to do here.
And toggling transitions, well, there are different ways you can do it. You can set transition duration like we did to a positive value and set it back to zero to stop transitions from happening. You can also change WebKit transition property to be a name of a property like we had before, WebKit transform, or just be none where you don't really need anything else going on.
And that really wraps up what we needed for step four. And so the last step is going to be hooking up movie playback. And it's actually going to be the easiest step. Even though it looks like it's doing a lot of stuff, the new JavaScript APIs we added in Safari and iPhone makes this really easy for you to build your custom UI and hook up into the media layer on iPhone. And so all we got to do here is when we detect a touch hand on the Play button that's overlaid on the image, just tell our movie to play. And a movie is just going to be an embed element.
That's what we always do on the web to play movies so far on iPhone. And we're going to use the JavaScriptMedia APIs to just say, well, here's the URL for this movie. We'll load that from the data source. And please play it now. And so that's what we're going to do now by getting into the code. So I'd like to switch back to the demo machine, please. Close our previous project and go back to the last step.
So we do have a little bit more code set up here, namely in the HTML. We have our play button, which is just the image, a nice little icon, custom icon that we made that's gonna be overlaid on top of the image. So the cool thing here is that even though it's a static button here, if you really want to get fancy, you could have, like, a big throbbing animation on your button using CSS animation so that it's really look alive. And that's the kind of thing you couldn't do with built-in controls of movie playback in iPhone in previous versions of Safari and iPhone. But we're adding new features here so we can have our own icon to do movie playback. need to have an embed element to actually-- that will hook up the media layer right here. And so our embed, we just give the mind type of the video and give it a little handle movie so that we can get to it and call JavaScript APIs on that element later on.
So it actually looks like we already have a bunch of stuff ready for us to go. So let's look at what it does now. The ring looks the same. If we go into the ring, we'll have a nice little play button. But we do have a little problem here is that we see an icon here. And that is because embed on Safari on iPhone has a bunch of default behaviors already hooked up. So that when you just say, here's the movie I want to embed in my web page, you don't need to do extra work to provide UI controls anything if you don't want to. If you just want to use a built-in behavior, that's fine.
But what happens here is that instead of showing the poster frame for it or the play button for it that the embed element would show by default, because there's no SRC set up on our embed, it actually shows an icon saying, I can play that movie. And we don't want to set up an SRC attribute yet, because that's going to be done dynamically as we select elements in the page. So the first thing we're going to want to do is make sure this doesn't show up.
And we can do that really easily. It's a bit of a trick-- actually, in the CSS, sorry. It's a bit of a trick, but I feel pretty comfortable at this time doing these kind of things, because what we've done so far was all so clean. So select on the movie, and all we're going to do is push it to the left of the screen. That means it's always going to be on the page, but it's just never going to be visible. 320 pixels out of the screen, it's just going to be out there, and we don't have to worry about it. So let's reload our project. And we don't have anything getting in our way. So that's really cool.
So we did a little bit more work already in the script. Just like we hooked up touch events on the back button in the previous step, we do the exact same thing on the play button. Get a handle to it with document.galvinbyid, add event listener and touch end, and call the play movie function.
So we already have Play Movie set up for us right here. And what we need to do here is really just set the source of the elements to be the new URL for the movie that we selected and call play because we're ready to go and we need to just get into movie playback mode. And so to do that, I'm just gonna drag a little piece of code here and it's really three easy steps. First step, get a pointer to the movie. That's our embed element in our HTML. And this is where all the new JavaScript media APIs going to be exposed on. So on this element, we'll be able to call new API straight away. So we get back into our data source with the current index, get the movie URL, and set that URL using setURL. That's a new function in Safari and iPhone 2.0. We can now dynamically change the URL of movies right using JavaScript. And now the URL is set, all we want to do is actually play the movie. So hopefully we're done here.
At this point, we get really nervous. I mean, what if everything goes wrong, and as the last step, I'll look really bad? So hopefully, we're going to click on there. And you'll notice that we actually have a little touch state here for our button. So this was actually done simply in CSS by having on our info play button an active state. And every time you're going to touch on an element, if you implement the active pseudo class, you'll be able to change properties of the element. So in that case, we changed the background image to have the little button ring. So that's actually pretty cool. So let's try and click it again. We have our nice touch state. And if we release it, we get into movie view. And we just play a movie straight out from the internet.
And that's all we needed to do. We can actually play it, which would be even better. And once we're done, we can just reuse the current UI that's already exposed for us and just get back in our presentation. And we're done. The code is finished now. And we have all the features we wanted to implement. So can we go back to the slides, please, so we can wrap it up? Thanks. Thank you.
So just to recap what we did here, get a pointer to the movie elements, which is an embed, and just call setURL and play. That's all you got to do. There's a lot more stuff you can do with this. You can get events when you get in and out of full screen view. You can detect when a movie is done playing so that you can play another one. You can do playlists like that.
I mean, there's a lot of stuff you can do. I mean, you have to go and dig into that because it's just fantastic. And so to wrap things up, what we did here is that we put together all this cool stuff we got for you in Safari on iPhone 2.0. We use CSS Transform to leave the ring out in 3D. Obviously, this is a first in any kind of web content strictly based on web standards.
And that's coming first on Safari on iPhone 2.0. And we use Touch Event as well, obviously an innovation, again, of iPhone, because we're the only device which has this great multi-touch display. And that's what gives you the opportunity to create those really advanced interactions in a web app.
And the third stage, we're adding CSS transitions to automatically have a state-based hardware accelerated animation going from one state to the other to slide things in and out. And the fourth step, we took that thing further and add the advanced support for CSS transitions to actually change transitions based on events. And that was really just fantastic. And finally, we just played movies using JavaScript media APIs. And once again, all of this stuff is new in Safari and iPhone 2.0.
So looking forward, we have actually a bit more features implemented in the main demo that we saw at the very beginning. And all that code is actually going to be available to you. I'll give you the URL in a minute. So you'll be able to actually get the code we've just done here and actually add all these great features if you're really feeling adventurous and feel you have a knack for it all. So we had a selection highlight. When you tap on something, it had a little blue background. That's really pretty easy. You just change the background color of the items in the ring. There's really nothing fancy here.
Landscape mode, again, is pretty easy. You just listen to orientation change events in your script. And you can just change the CSS properties for the ring to change from a vertical ring to a horizontal ring. Just relay out your items. And we actually did this all in CSS by just applying different selectors.
We also had really great flicking. So that was actually a bit too much for this session because we didn't have time to put it all in. But using CSS animations, which are different from transition because you have key frames and you can really specify what the animation does, you can just flick, lift your finger, and just let the ring animate based on the interaction data that you just recorded.
And we also had great things with selecting a rating with touch events. So it's pretty obvious, and it's really the same kind of interaction that we did in the ring where you want to track touch moves and et cetera. And we also have really advanced hit testing in our demo because we had really drop shadows on top of the ring at the bottom and the top so that we can really have more depth to our presentation. And because we have stuff on top of the ring, we don't get even straight into the ring. We get it on other elements. So to figure out what we hit, we actually have our own custom hit testing based on the 2D point APIs that we get from the touch events and map them out to the 3D content in the ring because we have special point conversion APIs. And so the code for this is all available for you right now for free on the developer.apple.com website, the attendance site for WWDC. So I'll let you take a minute to actually write this down. And if you navigate to the session 391, which is our session, you'll get the code link straight from here as well as a lot of documentation for all the technology that's relevant to our presentation here. So developer.apple.com/wwdc/attendee.
And so I really hope you can take this home, look at it, review all the code we've done. You can download the podcast later on, and just get a good feel of how everything was done. For more information, we have great evangelists. We have Vicky, who really helped us come up with this session. Mark Malone as well. Contact these people if you need any help with this or want to find out more.
And we have a coming session. We have debugging websites using Safari's integrated developer tools, where you can find out how you can do these kind of things and go into bugs and actually solve them, because it's not always that easy. And also, it's a great session about improving responsiveness of website and web applications, where we'll be focusing on performance.
And there were other sessions this week that are already passed, but you really want to check those out when the podcasts come out on iTunes and download them. And if you missed any of this, you can go back to the fundamentals of what we talked about today. And there's a great live this afternoon directly related to this session. So I hope you can come and stump us with great questions. All the team will be here-- Simon, Chris, Dean, myself, Barry, and our manager, Gilles. So come around and just ask us anything you want to know to make this happen in your own application. And we're a bit short of time, but I think we can open the floor for Q&A. Thanks. Just didn't have time to go. Do you still want to do it? No, whatever you want to do. Thank you.
So actually, we have a last demo. Because we had some technical difficulties at the beginning of the week, there's a demo that we couldn't do at a GriefFix in Media State of the Union, which is actually pretty amazing. I mean, it's just a lot of work put into it, and it's fantastic. So if you can go back to the phone here, please. And Chris will show it to you right now.
There we go. Okay. So we love 3D. 3D is such a fun thing to put into Safari on iPhone. And so we wanted to see how far we could push it. You know, rings, that's fine. Practical UIs, that's great. But we wanted to see how far we could take it.
So we went and did a little demo here. This is a map of the world. You might notice that it's -- you can kind of see them. It's actually made up of a bunch of little these are just background images, and they just make a nice flat map of the Earth. But of course, if we work at it, since we have CSS, transitions, transforms, we can reshape those tiles into a very nice looking glow. And in fact, we can style -- We can add some nice shadowing so it looks a lot better. And then we can add -- these are just little pop-ups that have some straight HTML/CSS formatted information. And it's just, you know, where people went, where people have been, what they've been doing. We can have that or we can also touch down and we can have direct interaction with this globe. We can flick the globe and have it spin around. We can look all over the place, you know, see where different people were. And then we can let go and we can let it go back into its automatic mode and see where other people were. We can also go to one particular little information pop-up here. And what it is, is this is Peter. He actually was in New York. He actually was in New York. And he sent us a little video that we can show.
You might have seen this already, so I won't go through the whole thing, but it's a very fun little short video. And also, we can look at some pictures. This is another thing that we put together. You've probably seen this in other sessions this week because it's very cool and a lot of people really enjoy it. Basically, what it is is it looks like just a regular picture, but what it actually is is a panorama. It's actually six images put together in a box, in a cube, just using CSS transforms and transitions to do the spinning. And it allows you to look in all directions. You can actually go and grab a lot of different panoramas that are cube mapped, six different images, and apply these and use any one you want. And so this is something that we put together just to show how far we could go with the 3D. And that's what I wanted to show you.