"use strict";
var Util = require('../util'),
Base = require('../base');
// reduced scale rate
var SCALE_RATE = 0.7;
var SCALE_TO_DURATION = 300;
/**
* A scalable plugin for xscroll.
* @constructor
* @param {object} cfg
* @param {number} cfg.minScale min value for scale
* @param {number} cfg.maxScale max value for scale
* @param {number} cfg.duration duration for scale animation
* @extends {Base}
*/
var Scale = function(cfg) {
Scale.superclass.constructor.call(this, cfg);
this.userConfig = Util.mix({
minScale: 1,
maxScale: 2,
duration: SCALE_TO_DURATION
}, cfg);
}
Util.extend(Scale, Base, {
/**
* a pluginId
* @memberOf Scale
* @type {string}
*/
pluginId: "scale",
/**
* plugin initializer
* @memberOf Scale
* @override Scale
* @return {Infinite}
*/
pluginInitializer: function(xscroll) {
var self = this;
self.scale = 1;
self.xscroll = xscroll.render();
self.initialContainerWidth = xscroll.containerWidth;
self.initialContainerHeight = xscroll.containerHeight;
self.minScale = self.userConfig.minScale || Math.max(xscroll.width / xscroll.containerWidth, xscroll.height / xscroll.containerHeight);
self.maxScale = self.userConfig.maxScale || 1;
self._bindEvt();
return self;
},
/**
* detroy the plugin
* @memberOf Scale
* @override Base
* @return {Scale}
*/
pluginDestructor: function() {
var self = this;
var xscroll = self.xscroll;
xscroll.off("doubletap", self._doubleTapHandler, self);
xscroll.off("pinchstart", self._pinchStartHandler, self);
xscroll.off("pinchmove", self._pinchHandler, self);
xscroll.off("pinchend pinchcancel", self._pinchEndHandler, self);
return self;
},
_doubleTapHandler: function(e) {
var self = this;
var xscroll = self.xscroll;
var minScale = self.userConfig.minScale;
var maxScale = self.userConfig.maxScale;
var duration = self.userConfig.duration;
self.originX = (e.center.x - xscroll.x) / xscroll.containerWidth;
self.originY = (e.center.y - xscroll.y) / xscroll.containerHeight;
xscroll.scale > self.minScale ? self.scaleTo(minScale, self.originX, self.originY, duration) : self.scaleTo(maxScale, self.originX, self.originY, duration);
return self;
},
_pinchStartHandler: function(e) {
var self = this;
var xscroll = self.xscroll;
//disable pan gesture
self.disablePan();
xscroll.stop();
self.isScaling = false;
self.scale = xscroll.scale;
self.originX = (e.center.x - xscroll.x) / xscroll.containerWidth;
self.originY = (e.center.y - xscroll.y) / xscroll.containerHeight;
},
_pinchHandler: function(e) {
var self = this;
var scale = self.scale;
var xscroll = self.xscroll;
var originX = self.originX;
var originY = self.originY;
var __scale = scale * e.scale;
if (__scale <= self.userConfig.minScale) {
// s = 1/2 * a * 2^(s/a)
__scale = 0.5 * self.userConfig.minScale * Math.pow(2, __scale / self.userConfig.minScale);
}
if (__scale >= self.userConfig.maxScale) {
// s = 2 * a * 1/2^(a/s)
__scale = 2 * self.userConfig.maxScale * Math.pow(0.5, self.userConfig.maxScale / __scale);
}
self._scale(__scale, originX, originY);
self.xscroll.translate(xscroll.x, xscroll.y, __scale, 'e.scale', e.scale);
},
disablePan: function() {
this.xscroll.mc.get("pan").set({
enable: false
});
return this;
},
enablePan: function() {
this.xscroll.mc.get("pan").set({
enable: true
});
return this;
},
_pinchEndHandler: function(e) {
var self = this;
var originX = self.originX;
var originY = self.originY;
var xscroll = self.xscroll;
if (xscroll.scale < self.minScale) {
self.scaleTo(self.minScale, originX, originY, SCALE_TO_DURATION, "ease-out", self.enablePan);
} else if (xscroll.scale > self.maxScale) {
self.scaleTo(self.maxScale, originX, originY, SCALE_TO_DURATION, "ease-out", self.enablePan);
} else {
self.enablePan();
}
},
_bindEvt: function() {
var self = this;
var xscroll = self.xscroll;
xscroll.on("doubletap", self._doubleTapHandler, self);
xscroll.on("pinchstart", self._pinchStartHandler, self);
xscroll.on("pinchmove", self._pinchHandler, self);
xscroll.on("pinchend pinchcancel", self._pinchEndHandler, self);
return self;
},
_scale: function(scale, originX, originY) {
var self = this;
var xscroll = self.xscroll;
var boundry = self.xscroll.boundry;
if (xscroll.scale == scale || !scale) return;
if (!self.isScaling) {
self.scaleBegin = xscroll.scale;
self.isScaling = true;
self.scaleBeginX = xscroll.x;
self.scaleBeginY = xscroll.y;
}
if (originX) {
self.originX = originX;
}
if (originY) {
self.originY = originY;
}
var containerWidth = scale * self.initialContainerWidth;
var containerHeight = scale * self.initialContainerHeight;
xscroll.containerWidth = Math.round(containerWidth > xscroll.width ? containerWidth : xscroll.width);
xscroll.containerHeight = Math.round(containerHeight > xscroll.height ? containerHeight : xscroll.height);
xscroll.scale = scale;
var x = originX * (self.initialContainerWidth * self.scaleBegin - xscroll.containerWidth) + self.scaleBeginX;
var y = originY * (self.initialContainerHeight * self.scaleBegin - xscroll.containerHeight) + self.scaleBeginY;
if (x > boundry.left) {
x = boundry.left;
}
if (y > boundry.top) {
y = boundry.top;
}
if (x < boundry.right - xscroll.containerWidth) {
x = boundry.right - xscroll.containerWidth;
}
if (y < boundry.bottom - xscroll.containerHeight) {
y = boundry.bottom - xscroll.containerHeight;
}
xscroll.x = x;
xscroll.y = y;
},
/**
* scale with an animation
* @memberOf Scale
* @param {number} scale
* @param {number} originX 0~1
* @param {number} originY 0~1
* @param {number} duration
* @param {string} easing
* @param {function} callback
*/
scaleTo: function(scale, originX, originY, duration, easing, callback) {
var self = this;
var xscroll = self.xscroll;
//unscalable
if (xscroll.scale == scale || !scale) return;
var duration = duration || SCALE_TO_DURATION;
var easing = easing || "ease-out";
self.scaleStart = xscroll.scale || 1;
// transitionStr = [transformStr, " ", duration , "s ", easing, " 0s"].join("");
self._scale(scale, originX, originY);
xscroll._animate("x", "translateX(" + xscroll.x + "px) scale(" + scale + ")", duration, easing, function(e) {
callback && callback.call(self, e);
});
xscroll._animate("y", "translateY(" + xscroll.y + "px)", duration, easing, function(e) {
callback && callback.call(self, e);
});
xscroll.__timers.x.timer.off("run", self.scaleHandler, self);
xscroll.__timers.x.timer.off("stop", self.scaleendHandler, self);
self.scaleHandler = function(e) {
var _scale = (scale - self.scaleStart) * e.percent + self.scaleStart;
//trigger scroll event
self.trigger("scale", {
scale: _scale,
origin: {
x: originX,
y: originY
}
});
};
self.scaleendHandler = function(e) {
self.isScaling = false;
//enable pan gesture
self.enablePan();
self.trigger("scaleend", {
type: "scaleend",
scale: self.scale,
origin: {
x: originX,
y: originY
}
})
}
xscroll.__timers.x.timer.on("run", self.scaleHandler, self);
xscroll.__timers.x.timer.on("stop", self.scaleendHandler, self);
self.trigger("scaleanimate", {
type:"scaleanimate",
scale: xscroll.scale,
duration: duration,
easing: easing,
offset: {
x: xscroll.x,
y: xscroll.y
},
origin: {
x: originX,
y: originY
}
});
}
});
if (typeof module == 'object' && module.exports) {
module.exports = Scale;
}