123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- (function($, window) {
- var CLASS_ZOOM = $.className('zoom');
- var CLASS_ZOOM_SCROLLER = $.className('zoom-scroller');
- var SELECTOR_ZOOM = '.' + CLASS_ZOOM;
- var SELECTOR_ZOOM_SCROLLER = '.' + CLASS_ZOOM_SCROLLER;
- var EVENT_PINCH_START = 'pinchstart';
- var EVENT_PINCH = 'pinch';
- var EVENT_PINCH_END = 'pinchend';
- if ('ongesturestart' in window) {
- EVENT_PINCH_START = 'gesturestart';
- EVENT_PINCH = 'gesturechange';
- EVENT_PINCH_END = 'gestureend';
- }
- $.Zoom = function(element, options) {
- var zoom = this;
- zoom.options = $.extend($.Zoom.defaults, options);
- zoom.wrapper = zoom.element = element;
- zoom.scroller = element.querySelector(SELECTOR_ZOOM_SCROLLER);
- zoom.scrollerStyle = zoom.scroller && zoom.scroller.style;
- zoom.zoomer = element.querySelector(SELECTOR_ZOOM);
- zoom.zoomerStyle = zoom.zoomer && zoom.zoomer.style;
- zoom.init = function() {
- //自动启用
- $.options.gestureConfig.pinch = true;
- $.options.gestureConfig.doubletap = true;
- zoom.initEvents();
- };
- zoom.initEvents = function(detach) {
- var action = detach ? 'removeEventListener' : 'addEventListener';
- var target = zoom.scroller;
- target[action](EVENT_PINCH_START, zoom.onPinchstart);
- target[action](EVENT_PINCH, zoom.onPinch);
- target[action](EVENT_PINCH_END, zoom.onPinchend);
- target[action]('touchstart', zoom.onTouchstart);
- target[action]('touchmove', zoom.onTouchMove);
- target[action]('touchcancel', zoom.onTouchEnd);
- target[action]('touchend', zoom.onTouchEnd);
- target[action]('drag', function(e) {
- if (imageIsMoved || isGesturing) {
- e.stopPropagation();
- }
- });
- target[action]('doubletap', function(e) {
- zoom.toggleZoom(e.detail.center);
- });
- };
- zoom.transition = function(style, time) {
- time = time || 0;
- style['webkitTransitionDuration'] = time + 'ms';
- return zoom;
- };
- zoom.translate = function(style, x, y) {
- x = x || 0;
- y = y || 0;
- style['webkitTransform'] = 'translate3d(' + x + 'px,' + y + 'px,0px)';
- return zoom;
- };
- zoom.scale = function(style, scale) {
- scale = scale || 1;
- style['webkitTransform'] = 'translate3d(0,0,0) scale(' + scale + ')';
- return zoom;
- };
- zoom.scrollerTransition = function(time) {
- return zoom.transition(zoom.scrollerStyle, time);
- };
- zoom.scrollerTransform = function(x, y) {
- return zoom.translate(zoom.scrollerStyle, x, y);
- };
- zoom.zoomerTransition = function(time) {
- return zoom.transition(zoom.zoomerStyle, time);
- };
- zoom.zoomerTransform = function(scale) {
- return zoom.scale(zoom.zoomerStyle, scale);
- };
- // Gestures
- var scale = 1,
- currentScale = 1,
- isScaling = false,
- isGesturing = false;
- zoom.onPinchstart = function(e) {
- isGesturing = true;
- };
- zoom.onPinch = function(e) {
- if (!isScaling) {
- zoom.zoomerTransition(0);
- isScaling = true;
- }
- scale = (e.detail ? e.detail.scale : e.scale) * currentScale;
- if (scale > zoom.options.maxZoom) {
- scale = zoom.options.maxZoom - 1 + Math.pow((scale - zoom.options.maxZoom + 1), 0.5);
- }
- if (scale < zoom.options.minZoom) {
- scale = zoom.options.minZoom + 1 - Math.pow((zoom.options.minZoom - scale + 1), 0.5);
- }
- zoom.zoomerTransform(scale);
- };
- zoom.onPinchend = function(e) {
- scale = Math.max(Math.min(scale, zoom.options.maxZoom), zoom.options.minZoom);
- zoom.zoomerTransition(zoom.options.speed).zoomerTransform(scale);
- currentScale = scale;
- isScaling = false;
- };
- zoom.setZoom = function(newScale) {
- scale = currentScale = newScale;
- zoom.scrollerTransition(zoom.options.speed).scrollerTransform(0, 0);
- zoom.zoomerTransition(zoom.options.speed).zoomerTransform(scale);
- };
- zoom.toggleZoom = function(position, speed) {
- if (typeof position === 'number') {
- speed = position;
- position = undefined;
- }
- speed = typeof speed === 'undefined' ? zoom.options.speed : speed;
- if (scale && scale !== 1) {
- scale = currentScale = 1;
- zoom.scrollerTransition(speed).scrollerTransform(0, 0);
- } else {
- scale = currentScale = zoom.options.maxZoom;
- if (position) {
- var offset = $.offset(zoom.zoomer);
- var top = offset.top;
- var left = offset.left;
- var offsetX = (position.x - left) * scale;
- var offsetY = (position.y - top) * scale;
- this._cal();
- if (offsetX >= imageMaxX && offsetX <= (imageMaxX + wrapperWidth)) { //center
- offsetX = imageMaxX - offsetX + wrapperWidth / 2;
- } else if (offsetX < imageMaxX) { //left
- offsetX = imageMaxX - offsetX + wrapperWidth / 2;
- } else if (offsetX > (imageMaxX + wrapperWidth)) { //right
- offsetX = imageMaxX + wrapperWidth - offsetX - wrapperWidth / 2;
- }
- if (offsetY >= imageMaxY && offsetY <= (imageMaxY + wrapperHeight)) { //middle
- offsetY = imageMaxY - offsetY + wrapperHeight / 2;
- } else if (offsetY < imageMaxY) { //top
- offsetY = imageMaxY - offsetY + wrapperHeight / 2;
- } else if (offsetY > (imageMaxY + wrapperHeight)) { //bottom
- offsetY = imageMaxY + wrapperHeight - offsetY - wrapperHeight / 2;
- }
- offsetX = Math.min(Math.max(offsetX, imageMinX), imageMaxX);
- offsetY = Math.min(Math.max(offsetY, imageMinY), imageMaxY);
- zoom.scrollerTransition(speed).scrollerTransform(offsetX, offsetY);
- } else {
- zoom.scrollerTransition(speed).scrollerTransform(0, 0);
- }
- }
- zoom.zoomerTransition(speed).zoomerTransform(scale);
- };
- zoom._cal = function() {
- wrapperWidth = zoom.wrapper.offsetWidth;
- wrapperHeight = zoom.wrapper.offsetHeight;
- imageWidth = zoom.zoomer.offsetWidth;
- imageHeight = zoom.zoomer.offsetHeight;
- var scaledWidth = imageWidth * scale;
- var scaledHeight = imageHeight * scale;
- imageMinX = Math.min((wrapperWidth / 2 - scaledWidth / 2), 0);
- imageMaxX = -imageMinX;
- imageMinY = Math.min((wrapperHeight / 2 - scaledHeight / 2), 0);
- imageMaxY = -imageMinY;
- };
- var wrapperWidth, wrapperHeight, imageIsTouched, imageIsMoved, imageCurrentX, imageCurrentY, imageMinX, imageMinY, imageMaxX, imageMaxY, imageWidth, imageHeight, imageTouchesStart = {},
- imageTouchesCurrent = {},
- imageStartX, imageStartY, velocityPrevPositionX, velocityPrevTime, velocityX, velocityPrevPositionY, velocityY;
- zoom.onTouchstart = function(e) {
- e.preventDefault();
- imageIsTouched = true;
- imageTouchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
- imageTouchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
- };
- zoom.onTouchMove = function(e) {
- e.preventDefault();
- if (!imageIsTouched) return;
- if (!imageIsMoved) {
- wrapperWidth = zoom.wrapper.offsetWidth;
- wrapperHeight = zoom.wrapper.offsetHeight;
- imageWidth = zoom.zoomer.offsetWidth;
- imageHeight = zoom.zoomer.offsetHeight;
- var translate = $.parseTranslateMatrix($.getStyles(zoom.scroller, 'webkitTransform'));
- imageStartX = translate.x || 0;
- imageStartY = translate.y || 0;
- zoom.scrollerTransition(0);
- }
- var scaledWidth = imageWidth * scale;
- var scaledHeight = imageHeight * scale;
- if (scaledWidth < wrapperWidth && scaledHeight < wrapperHeight) return;
- imageMinX = Math.min((wrapperWidth / 2 - scaledWidth / 2), 0);
- imageMaxX = -imageMinX;
- imageMinY = Math.min((wrapperHeight / 2 - scaledHeight / 2), 0);
- imageMaxY = -imageMinY;
- imageTouchesCurrent.x = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
- imageTouchesCurrent.y = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;
- if (!imageIsMoved && !isScaling) {
- // if (Math.abs(imageTouchesCurrent.y - imageTouchesStart.y) < Math.abs(imageTouchesCurrent.x - imageTouchesStart.x)) {
- //TODO 此处需要优化,当遇到长图,需要上下滚动时,下列判断会导致滚动不流畅
- if (
- (Math.floor(imageMinX) === Math.floor(imageStartX) && imageTouchesCurrent.x < imageTouchesStart.x) ||
- (Math.floor(imageMaxX) === Math.floor(imageStartX) && imageTouchesCurrent.x > imageTouchesStart.x)
- ) {
- imageIsTouched = false;
- return;
- }
- // }
- }
- imageIsMoved = true;
- imageCurrentX = imageTouchesCurrent.x - imageTouchesStart.x + imageStartX;
- imageCurrentY = imageTouchesCurrent.y - imageTouchesStart.y + imageStartY;
- if (imageCurrentX < imageMinX) {
- imageCurrentX = imageMinX + 1 - Math.pow((imageMinX - imageCurrentX + 1), 0.8);
- }
- if (imageCurrentX > imageMaxX) {
- imageCurrentX = imageMaxX - 1 + Math.pow((imageCurrentX - imageMaxX + 1), 0.8);
- }
- if (imageCurrentY < imageMinY) {
- imageCurrentY = imageMinY + 1 - Math.pow((imageMinY - imageCurrentY + 1), 0.8);
- }
- if (imageCurrentY > imageMaxY) {
- imageCurrentY = imageMaxY - 1 + Math.pow((imageCurrentY - imageMaxY + 1), 0.8);
- }
- //Velocity
- if (!velocityPrevPositionX) velocityPrevPositionX = imageTouchesCurrent.x;
- if (!velocityPrevPositionY) velocityPrevPositionY = imageTouchesCurrent.y;
- if (!velocityPrevTime) velocityPrevTime = $.now();
- velocityX = (imageTouchesCurrent.x - velocityPrevPositionX) / ($.now() - velocityPrevTime) / 2;
- velocityY = (imageTouchesCurrent.y - velocityPrevPositionY) / ($.now() - velocityPrevTime) / 2;
- if (Math.abs(imageTouchesCurrent.x - velocityPrevPositionX) < 2) velocityX = 0;
- if (Math.abs(imageTouchesCurrent.y - velocityPrevPositionY) < 2) velocityY = 0;
- velocityPrevPositionX = imageTouchesCurrent.x;
- velocityPrevPositionY = imageTouchesCurrent.y;
- velocityPrevTime = $.now();
- zoom.scrollerTransform(imageCurrentX, imageCurrentY);
- };
- zoom.onTouchEnd = function(e) {
- if (!e.touches.length) {
- isGesturing = false;
- }
- if (!imageIsTouched || !imageIsMoved) {
- imageIsTouched = false;
- imageIsMoved = false;
- return;
- }
- imageIsTouched = false;
- imageIsMoved = false;
- var momentumDurationX = 300;
- var momentumDurationY = 300;
- var momentumDistanceX = velocityX * momentumDurationX;
- var newPositionX = imageCurrentX + momentumDistanceX;
- var momentumDistanceY = velocityY * momentumDurationY;
- var newPositionY = imageCurrentY + momentumDistanceY;
- if (velocityX !== 0) momentumDurationX = Math.abs((newPositionX - imageCurrentX) / velocityX);
- if (velocityY !== 0) momentumDurationY = Math.abs((newPositionY - imageCurrentY) / velocityY);
- var momentumDuration = Math.max(momentumDurationX, momentumDurationY);
- imageCurrentX = newPositionX;
- imageCurrentY = newPositionY;
- var scaledWidth = imageWidth * scale;
- var scaledHeight = imageHeight * scale;
- imageMinX = Math.min((wrapperWidth / 2 - scaledWidth / 2), 0);
- imageMaxX = -imageMinX;
- imageMinY = Math.min((wrapperHeight / 2 - scaledHeight / 2), 0);
- imageMaxY = -imageMinY;
- imageCurrentX = Math.max(Math.min(imageCurrentX, imageMaxX), imageMinX);
- imageCurrentY = Math.max(Math.min(imageCurrentY, imageMaxY), imageMinY);
- zoom.scrollerTransition(momentumDuration).scrollerTransform(imageCurrentX, imageCurrentY);
- };
- zoom.destory = function() {
- zoom.initEvents(true); //detach
- delete $.data[zoom.wrapper.getAttribute('data-zoomer')];
- zoom.wrapper.setAttribute('data-zoomer', '');
- }
- zoom.init();
- return zoom;
- };
- $.Zoom.defaults = {
- speed: 300,
- maxZoom: 3,
- minZoom: 1,
- };
- $.fn.zoom = function(options) {
- var zoomApis = [];
- this.each(function() {
- var zoomApi = null;
- var self = this;
- var id = self.getAttribute('data-zoomer');
- if (!id) {
- id = ++$.uuid;
- $.data[id] = zoomApi = new $.Zoom(self, options);
- self.setAttribute('data-zoomer', id);
- } else {
- zoomApi = $.data[id];
- }
- zoomApis.push(zoomApi);
- });
- return zoomApis.length === 1 ? zoomApis[0] : zoomApis;
- };
- })(mui, window);
|