Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Zooming & Panning with CSS in IE 10

Blogs

Mike Taulty's Blog

Elsewhere

Archives

Something that I’ve been experimenting with just a little lately in the IE10 Preview (available on the Windows Developer Preview) is the idea of how simple HTML content (e.g. a DIV or an IMG) can respond to touch events in order to support the sort of gesture based interactions that a user might expect to use on a touch-first device – specifically here for zooming and panning.

Where touch isn’t the primary input mechanism, the same interactions can be driven by mouse/keyboard.

In the past, I'd have expected to write code to provide the sort of functionality that IE10 supports via some (currently vendor specific) CSS attributes. These make it easy to enable scenarios that are common and simple to use but aren’t necessarily simple to implement.

Zooming

Let’s say that I’ve got a really simple piece of “UI” represented by this block of HTML;

<html style="height:100%">

<head>
	<style type="text/css">
		body
		{
			height: 100%;
			-ms-content-zooming:none;
		}
		#grid
		{
			display: -ms-grid;
			-ms-grid-columns: 1fr 8fr 1fr;
			-ms-grid-rows: 1fr 8fr 1fr;
			height:100%;
		}
		#container
		{
			-ms-grid-column: 2;
			-ms-grid-row: 2;
			overflow: none;
		}
		#image
		{
			width: 100%;
			height: 100%;
		}
		#text
		{
			-ms-grid-row:3;
			-ms-grid-column:2;
			-ms-grid-row-alignment:stretch;
			-ms-grid-column-alignment:stretch;
			background-color: #FF0000;
		}
	</style>

</head>

<body>


   <div id="grid">
        <div id="container">
            <img id="image" src="img0.jpg" style="width: 100%; height: 100%" />
        </div>
	<div id="text">this is just some text to check for zoooming</div>
  	 </div>

	</div>
</body>
</html>
which displays;

image

With just this HTML in IE10, if I use my touch screen and try and do something like a pinch gesture on the content then the browser does nothing. For example, if I do a "shrink" pinch then nothing happens. Note that this is because I've set the -ms-content-zooming on my body element to none – otherwise the browser would have scaled the whole content and I wanted to start from a clean slate here with no zooming anywhere.

That HTML has a DIV that contains an IMG and if I want to do something "active" in response to a zoom gesture on that DIV then I could say so via my CSS;

	#container
		{
			-ms-grid-column: 2;
			-ms-grid-row: 2;
			overflow: scroll;
			-ms-content-zooming:zoom;
		}
changing the overflow there and adding the -ms-content-zooming attribute set to zoom allows my IMG containing DIV to be zoomed via a pinch/zoom gesture ( or equivalent mouse/keyboard shortcut ).

However, I can only zoom in, enlarging the image and adding scrollbars;

image

that's purely because I haven't set up the limits that I want for zooming. If I was to specify the maximum and minimum sizes for zooming;

#container
		{
			-ms-grid-column: 2;
			-ms-grid-row: 2;
			overflow: scroll;
			-ms-content-zooming:zoom;
			-ms-content-zoom-boundary-max: 500%;
			-ms-content-zoom-boundary-min: 75%;
		}
then I can now zoom the DIV down (or out) with a pinch gesture down to 3/4 of its original size and in (or up) to 5 times as large;

image

and all of that comes for free in the browser. Where might I use it? You can think of scenarios where you might have content that you want to be able to display at a default size and yet offer the capability to zoom in and see a little more detail ( maybe a tube map or something along those lines? ) - it's nice functionality to have to hand for zero effort Smile

At the moment, my zooming is unguided in that I can zoom to 75, 76, 77% or wherever I happen to release the pinch gesture. Often, touch interactions are a little tricky to be accurate about and so you can provide "guide points" or “snap points” for the zooming to guide the gesture.

There's 2 ways of specifying these "snap points" - as snapIntervals or as a snapList. Quick examples below;

 

  • snapInterval(25%, 25%) means that we want to guide the user to be able to scale the DIV to 25%, 50%, 75%, 100% and so on up to the 500% max that we've specified as the boundary.
  • snapList(25%, 100%, 200%, 300%, 400%, 500%) is a more explicit list of the points that we are interested in guiding the user’s pinch/zoom gestures to.

and either way I set those up using something like;

	#container
		{
			-ms-grid-column: 2;
			-ms-grid-row: 2;
			overflow: scroll;
			-ms-content-zooming:zoom;
			-ms-content-zoom-boundary-max: 500%;
			-ms-content-zoom-boundary-min: 25%;
			-ms-content-zoom-snap-type: mandatory;
			-ms-content-zoom-snap-points: snapList(75%, 150%, 200%, 300%, 400%, 500%);
		}

and then the browser is going to take my expand gesture and guide it towards one of those values that I've specified as a snap point.

I also have a choice as to how I want those snap-points to be handled.

I can choose to go with mandatory snap points which the previous CSS uses then that means that when the zoom/pinch gesture completes it will always be adjusted from its end point to the nearest snap point.

If I choose to go with proximity for –ms-content-zoom-snap-type instead then the zoom/pinch gesture will only be adjusted to the nearest snap point if it happens to land near to one of those snap points in the first place.

It’s not quite as easy to see the effect of these snap points on zooming as it is on panning unless you choose them somewhat artificially just to make it obvious. With panning though it’s far more obvious…

Panning ( or scrolling )

Rather than zooming, let's say that I want to control the ability to scroll/pan. If I change my HTML to provide a "2 x 2" viewport onto content which is actually a 6 x 4 grid as below;

<html style="height:100%">

<head>
	<style type="text/css">
		body
		{
			height: 100%;
			-ms-content-zooming:none;
		}
		#grid
		{
			display: -ms-grid;
			-ms-grid-columns: 1fr 8fr 1fr;
			-ms-grid-rows: 1fr 8fr 1fr;
			height:100%;
		}
		#container
		{
			-ms-grid-column: 2;
			-ms-grid-row: 2;
			overflow: auto;
		}
		#content
		{
			width: 300%;
			height: 200%;
			display: -ms-grid;
			-ms-grid-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
			-ms-grid-rows: 1fr 1fr 1fr 1fr;
		}

	</style>

</head>

<body>
	<div id="grid">
		<div id="container">
            		<div id="content">
				<div style="-ms-grid-column:1;-ms-grid-row:1;background-color:red"></div>
				<div style="-ms-grid-column:2;-ms-grid-row:1;background-color:green"></div>
				<div style="-ms-grid-column:1;-ms-grid-row:2;background-color:yellow"></div>
				<div style="-ms-grid-column:2;-ms-grid-row:2;background-color:blue"></div>
				<div style="-ms-grid-column:3;-ms-grid-row:1;background-color:orange"></div>
				<div style="-ms-grid-column:4;-ms-grid-row:1;background-color:pink"></div>
				<div style="-ms-grid-column:3;-ms-grid-row:2;background-color:black"></div>
				<div style="-ms-grid-column:4;-ms-grid-row:2;background-color:silver"></div>
				<div style="-ms-grid-column:5;-ms-grid-row:1;background-color:red"></div>
				<div style="-ms-grid-column:6;-ms-grid-row:1;background-color:green"></div>
				<div style="-ms-grid-column:5;-ms-grid-row:2;background-color:yellow"></div>
				<div style="-ms-grid-column:6;-ms-grid-row:2;background-color:blue"></div>
				<div style="-ms-grid-column:1;-ms-grid-row:3;background-color:orange"></div>
				<div style="-ms-grid-column:2;-ms-grid-row:3;background-color:pink"></div>
				<div style="-ms-grid-column:1;-ms-grid-row:4;background-color:black"></div>
				<div style="-ms-grid-column:2;-ms-grid-row:4;background-color:silver"></div>
				<div style="-ms-grid-column:3;-ms-grid-row:3;background-color:red"></div>
				<div style="-ms-grid-column:4;-ms-grid-row:3;background-color:green"></div>
				<div style="-ms-grid-column:3;-ms-grid-row:4;background-color:yellow"></div>
				<div style="-ms-grid-column:4;-ms-grid-row:4;background-color:blue"></div>
				<div style="-ms-grid-column:5;-ms-grid-row:3;background-color:orange"></div>
				<div style="-ms-grid-column:6;-ms-grid-row:3;background-color:pink"></div>
				<div style="-ms-grid-column:5;-ms-grid-row:4;background-color:black"></div>
				<div style="-ms-grid-column:6;-ms-grid-row:4;background-color:silver"></div>
			</div>
        	</div>
	</div>
</body>
</html>
then I immediately get an area that is 3 times wider and twice as high as the DIV which is providing a “viewport” onto the content and so scrollbars appear (because of my auto setting);

image

Rather than drag around on the scrollbars (which aren’t very touch friendly), I can just pan around on the content itself to move it along on the X and Y axes;

image

If I wanted to limit the scrolling then I can use -ms-scroll-boundary ( left, right, top, bottom ) so with this additional CSS;

	#container
		{
			-ms-scroll-boundary-left: 0px;
			-ms-scroll-boundary-right: 200%;

I can now only scroll my grid to the right by "2 squares" or 100% of the width of the grid container itself so as far as to see;

image

what that picture was trying to show is that there is content off to the right here but I can't pan to it because of the -ms-scroll-boundary-right setting.

That's all great but, once again, from the point of a touch user if this content was actually meaningful rather than just a bunch of coloured squares I might want to provide some guides for the user's scrolling. I can do that by changing my CSS in a similar way to providing guides for zooming;

		#container
		{
			-ms-grid-column: 2;
			-ms-grid-row: 2;
			overflow: auto;
			-ms-scroll-snap-x: mandatory snapInterval(0%, 50%);
		}

and then the scrolling will snap me (in a mandatory manner here) to each of my grid squares as I pan to the right/left;

image

image

I should say that the text there was drawn with a mouse rather than a pen or a finger Smile and I can also do this vertically;

		#container
		{
			-ms-grid-column: 2;
			-ms-grid-row: 2;
			overflow: auto;
			-ms-scroll-snap-x: mandatory snapInterval(0%, 50%);
			-ms-scroll-snap-y: mandatory snapInterval(0%, 50%);
		}

and now I'm snapping in 2 directions of panning;

image

Going back to my content when not constrained by snap points – i.e. with my #container DIV styled by;

#container
{
	-ms-grid-column: 2;
	-ms-grid-row: 2;
	overflow: auto;
}

then the content will largely just pan around in any direction and in any amount by following my finger as I pan around. If I want to again provide a bit more guidance around the panning and constrain it to largely be either horizontal or vertical then I can add “rails”;

#container
{
	-ms-grid-column: 2;
	-ms-grid-row: 2;
	overflow: auto;
	-ms-scroll-rails: railed;
}
then the user gets a more “guided” experience for their pan gesture in that if a pan along a particular axis is detected then slight movements along the other axis will be ignored so that the pan is “guided” horizontally or vertically.

I only came across these capabilities the other day (when someone pointed them out to me) and it was a minor revelation that the browser could handle these kinds of gestures for me with minimal effort – the doc page is available if you want to look in some more detail or see the other attributes that I left out here.


Posted Fri, Dec 9 2011 9:17 AM by mtaulty
Filed under: , , ,

Comments

Anon wrote re: Zooming & Panning with CSS in IE 10
on Fri, Dec 9 2011 11:53 AM

WTF? Is this IE6 or something? Why on earth are Microsoft developing browser specific functionality?

What happen to standards? And umm - HTML5?

Sorry if tone is a bit harsh but this really going to piss off developers.

mtaulty wrote re: Zooming & Panning with CSS in IE 10
on Sat, Dec 10 2011 12:09 PM

Anon,

No, it's not IE6.

I think it's a little unfair to ask 'what happened to standards and HTML5?' given that IE10 is doing a pretty stellar job of implementing those standards that you talk about as documented here;

blogs.msdn.com/.../html5-for-applications-the-fourth-ie10-platform-preview.aspx

and more details on MSDN about IE10;

msdn.microsoft.com/.../hh673549.aspx

The IE team has written about their practice for vendor prefixes;

blogs.msdn.com/.../a-best-practice-for-programming-with-vendor-prefixes.aspx

blogs.msdn.com/.../ie9-vendor-prefixes-and-developers.aspx

and so I think in this particular area that I talked about in the post, it's understandable why IE is using vendor prefixes because I don't believe that these interactions are covered under a specific standard right now?

Thanks,

Mike.

Dave Hogan wrote re: Zooming & Panning with CSS in IE 10
on Mon, Dec 12 2011 8:47 AM

Anon - Chrome are implementing even more browser specific functionality. Interesting read here: www.pcmag.com/.../0,2817,2397158,00.asp