зеркало из https://github.com/mozilla/pjs.git
(half-baked) animated zoom
This commit is contained in:
Родитель
57c5b43da0
Коммит
b0d9b18c4b
|
@ -373,7 +373,6 @@ pref("browser.ui.kinetic.swipeLength", 160);
|
|||
|
||||
// zooming
|
||||
pref("browser.ui.zoom.pageFitGranularity", 9); // don't zoom to fit by less than 1/9
|
||||
pref("browser.ui.zoom.animationFps", 60);
|
||||
pref("browser.ui.zoom.animationDuration", 350); // ms duration of double-tap zoom animation
|
||||
|
||||
// pinch gesture
|
||||
|
|
|
@ -45,219 +45,69 @@ let Cu = Components.utils;
|
|||
|
||||
/**
|
||||
* Responsible for zooming in to a given view rectangle
|
||||
* @param aBrowserView BrowserView instance
|
||||
* @param aZoomRect Optional. Zoom rectangle to be configured
|
||||
*/
|
||||
function AnimatedZoom(aBrowserView) {
|
||||
return;
|
||||
this.bv = aBrowserView;
|
||||
|
||||
this.snapshot = AnimatedZoom.createCanvas();
|
||||
if (this.snapshot.pending_render)
|
||||
return;
|
||||
|
||||
function AnimatedZoom() {
|
||||
// Render a snapshot of the viewport contents around the visible rect
|
||||
let [w, h] = this.bv.getViewportDimensions();
|
||||
let viewportRect = new Rect(0, 0, w, h);
|
||||
this.zoomFrom = this.bv.getVisibleRect().translateInside(viewportRect);
|
||||
|
||||
// try to cover twice the size of the current visible rect
|
||||
this.snapshotRect = this.bv.getVisibleRect().inflate(2);
|
||||
|
||||
// sanitize the snapshot rectangle to fit inside viewport
|
||||
this.snapshotRect.translateInside(viewportRect).restrictTo(viewportRect).expandToIntegers();
|
||||
this.snapshotRect.width = Math.min(this.snapshotRect.width, this.snapshot.width);
|
||||
this.snapshotRect.height = Math.min(this.snapshotRect.height, this.snapshot.height);
|
||||
|
||||
let snapshotCtx = this.snapshot.MozGetIPCContext("2d")
|
||||
snapshotCtx.clearRect(0, 0, this.snapshotRect.width, this.snapshotRect.height);
|
||||
this.bv.renderToCanvas(this.snapshot, this.snapshotRect.width, this.snapshotRect.height, this.snapshotRect.clone());
|
||||
|
||||
let remote = !this.bv.getBrowser().contentWindow;
|
||||
if (remote) {
|
||||
this.snapshot.addEventListener("MozAsyncCanvasRender", this, false);
|
||||
this.snapshot.pending_render = true;
|
||||
} else {
|
||||
this.setupCanvas();
|
||||
}
|
||||
this.animationDuration = Services.prefs.getIntPref("browser.ui.zoom.animationDuration");
|
||||
}
|
||||
|
||||
AnimatedZoom.prototype.handleEvent = function(aEvent) {
|
||||
if (aEvent.type == "MozAsyncCanvasRender") {
|
||||
let snapshot = aEvent.originalTarget;
|
||||
snapshot.pending_render = false;
|
||||
snapshot.removeEventListener("MozAsyncCanvasRender", this, false);
|
||||
AnimatedZoom.prototype = {
|
||||
startTimer: function() {
|
||||
if (this.zoomTo && !this.beginTime) {
|
||||
Browser.hideSidebars();
|
||||
Browser.hideTitlebar();
|
||||
Browser.forceChromeReflow();
|
||||
|
||||
if (this.snapshot == snapshot) {
|
||||
this.setupCanvas();
|
||||
this.startTimer();
|
||||
let browserRect = Rect.fromRect(getBrowser().getBoundingClientRect());
|
||||
this.zoomFrom = browserRect.translate(getBrowser()._frameLoader.viewportScrollX, getBrowser()._frameLoader.viewportScrollY);
|
||||
|
||||
this.updateTo(this.zoomFrom);
|
||||
this.beginTime = Date.now();
|
||||
window.addEventListener("MozBeforePaint", this, false);
|
||||
mozRequestAnimationFrame();
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** Creating a canvas element of width and height. */
|
||||
AnimatedZoom.createCanvas = function(aRemote) {
|
||||
if (!this._canvas) {
|
||||
let canvas = document.createElementNS(kXHTMLNamespaceURI, "canvas");
|
||||
canvas.width = Math.max(window.innerWidth, window.innerHeight) * 2;
|
||||
canvas.height = Math.max(window.innerWidth, window.innerHeight) * 2;
|
||||
canvas.mozOpaque = true;
|
||||
this._canvas = canvas;
|
||||
}
|
||||
return this._canvas;
|
||||
};
|
||||
|
||||
AnimatedZoom.prototype.setupCanvas = function() {
|
||||
return;
|
||||
|
||||
// stop live rendering during zooming
|
||||
this.bv.pauseRendering();
|
||||
|
||||
// hide ui elements to avoid undefined states after zoom
|
||||
Browser.hideTitlebar();
|
||||
Browser.hideSidebars();
|
||||
|
||||
let clientVis = Browser.browserViewToClientRect(this.bv.getCriticalRect());
|
||||
let viewBuffer = Elements.viewBuffer;
|
||||
viewBuffer.left = clientVis.left;
|
||||
viewBuffer.top = clientVis.top;
|
||||
viewBuffer.width = this.zoomFrom.width;
|
||||
viewBuffer.height = this.zoomFrom.height;
|
||||
viewBuffer.style.display = "block";
|
||||
|
||||
// configure defaults for the canvas' drawing context
|
||||
let ctx = viewBuffer.getContext("2d");
|
||||
|
||||
// disable smoothing and use the fastest composition operation
|
||||
ctx.mozImageSmoothingEnabled = false;
|
||||
ctx.globalCompositeOperation = "copy";
|
||||
|
||||
// set background fill pattern
|
||||
let backgroundImage = new Image();
|
||||
backgroundImage.src = "chrome://browser/content/checkerboard.png";
|
||||
ctx.fillStyle = ctx.createPattern(backgroundImage, "repeat");
|
||||
|
||||
this.canvasReady = true;
|
||||
};
|
||||
|
||||
AnimatedZoom.prototype.startTimer = function() {
|
||||
if (this.zoomTo && this.canvasReady && !this.timer) {
|
||||
this.updateTo(this.zoomFrom);
|
||||
|
||||
// start animation timer
|
||||
this.counter = 0;
|
||||
this.inc = 1.0 / Services.prefs.getIntPref("browser.ui.zoom.animationDuration");
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.interval = 1000 / Services.prefs.getIntPref("browser.ui.zoom.animationFps");
|
||||
this.timer.initWithCallback(Util.bind(this._callback, this), this.interval, this.timer.TYPE_REPEATING_PRECISE);
|
||||
|
||||
// force first update to be according to FPS even though first callback would take longer
|
||||
this.lastTime = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/** Updates the zoom to new rect. */
|
||||
AnimatedZoom.prototype.updateTo = function(nextRect) {
|
||||
this.zoomRect = nextRect;
|
||||
|
||||
if (this.snapshot.pending_render)
|
||||
return;
|
||||
|
||||
// prepare to draw to the zoom canvas
|
||||
let canvasRect = new Rect(0, 0, Elements.viewBuffer.width, Elements.viewBuffer.height);
|
||||
let ctx = Elements.viewBuffer.getContext("2d");
|
||||
ctx.save();
|
||||
|
||||
// srcRect = area inside this.snapshot to copy from
|
||||
let srcRect = nextRect.intersect(this.snapshotRect);
|
||||
if (srcRect.isEmpty())
|
||||
return;
|
||||
|
||||
// destRect = respective area inside canvas to paint to. The dimensions of
|
||||
// destRect within canvas equals those of srcRect within nextRect.
|
||||
let s = canvasRect.width / nextRect.width;
|
||||
let destRect = srcRect.clone().translate(-nextRect.x, -nextRect.y).scale(s, s);
|
||||
|
||||
// adjust from viewport coordinates to snapshot canvas coordinates
|
||||
srcRect.translate(-this.snapshotRect.left, -this.snapshotRect.top);
|
||||
|
||||
// fill background and draw the (possibly scaled) image
|
||||
destRect.restrictTo(canvasRect).expandToIntegers();
|
||||
|
||||
ctx.drawImage(this.snapshot,
|
||||
Math.floor(srcRect.left), Math.floor(srcRect.top),
|
||||
Math.floor(srcRect.width), Math.floor(srcRect.height),
|
||||
Math.floor(destRect.left), Math.floor(destRect.top),
|
||||
Math.floor(destRect.width), Math.floor(destRect.height));
|
||||
|
||||
// clip the over leftover area and fill it with checkerboard
|
||||
let unknowns = canvasRect.subtract(destRect);
|
||||
if (unknowns.length > 0) {
|
||||
ctx.beginPath();
|
||||
unknowns.forEach(function(r) { ctx.rect(r.x, r.y, r.width, r.height); });
|
||||
ctx.clip();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
/** Starts an animated zoom to zoomRect. */
|
||||
AnimatedZoom.prototype.animateTo = function(aZoomRect) {
|
||||
this.zoomTo = aZoomRect;
|
||||
this.finish();
|
||||
return;
|
||||
this.startTimer();
|
||||
};
|
||||
|
||||
/** Callback for the animation. */
|
||||
AnimatedZoom.prototype._callback = function() {
|
||||
try {
|
||||
if (this.counter < 1) {
|
||||
// increase animation position according to elapsed time
|
||||
let now = Date.now();
|
||||
if (this.lastTime == 0)
|
||||
this.lastTime = now - this.interval; // fix lastTime if not yet set (first frame)
|
||||
this.counter += this.inc * (now - this.lastTime);
|
||||
this.lastTime = now;
|
||||
|
||||
// update scaled image to interpolated rectangle
|
||||
let rect = this.zoomFrom.blend(this.zoomTo, Math.min(this.counter, 1));
|
||||
this.updateTo(rect);
|
||||
handleEvent: function(aEvent) {
|
||||
try {
|
||||
let tdiff = aEvent.timeStamp - this.beginTime;
|
||||
let counter = tdiff / this.animationDuration;
|
||||
if (counter < 1) {
|
||||
// update browser to interpolated rectangle
|
||||
let rect = this.zoomFrom.blend(this.zoomTo, Math.min(counter, 1));
|
||||
this.updateTo(rect);
|
||||
mozRequestAnimationFrame();
|
||||
}
|
||||
else {
|
||||
// last cycle already rendered final scaled image, now clean up
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// last cycle already rendered final scaled image, now clean up
|
||||
catch(e) {
|
||||
this.finish();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
Util.dumpLn("Error while zooming. Please report error at:", e);
|
||||
this.finish();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/** Stop animation, zoom to point, and clean up. */
|
||||
AnimatedZoom.prototype.finish = function() {
|
||||
Browser.setVisibleRect(this.zoomTo);
|
||||
return;
|
||||
try {
|
||||
Elements.viewBuffer.style.display = "none";
|
||||
/** Updates the zoom to new rect. */
|
||||
updateTo: function(nextRect) {
|
||||
let zoomRatio = window.innerWidth / nextRect.width;
|
||||
let zoomLevel = getBrowser().scale * zoomRatio;
|
||||
getBrowser()._frameLoader.setViewportScale(zoomLevel, zoomLevel);
|
||||
getBrowser()._frameLoader.scrollViewportTo(nextRect.left * zoomRatio, nextRect.top * zoomRatio);
|
||||
},
|
||||
|
||||
// resume live rendering
|
||||
this.bv.resumeRendering(true);
|
||||
/** Starts an animated zoom to zoomRect. */
|
||||
animateTo: function(aZoomRect) {
|
||||
this.zoomTo = aZoomRect.clone();
|
||||
this.startTimer();
|
||||
},
|
||||
|
||||
// if we actually zoomed somewhere, clean up the UI to normal
|
||||
if (this.zoomRect)
|
||||
Browser.setVisibleRect(this.zoomRect);
|
||||
}
|
||||
finally {
|
||||
if (this.timer) {
|
||||
this.timer.cancel();
|
||||
this.timer = null;
|
||||
}
|
||||
this.snapshot = null;
|
||||
/** Stop animation, zoom to point, and clean up. */
|
||||
finish: function() {
|
||||
window.removeEventListener("MozBeforePaint", this, false);
|
||||
Browser.setVisibleRect(this.zoomTo);
|
||||
this.beginTime = null;
|
||||
this.zoomTo = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -361,13 +361,6 @@ let ContentScroll = {
|
|||
content.scrollBy(json.dx, json.dy);
|
||||
break;
|
||||
|
||||
case "Content:SetResolution": {
|
||||
let cwu = Util.getWindowUtils(content);
|
||||
cwu.setResolution(json.zoomLevel, json.zoomLevel);
|
||||
sendAsyncMessage("Content:SetResolution:Return", { zoomLevel: json.zoomLevel });
|
||||
break;
|
||||
}
|
||||
|
||||
case "Content:SetCacheViewport": {
|
||||
let displayport = new Rect(json.x, json.y, json.w, json.h);
|
||||
if (displayport.isEmpty())
|
||||
|
@ -375,6 +368,7 @@ let ContentScroll = {
|
|||
|
||||
let cwu = Util.getWindowUtils(content);
|
||||
cwu.setDisplayPort(displayport.x, displayport.y, displayport.width, displayport.height);
|
||||
cwu.setResolution(json.zoomLevel, json.zoomLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -163,11 +163,6 @@
|
|||
this._viewportHeightInCSSPx = aMessage.json.viewportHeight;
|
||||
this._updateCacheViewport();
|
||||
break;
|
||||
|
||||
case "Content:SetResolution:Return":
|
||||
this._zoomLevel = aMessage.json.zoomLevel;
|
||||
this._updateCacheViewport();
|
||||
break;
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
@ -450,7 +445,8 @@
|
|||
x: (frameLoader.viewportScrollX - offscreenX) / this._zoomLevel,
|
||||
y: (frameLoader.viewportScrollY - offscreenY) / this._zoomLevel,
|
||||
w: (this._viewportWidthInCSSPx + offscreenX * 2) / this._zoomLevel,
|
||||
h: (this._viewportHeightInCSSPx + offscreenY * 2) / this._zoomLevel
|
||||
h: (this._viewportHeightInCSSPx + offscreenY * 2) / this._zoomLevel,
|
||||
zoomLevel: this._zoomLevel
|
||||
});
|
||||
|
||||
this._pendingPixelsX = 0;
|
||||
|
@ -479,8 +475,9 @@
|
|||
<![CDATA[
|
||||
if (zl <= 0) throw "Bad zoom level given.";
|
||||
|
||||
this._zoomLevel = zl;
|
||||
this._frameLoader.setViewportScale(zl, zl);
|
||||
this.messageManager.sendAsyncMessage("Content:SetResolution", { zoomLevel: zl });
|
||||
this._updateCacheViewport();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -561,8 +558,6 @@
|
|||
}, this);
|
||||
|
||||
this.messageManager.addMessageListener("MozScrolledAreaChanged", this);
|
||||
this.messageManager.addMessageListener("Content:SetDisplayportArea:Return", this);
|
||||
this.messageManager.addMessageListener("Content:SetResolution:Return", this);
|
||||
|
||||
this._webProgress._init();
|
||||
]]>
|
||||
|
|
|
@ -917,11 +917,8 @@ var Browser = {
|
|||
this.hideSidebars();
|
||||
this.hideTitlebar();
|
||||
|
||||
getBrowser().setScale(zoomLevel);
|
||||
// XXX
|
||||
setTimeout(function() {
|
||||
getBrowser().scrollTo(scrollX, scrollY);
|
||||
}, 500);
|
||||
getBrowser().setScale(this.selectedTab.clampZoomLevel(zoomLevel));
|
||||
getBrowser().scrollTo(scrollX, scrollY);
|
||||
},
|
||||
|
||||
zoomToPoint: function zoomToPoint(cX, cY, aRect) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче