Quickstart: adding a SemanticZoom (HTML)
[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]
Learn how to use the SemanticZoom control to zoom between two views of the same content.
Prerequisites
- We assume that you can create a basic Windows app using JavaScript that uses Windows Library for JavaScript controls. For instructions on getting started with WinJS controls, see the Quickstart: adding WinJS controls and styles.
- To follow this quickstart, you need to know how to use the ListView control. For help getting started with ListView control, see Quickstart: adding a ListView.
- To use the SemanticZoom control, you need to know how to create a grouped ListView. For more info, see How to group items in a ListView.
What is the SemanticZoom control?
The SemanticZoom control enables the user to switch between two different views of the same content. One of these is the main view of the content. The second view is a view of the same content represented in a way that allows users to quickly navigate through it. For example, when viewing an address book, the user could zoom in on a letter and see the names associated with that letter.
To provide this zooming functionality, the SemanticZoom control uses two other controls: one to provide the zoomed-in view and one to provide the zoomed-out view.
<div data-win-control="WinJS.UI.SemanticZoom">
<!-- The control that provides the zoomed-in view goes here. -->
<!-- The control that provides the zoomed-out view goes here. -->
</div>
These controls can be any two controls that implement the IZoomableView interface. The WinJS provides one control that implement the IZoomableView interface: the ListView control. The examples in this quickstart show you how to use the SemanticZoom with two ListView controls.
Don't confuse semantic zooming with optical zooming. While they share both the same interaction and basic behavior (displaying more or less detail based on a zoom factor), optical zoom refers to the adjustment of magnification for a content area or object such as a photograph.
Create your data
To use a SemanticZoom, you need an IListDataSource that contains group info. One way to create an IListDataSource is to create a WinJS.Binding.List. Each WinJS.Binding.List has a dataSource property that returns an IListDataSource that contains your data.
Add a new JavaScript file to your project to contain your data. Name it "data.js".
In the data.js file you just created, create the underlying data source that will supply your ListView controls with data. This example creates a WinJS.Binding.List from an array of JSON objects (myData):
// Start of data.js (function () { "use strict"; var myData = [ { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png" }, { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png" }, { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png" }, { title: "Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png" }, { title: "Lavish Lemon Ice", text: "Sorbet", picture: "images/60Lemon.png" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png" }, { title: "Marvelous Mint", text: "Gelato", picture: "images/60Mint.png" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png" }, { title: "Creamy Orange", text: "Sorbet", picture: "images/60Orange.png" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png" }, { title: "Succulent Strawberry", text: "Sorbet", picture: "images/60Strawberry.png" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png" }, { title: "Very Vanilla", text: "Ice Cream", picture: "images/60Vanilla.png" }, { title: "Orangy Orange", text: "Sorbet", picture: "images/60Orange.png" }, { title: "Orangy Orange", text: "Sorbet", picture: "images/60Orange.png" }, { title: "Absolutely Orange", text: "Sorbet", picture: "images/60Orange.png" }, { title: "Absolutely Orange", text: "Sorbet", picture: "images/60Orange.png" }, { title: "Triple Strawberry", text: "Sorbet", picture: "images/60Strawberry.png" }, { title: "Triple Strawberry", text: "Sorbet", picture: "images/60Strawberry.png" }, { title: "Double Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png" }, { title: "Double Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png" }, { title: "Double Banana Blast", text: "Low-fat frozen yogurt", picture: "images/60Banana.png" }, { title: "Green Mint", text: "Gelato", picture: "images/60Mint.png" } ]; // Create a WinJS.Binding.List from the array. var itemsList = new WinJS.Binding.List(myData);
Note This data refers to several images. To get the images, download the ListView grouping and SemanticZoom sample, then copy the images from the sample into your project. You can also use your own images—just be sure to update the value of the
picture
property in your data.Tip
You aren't limited to using an WinJS.Binding.List: you could also use a StorageDataSource or a custom VirtualizedDataSource. For more info about creating a custom data source, see How to create a custom data source.
Create a version of your data source that contains grouping info. If you're using a WinJS.Binding.List, you can call its createGrouped method to create a grouped version of the List. The createGrouped method takes 3 parameters:
- getGroupKey: a function that, given an item in the list, returns the group key that the item belongs to.
- getGroupData: a function that, given an item in the list, returns the data object that represents the group that the item belongs to.
- compareGroups: a function that compares two groups and returns a value less than zero if the first group is less than the second group, zero if the groups are the same, and a positive value if the first group is greater than the second group.
This example uses the List.createGrouped method to create a grouped version of the List. It uses the first letter of each item's title to define the groups.
// Sorts the groups. function compareGroups(leftKey, rightKey) { return leftKey.charCodeAt(0) - rightKey.charCodeAt(0); } // Returns the group key that an item belongs to. function getGroupKey(dataItem) { return dataItem.title.toUpperCase().charAt(0); } // Returns the title for a group. function getGroupData(dataItem) { return { title: dataItem.title.toUpperCase().charAt(0) }; } // Create the groups for the ListView from the item data and the grouping functions var groupedItemsList = itemsList.createGrouped(getGroupKey, getGroupData, compareGroups);
Make your data accessible to other portions of your program. This example uses WinJS.Namespace.define to make the grouped list publicly accessible.
WinJS.Namespace.define("myData", { groupedItemsList: groupedItemsList }); })(); // End of data.js
Create two ListView controls
As mentioned earlier, the SemanticZoom control requires two additional controls that implement the IZoomableView interface: one to supply the zoomed-in view and one for the zoomed-out view.
In the head section of your HTML page that will contain the SemanticZoom, add a reference to the data file you created in the previous step.
<!-- Your data file. --> <script src="/js/data.js"></script>
Define 3 templates for your ListView objects: one for the zoomed-in items view, one for the group headers in zoomed-in view, and one for the group headers in the zoomed-out view.
<!-- Template for the group headers in the zoomed-in view. --> <div id="headerTemplate" data-win-control="WinJS.Binding.Template" style="display: none"> <div class="simpleHeaderItem"> <h1 data-win-bind="innerText: title"></h1> </div> </div> <!-- Template for the ListView items in the zoomed-in view. --> <div id="mediumListIconTextTemplate" data-win-control="WinJS.Binding.Template" style="display: none"> <div class="mediumListIconTextItem"> <img class="mediumListIconTextItem-Image" data-win-bind="src: picture" /> <div class="mediumListIconTextItem-Detail"> <h4 data-win-bind="innerText: title"></h4> <h6 data-win-bind="innerText: text"></h6> </div> </div> </div> <!-- Template for the zoomed out view of the semantic view. --> <div id="semanticZoomTemplate" data-win-control="WinJS.Binding.Template" style="display: none"> <div class="semanticZoomItem"> <h1 class="semanticZoomItem-Text" data-win-bind="innerText: title"></h1> </div> </div>
In your HTML, define two ListView controls. The first control provides the zoomed-in view, and the second control provides the zoomed-out view.
- Set the itemDataSource for the zoomed-in ListView to myData.groupedItemList.dataSource, the IListDataSource that contains the items to display. Set its groupDataSource to myData.groupedItemsList.groups.dataSource, the IListDataSource that contains group info.
- For the zoomed-out ListView, set its itemDataSource to myData.groupedItemList.groups.dataSource, the IListDataSource that contains group info. That's where the ListView gets the group titles to display.
This example creates two ListView controls and configures them to use the templates that you just created.
<!-- The zoomed-in view. --> <div id="zoomedInListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: myData.groupedItemsList.dataSource, itemTemplate: select('#mediumListIconTextTemplate'), groupHeaderTemplate: select('#headerTemplate'), groupDataSource: myData.groupedItemsList.groups.dataSource, selectionMode: 'none', tapBehavior: 'none', swipeBehavior: 'none' }" ></div> <!--- The zoomed-out view. --> <div id="zoomedOutListView" data-win-control="WinJS.UI.ListView" data-win-options="{ itemDataSource: myData.groupedItemsList.groups.dataSource, itemTemplate: select('#semanticZoomTemplate'), selectionMode: 'none', tapBehavior: 'invoke', swipeBehavior: 'none' }" ></div>
In your CSS file, define the styles for your templates and ListView controls. If you skip this step, your app will still run, but it won't look as nice.
/* Template for headers in the zoomed-in ListView */ .simpleHeaderItem { width: 50px; height: 50px; padding: 8px; } /* Template for items in the zoomed-in ListView */ .mediumListIconTextItem { width: 282px; height: 70px; padding: 5px; overflow: hidden; display: -ms-grid; } .mediumListIconTextItem img.mediumListIconTextItem-Image { width: 60px; height: 60px; margin: 5px; -ms-grid-column: 1; } .mediumListIconTextItem .mediumListIconTextItem-Detail { margin: 5px; -ms-grid-column: 2; } /* Template for items in the zoomed-out ListView */ .semanticZoomItem { width: 130px; height: 130px; background-color: rgba(38, 160, 218, 1.0); } .semanticZoomItem .semanticZoomItem-Text { padding: 10px; line-height: 150px; white-space: nowrap; color: white; } /* CSS for the zoomed-in ListView */ #zoomedInListView { width: 600px; height: 300px; border: solid 2px rgba(0, 0, 0, 0.13); } #semanticZoomDiv { width: 600px; height: 300px; border: solid 2px rgba(0, 0, 0, 0.13); }
Run the app. You will see two ListView controls:
The first ListView provides the zoomed-in view and the second provides the zoomed-out view. Note that they both use a horizontal layout. We recommend that the zoomed-in and zoomed-out views of data always use the same layout.
Add the SemanticZoom control
In your markup, create the SemanticZoom control and move your ListView controls inside it.
<div id="semanticZoomDiv" data-win-control="WinJS.UI.SemanticZoom">
<!-- The zoomed-in view. -->
<div id="zoomedInListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource: myData.groupedItemsList.dataSource, itemTemplate: select('#mediumListIconTextTemplate'), groupHeaderTemplate: select('#headerTemplate'), groupDataSource: myData.groupedItemsList.groups.dataSource, selectionMode: 'none', tapBehavior: 'none', swipeBehavior: 'none' }"
></div>
<!--- The zoomed-out view. -->
<div id="zoomedOutListView"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemDataSource: myData.groupedItemsList.groups.dataSource, itemTemplate: select('#semanticZoomTemplate'), selectionMode: 'none', tapBehavior: 'invoke', swipeBehavior: 'none' }"
></div>
</div>
When you run app, you now see a single ListView, and you can now zoom between the two views you defined.
Note Don't set a border on the SemanticZoom control's child controls. If you set borders on both the SemanticZoom and its child controls, the SemanticZoom border and the border of the child control that is in view will both be visible. When zooming in/out, the child controls' borders are scaled together with the content and won't look good. Set only a border on the SemanticZoom control.
Using the SemanticZoom
To zoom between the two views:
Input mechanism | Zoom out | Zoom in |
---|---|---|
Touch | Pinch out | Pinch, tap |
Keyboard | Ctrl + Minus sign, Enter | Ctrl + Plus sign, Enter |
Mouse | Ctrl + Rotate the mouse wheel backward | Ctrl + Rotate the mouse wheel forward |
Using the SemanticZoom with a custom control
To use the SemanticZoom with a control other than ListView, you must implement the IZoomableView interface. For a sample that shows you how, see the SemanticZoom for custom controls sample.
Keeping the SemanticZoom responsive
It's important for the user to be able to quickly and smoothly switch between the zoomed-in and zoomed-out views of a SemanticZoom. That means that the child controls of the SemanticZoom control shouldn't make the app wait while they load their data. When using the ListView (or a version of the FlipView that you've customized to implement IZoomableView) with the SemanticZoom, use a templating function that creates placeholders when there's a chance that the items might not be available by the time the control comes into view. For more info about using placeholders in item templates, see FlipView.itemTemplate. If you're using a custom control with the SemanticZoom, implement a progress circle and use placeholders if items might not be available.
Samples
Summary and next steps
You learned how to create a SemanticZoom that uses two ListView controls to create its zoomed-in and zoomed-out views.
Now, learn more about when and how to use the SemanticZoom by reading the Guidelines and checklist for SemanticZoom controls.