Search

1/13/2009

A Snapshot of The Yahoo! Photos Beta (from 2006)

A Snapshot of The Yahoo! Photos Beta (from 2006)

When the user is drawing a selection marquee over a number of photos, it is faster to directly modify the style attributes of the selected photo items. Changing class names on the photo items as they were highlighted made the browser eat up more CPU and therefore took longer to update visually.

Photos creates a "PhotoItem" object instance for each thumbnail that is shown. Each object has a number of DOM node references (eg. an image or a DIV,) methods, properties and references to other objects in some instances.

Internet Explorer: "The Wall"
Having many more than 100 "PhotoItem" objects active at a time begins to degrade DOM, XML parsing, general javascript performance in IE (seen in development on IE6, Windows XP) in a seemingly non-linear fashion. The more active photo objects, the more memory is used at once and the slower the browser runs. Though unconfirmed, it may be possible there's a correlation between this and the number of image elements being generated and loaded, as well.
"The Wall" Workaround
To avoid hitting the wall, Photos will destroy PhotoItem objects as they are not needed. PhotoItem objects are created as needed, as the user is viewing and scrolling through photos. Once they move to the next "page" (javascript-driven, same URL) of photos, the previous page is hidden from view and its related objects are destroyed. Throwing away these objects is enough to avoid the effects of the wall without introducing other performance side-effects.

Lightweight event handling: Event Delegation
Since event handlers can be somewhat expensive to individually create, assign and maintain, Photos has taken a higher-level approach of assigning a "controller" object which watches events at the document level, determines the object that should handle the event, and dispatches the event accordingly.


- Watch "global" document.onmousedown
On mousedown:
Capture event target (element that received mousedown)

IF (event target is a child element of the thumbnail area - ie., user clicked inside thumb area) {

CASE: mousedown on a thumbnail {
watch mousemove for start of drag
ON (mousemove) {
begin drag/drop, assign event handlers (mousemove/mouseup event handlers change)
mousemove: drag photos and coordinate checking (target "collision detection")
mouseup: stop drag/drop (reset handlers,) determine drop target, take action if necessary
}
ON (mouseup) {
IF (CTRL key was held down, multi-select mode) {
Toggle selection (highlight) on this photo, retaining other selections
} ELSE IF (SHIFT key was held, range select mode) {
Select "from last-clicked photo (or first photo) to this one"
} ELSE (no modifier keys) {
Select only this single photo
}
}
}

CASE: mousedown in empty/whitespace inside photo area {
watch mousemove for start of selection marquee
on (mousemove) {
draw selection marquee, do coordinate checks to determine photos to highlight
selection highlighting logic changes to "toggle" with CTRL key - existing selection retained, items "hovered over" toggle their selection status.
}
mouseup: reset event handlers
}

}

Event Handler Throttling Example

function SomeObject() {
var self = this;
this.lastExecThrottle = 500; // limit to one call every "n" msec
this.lastExec = new Date();
this.timer = null;
this.resizeHandler = function() {
var d = new Date();
if (d-self.lastExec<self.lastExecThrottle) {
// This function has been called "too soon," before the allowed "rate" of twice per second
// Set (or reset) timer so the throttled handler execution happens "n" msec from now instead
if (self.timer) {
window.clearTimeout(self.timer);
}
self.timer = window.setTimeout(self.resizeHandler,self.lastExecThrottle);
return false; // exit
}
self.lastExec = d; // update "last exec" time
// At this point, actual handler code can be called (update positions, resize elements etc.)
// self.callResizeHandlerFunctions();
}
}

var someObject = new SomeObject();

window.onresize = someObject.resizeHandler;

沒有留言: