Bug 520872: Make zooming transition better [r=mark.finkle]

This commit is contained in:
Benjamin Stover 2009-10-21 19:11:38 -04:00
Родитель e34f62af05
Коммит ad42ee2cab
4 изменённых файлов: 162 добавлений и 86 удалений

Просмотреть файл

@ -232,6 +232,7 @@ BrowserView.prototype = {
this._browserViewportState = null;
this._contentWindow = null;
this._renderMode = 0;
this._offscreenDepth = 0;
let cacheSize = kBrowserViewCacheSize;
try {
@ -300,6 +301,28 @@ BrowserView.prototype = {
return bvs.zoomLevel;
},
beginOffscreenOperation: function beginOffscreenOperation() {
if (this._offscreenDepth == 0) {
let vis = this.getVisibleRect();
let canvas = document.getElementById("view-buffer");
canvas.width = vis.width;
canvas.height = vis.height;
this.renderToCanvas(canvas, vis.width, vis.height, vis);
canvas.style.display = "block";
this.pauseRendering();
}
this._offscreenDepth++;
},
commitOffscreenOperation: function commitOffscreenOperation() {
this._offscreenDepth--;
if (this._offscreenDepth == 0) {
this.resumeRendering();
let canvas = document.getElementById("view-buffer");
canvas.style.display = "none";
}
},
beginBatchOperation: function beginBatchOperation() {
this._batchOps.push(BrowserView.Util.getNewBatchOperationState());
this.pauseRendering();
@ -504,10 +527,13 @@ BrowserView.prototype = {
},
zoomToPage: function zoomToPage() {
let browser = this._browser;
this.setZoomLevel(this.getZoomForPage());
},
getZoomForPage: function getZoomForPage() {
let browser = this._browser;
if (!browser)
return;
return 0;
var windowUtils = browser.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
@ -515,19 +541,15 @@ BrowserView.prototype = {
var handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly");
if (handheldFriendly == "true") {
browser.className = "browser-handheld";
this.setZoomLevel(1);
browser.markupDocumentViewer.textZoom = 1;
return 1;
} else {
browser.className = "browser";
let [w, h] = BrowserView.Util.getBrowserDimensions(browser);
this.setZoomLevel(BrowserView.Util.pageZoomLevel(this.getVisibleRect(), w, h));
return BrowserView.Util.pageZoomLevel(this.getVisibleRect(), w, h);
}
},
zoom: function zoom(aDirection) {
let bvs = this._browserViewportState;
if (!bvs)
throw "No browser is set";
@ -574,7 +596,6 @@ BrowserView.prototype = {
viewportToBrowser: function viewportToBrowser(x) {
let bvs = this._browserViewportState;
if (!bvs)
throw "No browser is set";
@ -583,7 +604,6 @@ BrowserView.prototype = {
browserToViewport: function browserToViewport(x) {
let bvs = this._browserViewportState;
if (!bvs)
throw "No browser is set";
@ -617,7 +637,6 @@ BrowserView.prototype = {
_viewportChanged: function _viewportChanged(viewportSizeChanged, dirtyAll) {
let bops = this._batchOps;
if (bops.length > 0) {
let opState = bops[bops.length - 1];
@ -631,7 +650,6 @@ BrowserView.prototype = {
}
let bvs = this._browserViewportState;
if (bvs) {
BrowserView.Util.resizeContainerToViewport(this._container, bvs.viewportRect);

Просмотреть файл

@ -343,5 +343,14 @@ Rect.prototype = {
this.right = f.call(this, this.right);
this.bottom = f.call(this, this.bottom);
return this;
},
/** Ensure this rectangle is inside the other, if possible. Preserves w, h. */
translateInside: function translateInside(other) {
let offsetX = (this.left < other.left ? other.left - this.left :
(this.right > other.right ? this.right - other.right : 0));
let offsetY = (this.top < other.top ? other.top - this.top :
(this.bottom > other.bottom ? this.bottom - other.bottom : 0));
return this.translate(offsetX, offsetY);
}
};

Просмотреть файл

@ -603,6 +603,7 @@ var Browser = {
scrollContentToTop: function scrollContentToTop() {
this.contentScrollboxScroller.scrollTo(0, 0);
this.pageScrollboxScroller.scrollTo(0, 0);
this._browserView.onAfterVisibleMove();
},
@ -613,6 +614,14 @@ var Browser = {
this._browserView.onAfterVisibleMove();
},
hideTitlebar: function hideTitlebar() {
let container = this.contentScrollbox;
let rect = container.getBoundingClientRect();
this.pageScrollboxScroller.scrollBy(0, Math.round(rect.top));
this.tryUnfloatToolbar();
this._browserView.onAfterVisibleMove();
},
/**
* Return the currently active <browser> object
*/
@ -954,7 +963,7 @@ var Browser = {
return true;
let [leftvis, ritevis, leftw, ritew] = Browser.computeSidebarVisibility(dx, dy);
if (leftvis <= 0.002 && ritevis <= 0.002) {
if (leftvis == 0 && ritevis == 0) {
BrowserUI.unlockToolbar();
this.floatedWhileDragging = false;
return true;
@ -973,78 +982,101 @@ var Browser = {
// needed then uncomment this line.
},
/**
* Returns a good zoom rectangle for given element.
* @param y Where the user clicked in browser coordinates. For long elements
* (like news columns), this keeps the clicked spot in the viewport.
* @return Rectangle in current viewport coordinates, null if nothing works.
*/
_getZoomRectForElement: function _getZoomRectForElement(element, y) {
if (element == null)
return null;
const margin = 15;
let bv = Browser._browserView;
let vis = bv.getVisibleRect();
let elRect = bv.browserToViewportRect(Browser.getBoundingContentRect(element));
y = bv.browserToViewport(y);
let zoomLevel = BrowserView.Util.clampZoomLevel(bv.getZoomLevel() * vis.width / (elRect.width + margin * 2));
let zoomRatio = bv.getZoomLevel() / zoomLevel;
// Don't zoom in a marginal amount
// > 2/3 means operation increases the zoom level by less than 1.5
if (zoomRatio >= .6666)
return null;
let newVisW = vis.width * zoomRatio, newVisH = vis.height * zoomRatio;
let result = new Rect(elRect.center().x - newVisW / 2, y - newVisH / 2, newVisW, newVisH).expandToIntegers();
// Make sure rectangle doesn't poke out of viewport
return result.translateInside(bv._browserViewportState.viewportRect);
},
/**
* Find a good zoom rectangle for point specified in browser coordinates.
* @return Point in viewport coordinates, null if the zoom is too big.
*/
_getZoomRectForPoint: function _getZoomRectForPoint(x, y, zoomLevel) {
let bv = Browser._browserView;
let vis = bv.getVisibleRect();
x = bv.browserToViewport(x);
y = bv.browserToViewport(y);
if (zoomLevel >= 4)
return null;
let zoomRatio = zoomLevel / bv.getZoomLevel();
let newVisW = vis.width / zoomRatio, newVisH = vis.height / zoomRatio;
let result = new Rect(x - newVisW / 2, y - newVisH / 2, newVisW, newVisH);
// Make sure rectangle doesn't poke out of viewport
return result.translateInside(bv._browserViewportState.viewportRect);
},
setVisibleRect: function setVisibleRect(rect) {
let bv = Browser._browserView;
let vis = bv.getVisibleRect();
let zoomRatio = vis.width / rect.width;
let zoomLevel = bv.getZoomLevel() * zoomRatio;
let scrollX = rect.left * zoomRatio;
let scrollY = rect.top * zoomRatio;
bv.beginOffscreenOperation();
Browser.hideSidebars();
Browser.hideTitlebar();
bv.setZoomLevel(zoomLevel);
Browser.contentScrollboxScroller.scrollTo(scrollX, scrollY);
bv.onAfterVisibleMove();
bv.renderNow();
bv.commitOffscreenOperation();
},
zoomToPoint: function zoomToPoint(cX, cY) {
const margin = 15;
let [elementX, elementY] = Browser.transformClientToBrowser(cX, cY);
let aElement = Browser.elementFromPoint(elementX, elementY);
let element = Browser.elementFromPoint(elementX, elementY);
let zoomRect = this._getZoomRectForElement(element, elementY);
let bv = Browser._browserView;
let scroller = Browser.contentScrollboxScroller;
let elRect = Browser.getBoundingContentRect(aElement);
let elWidth = elRect.width;
let vis = bv.getVisibleRect();
let vrWidth = vis.width;
/* Try to set zoom-level such that once zoomed element is as wide
* as the visible rect */
let zoomLevel = BrowserView.Util.clampZoomLevel((vrWidth - (2 * margin)) / elWidth);
let oldZoomLevel = bv.getZoomLevel();
//dump("element width: " + elWidth + "\n");
//dump("old zoom level: " + oldZoomLevel + "\n");
//dump("new zoom level: " + zoomLevel + "\n");
/* If the new zoom level we've calculated is less than or equal to
* the current zoom level, return early and don't progress further.
* If the new zoom level is higher, continue to zoom inward.
*/
if (oldZoomLevel >= zoomLevel)
if (zoomRect == null)
return false;
bv.beginBatchOperation();
/* XXX
* This isn't ideal.
*/
this.hideSidebars();
bv.setZoomLevel(zoomLevel);
bv.forceContainerResize();
//Browser.forceChromeReflow();
let dx = Math.round(bv.browserToViewport(elRect.left) - margin - vis.left);
let dy = Math.round(bv.browserToViewport(elementY) - (window.innerHeight / 2) - vis.top);
Browser.contentScrollbox.customDragger.dragMove(dx, dy, scroller);
bv.commitBatchOperation();
this.setVisibleRect(zoomRect);
return true;
},
zoomFromPoint: function zoomFromPoint(cX, cY) {
let bv = this._browserView;
let zoomLevel = bv.getZoomForPage();
if (bv.getZoomLevel() != zoomLevel) {
let [elementX, elementY] = this.transformClientToBrowser(cX, cY);
let bv = Browser._browserView;
bv.beginBatchOperation();
bv.zoomToPage();
bv.forceContainerResize();
let dy = Math.round(bv.browserToViewport(elementY) - (window.innerHeight / 2) - bv.getVisibleRect().top);
this.contentScrollbox.customDragger.dragMove(0, dy, this.contentScrollboxScroller);
Browser.forceChromeReflow();
this.hideSidebars();
bv.commitBatchOperation();
let zoomRect = this._getZoomRectForPoint(elementX, elementY, zoomLevel);
this.setVisibleRect(zoomRect);
}
},
getBoundingContentRect: function getBoundingContentRect(contentElem) {
@ -1053,18 +1085,18 @@ var Browser = {
if (!browser)
return null;
let scrollX = { value: 0 };
let scrollY = { value: 0 };
let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser);
cwu.getScrollXY(false, scrollX, scrollY);
let offset = BrowserView.Util.getContentScrollOffset(browser);
let r = contentElem.getBoundingClientRect();
//dump('getBoundingContentRect: clientRect is at ' + r.left + ', ' + r.top + '; scrolls are ' + scrollX.value + ', ' + scrollY.value + '\n');
// step out of iframes and frames, offsetting scroll values
for (let frame = contentElem.ownerDocument.defaultView; frame != browser.contentWindow; frame = frame.parent) {
// adjust client coordinates' origin to be top left of iframe viewport
let rect = frame.frameElement.getBoundingClientRect();
offset.add(rect.left, rect.top);
}
return new Rect(r.left + scrollX.value,
r.top + scrollY.value,
r.width, r.height);
return new Rect(r.left + offset.x, r.top + offset.y, r.width, r.height);
},
/**
@ -2446,6 +2478,18 @@ Tab.prototype = {
},
endLoading: function() {
// Determine at what resolution the browser is rendered based on meta tag
let browser = this._browser;
let windowUtils = browser.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly");
if (handheldFriendly == "true") {
browser.className = "browser-handheld";
} else {
browser.className = "browser";
}
//if (!this._loading)
// dump("!!! Already finished loading this tab, please file a bug\n");
this.setIcon(this._browser.mIconURL);

Просмотреть файл

@ -226,10 +226,15 @@
<notificationbox id="notifications"/>
<!-- Content viewport -->
<stack id="tile-stack" class="window-width window-height">
<scrollbox id="content-scrollbox" style="overflow: hidden;" class="window-width window-height">
<!-- Content viewport -->
<html:div id="tile-container" style="overflow: hidden;"/>
</scrollbox>
<html:canvas id="view-buffer" style="display: none;" moz-opaque="true">
</html:canvas>
</stack>
</vbox>
</scrollbox>