• Javascript, Web Apps December 19, 2008

    Web applications for Mobile Safari (used on the iPhone and iPod touch) can take advantage of multiple touches and gestures. The simplest gestures to implement are scaling and rotating an object, since support for them is built-in.

    Below is an example which allows you to use the pinch gesture to resize the box and rotating your fingers to rotate it. Since it’s using the Mobile Safari gesture events, it will only work on the iPhone and iPod touch.

    Scale and rotate me!

    The CSS and Javascript used are embedded in the head of this document (look for the strings “Gesture Demo CSS” and “Gesture Demo Javascript” in the source for this page).

    The first thing the demo does is to add events on the red box for ongesturestart, ongesturechange, and ongestureend. In addition, it seeds a beginning scale and rotation for the CSS transform property. so the result of the previous transformation is preserved. The id of the red box is gesture.

    window.onload = function () {
        var gdiv = document.getElementById ('gesture');
        if (! gdiv)
            return;
        gdiv.addEventListener ('gesturestart', gStart, false);
        gdiv.addEventListener ('gesturechange', gChange, false);
        gdiv.addEventListener ('gestureend', gEnd, false)
        gdiv.gStartScale = 1.0;
        gdiv.gStartRotation = 0;
    }

    All three event handlers call preventDefault() so Mobile Safari won’t scale the whole page when you supply a gesture to the red box. In fact, this is all the ongesturestart event handler does.

    function gStart (e) {
        e.preventDefault ();
    }

    The handler for ongesturechange grabs the scale and rotation values from the event and creates a new string for the transform CSS property of the red box.

    function gChange (e) {
        var target = e.target;
        e.preventDefault ();
        target.style.webkitTransform =
            'scale(' + (target.gStartScale * e.scale) + ') ' +
            'rotate(' + (target.gStartRotation + e.rotation) + 'deg)';
    }

    The handler for ongestureend takes the initial scale and rotation and saves new values which represent the current state of the box.

    function gEnd (e) {
        var target = e.target;
        e.preventDefault ();
        target.gStartScale *= e.scale;
        target.gStartRotation += e.rotation;
    }

    This demo will not let you drag the box to a new location. That would require also watching the touch events and  letting the gesture events handlers do their thing when two fingers are active.

    This application was somewhat sluggish when I tested it on an iPod touch. I’d assume the bottleneck would be in the gChange function, and probably because there’s math, converting floats to strings, and string concatenation going on, as well as the actual transform. Keeping the initial values for the scale and rotation as global variables would probably also help (assign them to globals in gStart and put them back to the target in gEnd).

    If you look at the CSS you’ll notice that the overflow property is set to hidden, but when you manipulate the pink box, it does overflow outside the white box. This seems to be a bug with Mobile Safari. The outer box properly clips contents of contents which are positioned with CSS, but does not if there is a transformation (even if it’s a no-op). As an example, here are a couple screenshots, the left from Safari on a Mac and the right from Mobile Safari.

    overflow-hidden-iphone

    The HTML is just:

    <div id="outer">
        <div id="inner1"></div>
        <div id="inner2"></div>
    </div>

    The outer, white square has the CSS:

    #outer {
        width: 120px;
        height: 50px;
        background: white;
        border: 1px black solid;
        position: relative;
        overflow: hidden;
    }

    The pink squares have the common CSS:

    #outer div {
        width: 40px;
        height: 50px;
        position: absolute;
        top: 10px;
        background: rgba(255,180,180,0.8);
        border: 5px green solid;
    }

    The left pink square is positioned on the left side, and the right pink square is positioned on the right and has a transformation which does not actually do anything:

    #inner1 {
        left: 5px;
    }
    #inner2 {
        right: 5px;
        -webkit-transform: translate(0,0);
    }

    As you can see, all the boxes are correctly clipped except for the one using a transform on Mobile Safari. You can see the bottom border peeking through a bit because the the opacity of the pink background is 80%.

    Posted by fmf @ 5:18pm

  • Leave a Comment

    Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.