Thursday, February 26, 2009

WebKit CSS Animation behavior weirdness

Okay, not weirdness. I guess more like (up until now) undocumented behavior. Due to a workaround that I needed to implement in order to properly clear out CSS properties when starting a new transition in my hacked-out S5 presentation, I filed Bug 23528: CSS transitions on opacity fail when used with visibility in certain circumstances.

When performing a transition, I had expected that a function like the following (simplified for demonstration) would work:

/**
 * 1. take the element,
 * 2. make it visible,
 * 3. yet transparent,
 * 4. then set the transition,
 * 5. and then have it fade from opacity of 0 to 1 over one second.
 */
function fadeIn() {
    // <span id="one" style="visibility: hidden;">testing</span>
    var theOne = document.getElementById("one");
    theOne.style.visibility = "visible";
    theOne.style.opacity = 0;
    theOne.style.WebkitTransition = "all 1s ease-in-out";
    theOne.style.opacity = 1;
}

Instead of fading in over one second, the span instantly appears, as if the last three lines of the function didn't even exist. This happens because CSS changes don't take effect when you assign new values. They queue up, taking effect once the JavaScript thread of execution ends. This makes sense for static changes, so that if you have a whole load of changes to apply to the DOM where many of the changes end up canceling out by the time the thread ends, the browser has no reason to apply each one of them to the DOM just in order to remove the change in the end. It also makes sense in a pure performance sense, since this keeps the JavaScript execution moving along at a nice, steady pace, rather than having to wait for the DOM to apply the change before moving on to the next step in the process.

Unfortunately for the code example above, it means that since the DOM node starts out completely opaque, the last three lines of the fadeIn function do effectively fall off and the span simply becomes visible. In order to get around this, the last couple of lines have to run on a different JavaScript "thread" so that it splits up the batched DOM changes into two parts and the node properly fades in:

function fadeIn() {
    // <span id="one" style="visibility: hidden;">testing</span>
    var theOne = document.getElementById("one");
    theOne.style.visibility = "visible";
    theOne.style.opacity = 0;
    setTimeout(function() {
        theOne.style.WebkitTransition = "all 1s ease-in-out";
        theOne.style.opacity = 1;
    }, 0);
}

Labels: , , , , ,

Saturday, November 29, 2008

Enterprise Guts slides up, with CSS animations

I just realized that I didn't post the final version of my slides from my talk at ZendCon, but now I have: Digging Through the Guts of Enterprise PHP. However, they will only work in Safari, since I hacked CSS transitions into S5. Different builds of WebKit (and different releases of Safari) will have varying degrees of support for everything used in there (sliding, fading, and flipping). For the talk itself, I ended up having to go back a couple of weeks in the WebKit builds, since they broke a few things... Part of the fun of using experimental technology in a nightly build, though.

As a bonus, this means that the slides (mostly) work in the iPhone/iPod Touch version of Safari:

Due to how S5 triggers moving to the next slide, you just need to bring up the control bar at the bottom so you can tap on the arrows to move forward. In the last bit there, the animation of two simultaneous transforms, flipping the slides around, caused what we software engineers refer to as a "rendering freak out" followed by mobile Safari freezing up. All in due time!

Labels: , , , , , , , ,

Tuesday, September 02, 2008

S5 with CSS Slide Transitions in WebKit

So, in an effort to have the ability to post my slides for my Zend/PHP Conference talk online without having to rely on Flash or typically awkward HTML exports from other formats, I've decided to go with S5, as mentioned previously. I've also decided to add a little of WebKit's recent additions of CSS transforms and animation support for transitions.

Since CSS CSS animation only occurs when attributes change, it just means that the current and next slides need to have the changes applied at different times, or at the same time, depending. To make managing these things easier, I started with a base object:

function Transition() { }
Transition.prototype = {
 "duration" : 1,
 "cslide" : null,
 "nslide" : null,
 "transition" : function(toggle) {
  this.cslide.style.WebkitTransition =
   this.nslide.style.WebkitTransition =
    (toggle
     ? 'all ' + this.duration + 's ease-in-out'
     : 'all 0s none');
 },
 "prepare" : function() { },
 "perform" : function() { },
 "cleanup" : function() { },
 "run" : function(cid, nid) {
  this.cslide = document.getElementById(cid);
  this.nslide = document.getElementById(nid);
  this.cslide.style.visibility = this.nslide.style.visibility = 'visible';
  this.transition(false);
  this.prepare();
  var dis = this;
  setTimeout(function() {
   dis.transition(true);
   setTimeout(function() { dis.perform.apply(dis); }, 0);
  }, 0);
  setTimeout(function() {
   dis.transition(false);
   dis.cslide.style.visibility = 'hidden';
   dis.cleanup.apply(dis);
  }, dis.duration * 1000);
 }
}

The code managing the switch of the slides now calls RunTransition(transitions[snum], cid, nid);, where transitions just holds an array of constructor references corresponding to the slide numbers (snum):

var transitions = [
 null,
 Fade,
 Wipe,
 Rotate,
 Rotate,
 Fade
]

function RunTransition(className, cid, nid) {
 var trans = new className();
 trans.run.call(trans, cid, nid);
}

Each transition just extends Transition and overrides the applicable methods in order to change the slide CSS properties at the correct time, like this Fade transition:

function Fade() { }
Fade.prototype = new Transition;
Fade.prototype.prepare = function() {
 this.cslide.style.opacity = '1';
 this.nslide.style.opacity = '0';
}
Fade.prototype.perform = function() {
 this.cslide.style.opacity = '0';
 this.nslide.style.opacity = '1';
}

I've uploaded a very quick demo of the transitions in action, which will work in whichever builds of WebKit support CSS transforms and animation. It worked a bit smoother in WebKit builds a few weeks ago than those current (bugs galore at the moment), but nightly builds always tend introduce unpredictable behavior.

Update (1/25): Between fixes in the WebKit trunk since this post, and fixes in my code (with many thanks to smfr in comments and on the bug itself), all of the transitions work in the current Webkit nightly! From my final note, just in case anyone else has issues with transforms when used with transformations:

"If you set the WebkitTransform using just matrix() in the first step, then set the WebkitTransition, then set the WebkitTransform using both matrix and scale, it (reasonably) will not apply the transformation. When I set both both functions for the start and end styles, it works beautifully."

Labels: , , , , ,

Monday, September 01, 2008

Google launching open source browser based on WebKit?

No idea whether this theory holds any water, but given the recent speculation hitting...everywhere, I checked http://code.google.com/p/chrome/ and found a login screen (quickly followed by a permission denied error), rather than a Not Found error for a project that doesn't exist.

Wondering what this will bring to WebKit that Safari doesn't (aside from Gears and even-more-targeted advertising), but I guess we'll see soon enough.

Edited to add: the more I read about this, the more it looks like they ported a bunch of features from Opera (tabs above window chrome, long-running scripts' threads not killing your browser, Speed Dial-like default page, etc.) to a WebKit-based browser with what sounds like XUL for the window chrome itself. I guess tomorrow we'll find out for sure, but it seems a bit odd that an open source project would outright deny access to the public before launch, and distribute the info under the most closed-minded CC license available (feel free to share it, just don't touch it, don't charge for it, and you'd better tell people who made it). I suppose after the recent Android SDK excitement, this still makes a decent step forward, but still. I guess so long as it takes market share away from IE, I shouldn't complain too much.

Labels: ,

Tuesday, May 06, 2008

Opera Dragonfly (alpha) Released

Over a year ago, I wrote about the Opera Developer Console. This morning (for me, at any rate), Opera posted Dragonfly, which (two years in the making) offers a completely fresh look at browser-based debugging.

An Opera Dragonfly window showing a JavaScript console, stack trace, and active debugger, stepping through a call to add an event listener

It offers most of the familiar tools for DOM inspection (along with a nice DOM editing capability), error logging (with the same granularity as before wrapped in a more polished UI), a JavaScript debugger that rivals WebKit's Drosera, a JavaScript thread logger, and a lot more that I haven't explored yet.

Time will tell whether Dragonfly can get enough developers to use Opera and keep them there, and how much the developers behind the new developer tools listen to the community in the coming iterations, but so far this looks extremely promising.

Edit: Chris Mills from Opera gave me some additional info:

Part of the reason I haven't found the XMLHttpRequest logger/debugger apparently stems from it not getting exposed yet, though it will appear in an upcoming iteration, along with HTTP header inspection (not just for XMLHttpRequests, of course) and a new "single window mode" which sounds like it will make things much more usable! When using Safari's inspector, I almost always find myself attaching it to the window, and I always had the urge to do the same with Opera's developer console.

Chris also mentioned something which makes me very happy to hear: even though the Developer Tools (loaded via the Tools → Advanced → Developer Tools) download from Opera's server, it only does so the first time, and for each following update of the tools. This ensures that it not only saves Opera's servers when usage takes off, but it also ensures that developers can work offline, and the slow loading of the tools will only happen initially, loading from the local drive after installation.

Written in XML/CSS/JavaScript, Dragonfly will run in all browsers including Opera's Core-2.1 rendering engine (except Opera Mini for some reason), and even supports debugging on mobile devices by way of a proxy setup between the device and the desktop Dragonfly installation! This will prove invaluable for developers of web applications supporting devices, as they will have the ability to use their normal desktop tools to debug on the mobile browser without having to use emulation.

You can find more information in an Introduction to Opera Dragonfly and on the Opera Dragonfly product page (which has a very good start of fleshed-out documentation already, feedback and bugtracking, and of course a blog).

Edited to fix the Introduction to Opera Dragonfly link...

Labels: , , , ,

Thursday, November 08, 2007

Announcing Universe Conflict!

Posted to the new project space on SourceForge.net:

An implementation of Space War!, one of the first digital computer games, created in 1961 on the PDP-1 computer, as recreated using the <canvas> HTML5 element and Ajax. It uses the Frozen Toolkit for the client-server communication and currently renders all images and animation using canvas and JavaScript objects.

I had always intended to release this as an open source project, but then realized that I didn't really have the full time to dedicate to it. It also currently exists more as sample code and an interactive demo than anything else, and I figured I should just post it for all to see and hopefully for some to contribute. I initially posted the introduction to it on my blog.

The client-side of this game currently works in Firefox and Opera, though gravity and shooting do not yet exist. The server-side of this currently works insofar as the absolute minimum required to send messages from one browser to the other in order for the two players to fly around each other.

At any rate, check out the code (svn co https://@webspacewar.svn.sourceforge.net/svnroot/webspacewar), play around with it, and feel free to submit patches! :-) I would love to see this completed and fully playable. "Space War!" doesn't have a lot of (base) rules, so it shouldn't take much to get there.

Edit: I've also finally, finally checked in the source code for a link checker I started...oh, I think back in 2002... I created the Cocoa/WebKit branch in order to do two things:

  1. Learn Objective-C and Cocoa
  2. Use the Cocoa/WebKit API in order to take care of the HTTP handling

Labels: , , , , , ,