123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579 |
- /*!
- * better-scroll / zoom
- * (c) 2016-2021 ustbhuangyi
- * Released under the MIT License.
- */
- var sourcePrefix = 'plugins.zoom';
- var propertiesMap = [
- {
- key: 'zoomTo',
- name: 'zoomTo'
- }
- ];
- var propertiesConfig = propertiesMap.map(function (item) {
- return {
- key: item.key,
- sourceKey: sourcePrefix + "." + item.name
- };
- });
- // ssr support
- var inBrowser = typeof window !== 'undefined';
- var ua = inBrowser && navigator.userAgent.toLowerCase();
- !!(ua && /wechatdevtools/.test(ua));
- ua && ua.indexOf('android') > 0;
- /* istanbul ignore next */
- ((function () {
- if (typeof ua === 'string') {
- var regex = /os (\d\d?_\d(_\d)?)/;
- var matches = regex.exec(ua);
- if (!matches)
- return false;
- var parts = matches[1].split('_').map(function (item) {
- return parseInt(item, 10);
- });
- // ios version >= 13.4 issue 982
- return !!(parts[0] === 13 && parts[1] >= 4);
- }
- return false;
- }))();
- /* istanbul ignore next */
- var supportsPassive = false;
- /* istanbul ignore next */
- if (inBrowser) {
- var EventName = 'test-passive';
- try {
- var opts = {};
- Object.defineProperty(opts, 'passive', {
- get: function () {
- supportsPassive = true;
- },
- }); // https://github.com/facebook/flow/issues/285
- window.addEventListener(EventName, function () { }, opts);
- }
- catch (e) { }
- }
- function getNow() {
- return window.performance &&
- window.performance.now &&
- window.performance.timing
- ? window.performance.now() + window.performance.timing.navigationStart
- : +new Date();
- }
- var extend = function (target, source) {
- for (var key in source) {
- target[key] = source[key];
- }
- return target;
- };
- function getDistance(x, y) {
- return Math.sqrt(x * x + y * y);
- }
- function between(x, min, max) {
- if (x < min) {
- return min;
- }
- if (x > max) {
- return max;
- }
- return x;
- }
- var elementStyle = (inBrowser &&
- document.createElement('div').style);
- var vendor = (function () {
- /* istanbul ignore if */
- if (!inBrowser) {
- return false;
- }
- var transformNames = [
- {
- key: 'standard',
- value: 'transform',
- },
- {
- key: 'webkit',
- value: 'webkitTransform',
- },
- {
- key: 'Moz',
- value: 'MozTransform',
- },
- {
- key: 'O',
- value: 'OTransform',
- },
- {
- key: 'ms',
- value: 'msTransform',
- },
- ];
- for (var _i = 0, transformNames_1 = transformNames; _i < transformNames_1.length; _i++) {
- var obj = transformNames_1[_i];
- if (elementStyle[obj.value] !== undefined) {
- return obj.key;
- }
- }
- /* istanbul ignore next */
- return false;
- })();
- /* istanbul ignore next */
- function prefixStyle(style) {
- if (vendor === false) {
- return style;
- }
- if (vendor === 'standard') {
- if (style === 'transitionEnd') {
- return 'transitionend';
- }
- return style;
- }
- return vendor + style.charAt(0).toUpperCase() + style.substr(1);
- }
- function offsetToBody(el) {
- var rect = el.getBoundingClientRect();
- return {
- left: -(rect.left + window.pageXOffset),
- top: -(rect.top + window.pageYOffset),
- };
- }
- vendor && vendor !== 'standard' ? '-' + vendor.toLowerCase() + '-' : '';
- var transform = prefixStyle('transform');
- var transition = prefixStyle('transition');
- inBrowser && prefixStyle('perspective') in elementStyle;
- var style = {
- transform: transform,
- transition: transition,
- transitionTimingFunction: prefixStyle('transitionTimingFunction'),
- transitionDuration: prefixStyle('transitionDuration'),
- transitionDelay: prefixStyle('transitionDelay'),
- transformOrigin: prefixStyle('transformOrigin'),
- transitionEnd: prefixStyle('transitionEnd'),
- transitionProperty: prefixStyle('transitionProperty'),
- };
- function getRect(el) {
- /* istanbul ignore if */
- if (el instanceof window.SVGElement) {
- var rect = el.getBoundingClientRect();
- return {
- top: rect.top,
- left: rect.left,
- width: rect.width,
- height: rect.height,
- };
- }
- else {
- return {
- top: el.offsetTop,
- left: el.offsetLeft,
- width: el.offsetWidth,
- height: el.offsetHeight,
- };
- }
- }
- var ease = {
- // easeOutQuint
- swipe: {
- style: 'cubic-bezier(0.23, 1, 0.32, 1)',
- fn: function (t) {
- return 1 + --t * t * t * t * t;
- }
- },
- // easeOutQuard
- swipeBounce: {
- style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
- fn: function (t) {
- return t * (2 - t);
- }
- },
- // easeOutQuart
- bounce: {
- style: 'cubic-bezier(0.165, 0.84, 0.44, 1)',
- fn: function (t) {
- return 1 - --t * t * t * t;
- }
- }
- };
- var DEFAULT_INTERVAL = 1000 / 60;
- var windowCompat = inBrowser && window;
- /* istanbul ignore next */
- function noop() { }
- var requestAnimationFrame = (function () {
- /* istanbul ignore if */
- if (!inBrowser) {
- return noop;
- }
- return (windowCompat.requestAnimationFrame ||
- windowCompat.webkitRequestAnimationFrame ||
- windowCompat.mozRequestAnimationFrame ||
- windowCompat.oRequestAnimationFrame ||
- // if all else fails, use setTimeout
- function (callback) {
- return window.setTimeout(callback, callback.interval || DEFAULT_INTERVAL); // make interval as precise as possible.
- });
- })();
- var cancelAnimationFrame = (function () {
- /* istanbul ignore if */
- if (!inBrowser) {
- return noop;
- }
- return (windowCompat.cancelAnimationFrame ||
- windowCompat.webkitCancelAnimationFrame ||
- windowCompat.mozCancelAnimationFrame ||
- windowCompat.oCancelAnimationFrame ||
- function (id) {
- window.clearTimeout(id);
- });
- })();
- var TWO_FINGERS = 2;
- var RAW_SCALE = 1;
- var Zoom = /** @class */ (function () {
- function Zoom(scroll) {
- this.scroll = scroll;
- this.scale = RAW_SCALE;
- this.prevScale = 1;
- this.init();
- }
- Zoom.prototype.init = function () {
- this.handleBScroll();
- this.handleOptions();
- this.handleHooks();
- this.tryInitialZoomTo(this.zoomOpt);
- };
- Zoom.prototype.zoomTo = function (scale, x, y, bounceTime) {
- var _a = this.resolveOrigin(x, y), originX = _a.originX, originY = _a.originY;
- var origin = {
- x: originX,
- y: originY,
- baseScale: this.scale,
- };
- this._doZoomTo(scale, origin, bounceTime, true);
- };
- Zoom.prototype.handleBScroll = function () {
- this.scroll.proxy(propertiesConfig);
- this.scroll.registerType([
- 'beforeZoomStart',
- 'zoomStart',
- 'zooming',
- 'zoomEnd',
- ]);
- };
- Zoom.prototype.handleOptions = function () {
- var userOptions = (this.scroll.options.zoom === true
- ? {}
- : this.scroll.options.zoom);
- var defaultOptions = {
- start: 1,
- min: 1,
- max: 4,
- initialOrigin: [0, 0],
- minimalZoomDistance: 5,
- bounceTime: 800,
- };
- this.zoomOpt = extend(defaultOptions, userOptions);
- };
- Zoom.prototype.handleHooks = function () {
- var _this = this;
- var scroll = this.scroll;
- var scroller = this.scroll.scroller;
- this.wrapper = this.scroll.scroller.wrapper;
- this.setTransformOrigin(this.scroll.scroller.content);
- var scrollBehaviorX = scroller.scrollBehaviorX;
- var scrollBehaviorY = scroller.scrollBehaviorY;
- this.hooksFn = [];
- // BScroll
- this.registerHooks(scroll.hooks, scroll.hooks.eventTypes.contentChanged, function (content) {
- _this.setTransformOrigin(content);
- _this.scale = RAW_SCALE;
- _this.tryInitialZoomTo(_this.zoomOpt);
- });
- this.registerHooks(scroll.hooks, scroll.hooks.eventTypes.beforeInitialScrollTo, function () {
- // if perform a zoom action, we should prevent initial scroll when initialised
- if (_this.zoomOpt.start !== RAW_SCALE) {
- return true;
- }
- });
- // enlarge boundary
- this.registerHooks(scrollBehaviorX.hooks, scrollBehaviorX.hooks.eventTypes.beforeComputeBoundary, function () {
- // content may change, don't cache it's size
- var contentSize = getRect(_this.scroll.scroller.content);
- scrollBehaviorX.contentSize = Math.floor(contentSize.width * _this.scale);
- });
- this.registerHooks(scrollBehaviorY.hooks, scrollBehaviorY.hooks.eventTypes.beforeComputeBoundary, function () {
- // content may change, don't cache it's size
- var contentSize = getRect(_this.scroll.scroller.content);
- scrollBehaviorY.contentSize = Math.floor(contentSize.height * _this.scale);
- });
- // touch event
- this.registerHooks(scroller.actions.hooks, scroller.actions.hooks.eventTypes.start, function (e) {
- var numberOfFingers = (e.touches && e.touches.length) || 0;
- _this.fingersOperation(numberOfFingers);
- if (numberOfFingers === TWO_FINGERS) {
- _this.zoomStart(e);
- }
- });
- this.registerHooks(scroller.actions.hooks, scroller.actions.hooks.eventTypes.beforeMove, function (e) {
- var numberOfFingers = (e.touches && e.touches.length) || 0;
- _this.fingersOperation(numberOfFingers);
- if (numberOfFingers === TWO_FINGERS) {
- _this.zoom(e);
- return true;
- }
- });
- this.registerHooks(scroller.actions.hooks, scroller.actions.hooks.eventTypes.beforeEnd, function (e) {
- var numberOfFingers = _this.fingersOperation();
- if (numberOfFingers === TWO_FINGERS) {
- _this.zoomEnd();
- return true;
- }
- });
- this.registerHooks(scroller.translater.hooks, scroller.translater.hooks.eventTypes.beforeTranslate, function (transformStyle, point) {
- var scale = point.scale ? point.scale : _this.prevScale;
- _this.prevScale = scale;
- transformStyle.push("scale(" + scale + ")");
- });
- this.registerHooks(scroller.hooks, scroller.hooks.eventTypes.scrollEnd, function () {
- if (_this.fingersOperation() === TWO_FINGERS) {
- _this.scroll.trigger(_this.scroll.eventTypes.zoomEnd, {
- scale: _this.scale,
- });
- }
- });
- this.registerHooks(this.scroll.hooks, 'destroy', this.destroy);
- };
- Zoom.prototype.setTransformOrigin = function (content) {
- content.style[style.transformOrigin] = '0 0';
- };
- Zoom.prototype.tryInitialZoomTo = function (options) {
- var start = options.start, initialOrigin = options.initialOrigin;
- var _a = this.scroll.scroller, scrollBehaviorX = _a.scrollBehaviorX, scrollBehaviorY = _a.scrollBehaviorY;
- if (start !== RAW_SCALE) {
- // Movable plugin may wanna modify minScrollPos or maxScrollPos
- // so we force Movable to caculate them
- this.resetBoundaries([scrollBehaviorX, scrollBehaviorY]);
- this.zoomTo(start, initialOrigin[0], initialOrigin[1], 0);
- }
- };
- // getter or setter operation
- Zoom.prototype.fingersOperation = function (amounts) {
- if (typeof amounts === 'number') {
- this.numberOfFingers = amounts;
- }
- else {
- return this.numberOfFingers;
- }
- };
- Zoom.prototype._doZoomTo = function (scale, origin, time, useCurrentPos) {
- var _this = this;
- if (time === void 0) { time = this.zoomOpt.bounceTime; }
- if (useCurrentPos === void 0) { useCurrentPos = false; }
- var _a = this.zoomOpt, min = _a.min, max = _a.max;
- var fromScale = this.scale;
- var toScale = between(scale, min, max);
- (function () {
- if (time === 0) {
- _this.scroll.trigger(_this.scroll.eventTypes.zooming, {
- scale: toScale,
- });
- return;
- }
- if (time > 0) {
- var timer_1;
- var startTime_1 = getNow();
- var endTime_1 = startTime_1 + time;
- var scheduler_1 = function () {
- var now = getNow();
- if (now >= endTime_1) {
- _this.scroll.trigger(_this.scroll.eventTypes.zooming, {
- scale: toScale,
- });
- cancelAnimationFrame(timer_1);
- return;
- }
- var ratio = ease.bounce.fn((now - startTime_1) / time);
- var currentScale = ratio * (toScale - fromScale) + fromScale;
- _this.scroll.trigger(_this.scroll.eventTypes.zooming, {
- scale: currentScale,
- });
- timer_1 = requestAnimationFrame(scheduler_1);
- };
- // start scheduler job
- scheduler_1();
- }
- })();
- // suppose you are zooming by two fingers
- this.fingersOperation(2);
- this._zoomTo(toScale, fromScale, origin, time, useCurrentPos);
- };
- Zoom.prototype._zoomTo = function (toScale, fromScale, origin, time, useCurrentPos) {
- if (useCurrentPos === void 0) { useCurrentPos = false; }
- var ratio = toScale / origin.baseScale;
- this.setScale(toScale);
- var scroller = this.scroll.scroller;
- var scrollBehaviorX = scroller.scrollBehaviorX, scrollBehaviorY = scroller.scrollBehaviorY;
- this.resetBoundaries([scrollBehaviorX, scrollBehaviorY]);
- // position is restrained in boundary
- var newX = this.getNewPos(origin.x, ratio, scrollBehaviorX, true, useCurrentPos);
- var newY = this.getNewPos(origin.y, ratio, scrollBehaviorY, true, useCurrentPos);
- if (scrollBehaviorX.currentPos !== Math.round(newX) ||
- scrollBehaviorY.currentPos !== Math.round(newY) ||
- toScale !== fromScale) {
- scroller.scrollTo(newX, newY, time, ease.bounce, {
- start: {
- scale: fromScale,
- },
- end: {
- scale: toScale,
- },
- });
- }
- };
- Zoom.prototype.resolveOrigin = function (x, y) {
- var _a = this.scroll.scroller, scrollBehaviorX = _a.scrollBehaviorX, scrollBehaviorY = _a.scrollBehaviorY;
- var resolveFormula = {
- left: function () {
- return 0;
- },
- top: function () {
- return 0;
- },
- right: function () {
- return scrollBehaviorX.contentSize;
- },
- bottom: function () {
- return scrollBehaviorY.contentSize;
- },
- center: function (index) {
- var baseSize = index === 0
- ? scrollBehaviorX.contentSize
- : scrollBehaviorY.contentSize;
- return baseSize / 2;
- },
- };
- return {
- originX: typeof x === 'number' ? x : resolveFormula[x](0),
- originY: typeof y === 'number' ? y : resolveFormula[y](1),
- };
- };
- Zoom.prototype.zoomStart = function (e) {
- var firstFinger = e.touches[0];
- var secondFinger = e.touches[1];
- this.startDistance = this.getFingerDistance(e);
- this.startScale = this.scale;
- var _a = offsetToBody(this.wrapper), left = _a.left, top = _a.top;
- this.origin = {
- x: Math.abs(firstFinger.pageX + secondFinger.pageX) / 2 +
- left -
- this.scroll.x,
- y: Math.abs(firstFinger.pageY + secondFinger.pageY) / 2 +
- top -
- this.scroll.y,
- baseScale: this.startScale,
- };
- this.scroll.trigger(this.scroll.eventTypes.beforeZoomStart);
- };
- Zoom.prototype.zoom = function (e) {
- var currentDistance = this.getFingerDistance(e);
- // at least minimalZoomDistance pixels for the zoom to initiate
- if (!this.zoomed &&
- Math.abs(currentDistance - this.startDistance) <
- this.zoomOpt.minimalZoomDistance) {
- return;
- }
- // when out of boundary , perform a damping algorithm
- var endScale = this.dampingScale((currentDistance / this.startDistance) * this.startScale);
- var ratio = endScale / this.startScale;
- this.setScale(endScale);
- if (!this.zoomed) {
- this.zoomed = true;
- this.scroll.trigger(this.scroll.eventTypes.zoomStart);
- }
- var scroller = this.scroll.scroller;
- var scrollBehaviorX = scroller.scrollBehaviorX, scrollBehaviorY = scroller.scrollBehaviorY;
- var x = this.getNewPos(this.origin.x, ratio, scrollBehaviorX, false, false);
- var y = this.getNewPos(this.origin.y, ratio, scrollBehaviorY, false, false);
- this.scroll.trigger(this.scroll.eventTypes.zooming, {
- scale: this.scale,
- });
- scroller.translater.translate({ x: x, y: y, scale: endScale });
- };
- Zoom.prototype.zoomEnd = function () {
- if (!this.zoomed)
- return;
- // if out of boundary, do rebound!
- if (this.shouldRebound()) {
- this._doZoomTo(this.scale, this.origin, this.zoomOpt.bounceTime);
- return;
- }
- this.scroll.trigger(this.scroll.eventTypes.zoomEnd, { scale: this.scale });
- };
- Zoom.prototype.getFingerDistance = function (e) {
- var firstFinger = e.touches[0];
- var secondFinger = e.touches[1];
- var deltaX = Math.abs(firstFinger.pageX - secondFinger.pageX);
- var deltaY = Math.abs(firstFinger.pageY - secondFinger.pageY);
- return getDistance(deltaX, deltaY);
- };
- Zoom.prototype.shouldRebound = function () {
- var _a = this.zoomOpt, min = _a.min, max = _a.max;
- var currentScale = this.scale;
- // scale exceeded!
- if (currentScale !== between(currentScale, min, max)) {
- return true;
- }
- var _b = this.scroll.scroller, scrollBehaviorX = _b.scrollBehaviorX, scrollBehaviorY = _b.scrollBehaviorY;
- // enlarge boundaries manually when zoom is end
- this.resetBoundaries([scrollBehaviorX, scrollBehaviorY]);
- var xInBoundary = scrollBehaviorX.checkInBoundary().inBoundary;
- var yInBoundary = scrollBehaviorX.checkInBoundary().inBoundary;
- return !(xInBoundary && yInBoundary);
- };
- Zoom.prototype.dampingScale = function (scale) {
- var _a = this.zoomOpt, min = _a.min, max = _a.max;
- if (scale < min) {
- scale = 0.5 * min * Math.pow(2.0, scale / min);
- }
- else if (scale > max) {
- scale = 2.0 * max * Math.pow(0.5, max / scale);
- }
- return scale;
- };
- Zoom.prototype.setScale = function (scale) {
- this.scale = scale;
- };
- Zoom.prototype.resetBoundaries = function (scrollBehaviorPairs) {
- scrollBehaviorPairs.forEach(function (behavior) { return behavior.computeBoundary(); });
- };
- Zoom.prototype.getNewPos = function (origin, lastScale, scrollBehavior, shouldInBoundary, useCurrentPos) {
- if (useCurrentPos === void 0) { useCurrentPos = false; }
- var newPos = origin -
- origin * lastScale +
- (useCurrentPos ? scrollBehavior.currentPos : scrollBehavior.startPos);
- if (shouldInBoundary) {
- newPos = between(newPos, scrollBehavior.maxScrollPos, scrollBehavior.minScrollPos);
- }
- // maxScrollPos or minScrollPos maybe a negative or positive digital
- return newPos > 0 ? Math.floor(newPos) : Math.ceil(newPos);
- };
- Zoom.prototype.registerHooks = function (hooks, name, handler) {
- hooks.on(name, handler, this);
- this.hooksFn.push([hooks, name, handler]);
- };
- Zoom.prototype.destroy = function () {
- this.hooksFn.forEach(function (item) {
- var hooks = item[0];
- var hooksName = item[1];
- var handlerFn = item[2];
- hooks.off(hooksName, handlerFn);
- });
- this.hooksFn.length = 0;
- };
- Zoom.pluginName = 'zoom';
- return Zoom;
- }());
- export default Zoom;
|