gushidianzibao-h5/public/static/js/pinchzoom.min.js
2024-12-26 14:18:10 +08:00

504 lines
15 KiB
JavaScript

(function() {
"use strict";
var definePinchZoom = function($) {
var PinchZoom = function(el, options) {
this.el = $(el);
this.zoomFactor = 1;
this.lastScale = 1;
this.offset = {
x: 0,
y: 0
};
this.options = $.extend({}, this.defaults, options);
this.setupMarkup();
this.bindEvents();
this.update();
this.enable()
},
sum = function(a, b) {
return a + b
},
isCloseTo = function(value, expected) {
return value > expected - .01 && value < expected + .01
};
PinchZoom.prototype = {
defaults: {
tapZoomFactor: 2,
zoomOutFactor: 1.3,
animationDuration: 300,
animationInterval: 5,
maxZoom: 4,
minZoom: .5,
lockDragAxis: false,
use2d: true,
zoomStartEventName: "pz_zoomstart",
zoomEndEventName: "pz_zoomend",
dragStartEventName: "pz_dragstart",
dragEndEventName: "pz_dragend",
doubleTapEventName: "pz_doubletap"
},
handleDragStart: function(event) {
this.el.trigger(this.options.dragStartEventName);
this.stopAnimation();
this.lastDragPosition = false;
this.hasInteraction = true;
this.handleDrag(event)
},
handleDrag: function(event) {
if (this.zoomFactor > 1) {
var touch = this.getTouches(event)[0];
this.drag(touch, this.lastDragPosition);
this.offset = this.sanitizeOffset(this.offset);
this.lastDragPosition = touch
}
},
handleDragEnd: function() {
this.el.trigger(this.options.dragEndEventName);
this.end()
},
handleZoomStart: function(event) {
this.el.trigger(this.options.zoomStartEventName);
this.stopAnimation();
this.lastScale = 1;
this.nthZoom = 0;
this.lastZoomCenter = false;
this.hasInteraction = true
},
handleZoom: function(event, newScale) {
var touchCenter = this.getTouchCenter(this.getTouches(event)),
scale = newScale / this.lastScale;
this.lastScale = newScale;
this.nthZoom += 1;
if (this.nthZoom > 3) {
this.scale(scale, touchCenter);
this.drag(touchCenter, this.lastZoomCenter)
}
this.lastZoomCenter = touchCenter
},
handleZoomEnd: function() {
this.el.trigger(this.options.zoomEndEventName);
this.end()
},
handleDoubleTap: function(event) {
var center = this.getTouches(event)[0],
zoomFactor = this.zoomFactor > 1 ? 1 : this.options.tapZoomFactor,
startZoomFactor = this.zoomFactor,
updateProgress = function(progress) {
this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center)
}.bind(this);
if (this.hasInteraction) {
return
}
if (startZoomFactor > zoomFactor) {
center = this.getCurrentZoomCenter()
}
this.animate(this.options.animationDuration, this.options.animationInterval, updateProgress, this.swing);
this.el.trigger(this.options.doubleTapEventName)
},
sanitizeOffset: function(offset) {
var maxX = (this.zoomFactor - 1) * this.getContainerX(),
maxY = (this.zoomFactor - 1) * this.getContainerY(),
maxOffsetX = Math.max(maxX, 0),
maxOffsetY = Math.max(maxY, 0),
minOffsetX = Math.min(maxX, 0),
minOffsetY = Math.min(maxY, 0);
return {
x: Math.min(Math.max(offset.x, minOffsetX), maxOffsetX),
y: Math.min(Math.max(offset.y, minOffsetY), maxOffsetY)
}
},
scaleTo: function(zoomFactor, center) {
this.scale(zoomFactor / this.zoomFactor, center)
},
scale: function(scale, center) {
scale = this.scaleZoomFactor(scale);
this.addOffset({
x: (scale - 1) * (center.x + this.offset.x),
y: (scale - 1) * (center.y + this.offset.y)
})
},
scaleZoomFactor: function(scale) {
var originalZoomFactor = this.zoomFactor;
this.zoomFactor *= scale;
this.zoomFactor = Math.min(this.options.maxZoom, Math.max(this.zoomFactor, this.options.minZoom));
return this.zoomFactor / originalZoomFactor
},
drag: function(center, lastCenter) {
if (lastCenter) {
if (this.options.lockDragAxis) {
if (Math.abs(center.x - lastCenter.x) > Math.abs(center.y - lastCenter.y)) {
this.addOffset({
x: -(center.x - lastCenter.x),
y: 0
})
} else {
this.addOffset({
y: -(center.y - lastCenter.y),
x: 0
})
}
} else {
this.addOffset({
y: -(center.y - lastCenter.y),
x: -(center.x - lastCenter.x)
})
}
}
},
getTouchCenter: function(touches) {
return this.getVectorAvg(touches)
},
getVectorAvg: function(vectors) {
return {
x: vectors.map(function(v) {
return v.x
}).reduce(sum) / vectors.length,
y: vectors.map(function(v) {
return v.y
}).reduce(sum) / vectors.length
}
},
addOffset: function(offset) {
this.offset = {
x: this.offset.x + offset.x,
y: this.offset.y + offset.y
}
},
sanitize: function() {
if (this.zoomFactor < this.options.zoomOutFactor) {
this.zoomOutAnimation()
} else if (this.isInsaneOffset(this.offset)) {
this.sanitizeOffsetAnimation()
}
},
isInsaneOffset: function(offset) {
var sanitizedOffset = this.sanitizeOffset(offset);
return sanitizedOffset.x !== offset.x || sanitizedOffset.y !== offset.y
},
sanitizeOffsetAnimation: function() {
var targetOffset = this.sanitizeOffset(this.offset),
startOffset = {
x: this.offset.x,
y: this.offset.y
},
updateProgress = function(progress) {
this.offset.x = startOffset.x + progress * (targetOffset.x - startOffset.x);
this.offset.y = startOffset.y + progress * (targetOffset.y - startOffset.y);
this.update()
}.bind(this);
this.animate(this.options.animationDuration, this.options.animationInterval, updateProgress, this.swing)
},
zoomOutAnimation: function() {
var startZoomFactor = this.zoomFactor,
zoomFactor = 1,
center = this.getCurrentZoomCenter(),
updateProgress = function(progress) {
this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center)
}.bind(this);
this.animate(this.options.animationDuration, this.options.animationInterval, updateProgress, this.swing)
},
updateAspectRatio: function() {
this.setContainerY(this.getContainerX() / this.getAspectRatio())
},
getInitialZoomFactor: function() {
return this.container[0].offsetWidth / this.el[0].offsetWidth
},
getAspectRatio: function() {
return this.el[0].offsetWidth / this.el[0].offsetHeight
},
getCurrentZoomCenter: function() {
var length = this.container[0].offsetWidth * this.zoomFactor,
offsetLeft = this.offset.x,
offsetRight = length - offsetLeft - this.container[0].offsetWidth,
widthOffsetRatio = offsetLeft / offsetRight,
centerX = widthOffsetRatio * this.container[0].offsetWidth / (widthOffsetRatio + 1),
height = this.container[0].offsetHeight * this.zoomFactor,
offsetTop = this.offset.y,
offsetBottom = height - offsetTop - this.container[0].offsetHeight,
heightOffsetRatio = offsetTop / offsetBottom,
centerY = heightOffsetRatio * this.container[0].offsetHeight / (heightOffsetRatio + 1);
if (offsetRight === 0) {
centerX = this.container[0].offsetWidth
}
if (offsetBottom === 0) {
centerY = this.container[0].offsetHeight
}
return {
x: centerX,
y: centerY
}
},
canDrag: function() {
return !isCloseTo(this.zoomFactor, 1)
},
getTouches: function(event) {
var position = this.container.offset();
return Array.prototype.slice.call(event.touches).map(function(touch) {
return {
x: touch.pageX - position.left,
y: touch.pageY - position.top
}
})
},
animate: function(duration, interval, framefn, timefn, callback) {
var startTime = (new Date).getTime(),
renderFrame = function() {
if (!this.inAnimation) {
return
}
var frameTime = (new Date).getTime() - startTime,
progress = frameTime / duration;
if (frameTime >= duration) {
framefn(1);
if (callback) {
callback()
}
this.update();
this.stopAnimation();
this.update()
} else {
if (timefn) {
progress = timefn(progress)
}
framefn(progress);
this.update();
setTimeout(renderFrame, interval)
}
}.bind(this);
this.inAnimation = true;
renderFrame()
},
stopAnimation: function() {
this.inAnimation = false
},
swing: function(p) {
return -Math.cos(p * Math.PI) / 2 + .5
},
getContainerX: function() {
return this.container[0].offsetWidth
},
getContainerY: function() {
return this.container[0].offsetHeight
},
setContainerY: function(y) {
return this.container.height(y)
},
setupMarkup: function() {
this.container = $('<div class="pinch-zoom-container"></div>');
this.el.before(this.container);
this.container.append(this.el);
this.container.css({
overflow: "hidden",
//position: "relative"
position:"absolute",
top:"0",
});
this.el.css({
"-webkit-transform-origin": "0% 0%",
"-moz-transform-origin": "0% 0%",
"-ms-transform-origin": "0% 0%",
"-o-transform-origin": "0% 0%",
"transform-origin": "0% 0%",
position: "absolute"
})
},
end: function() {
this.hasInteraction = false;
this.sanitize();
this.update()
},
bindEvents: function() {
detectGestures(this.container.get(0), this);
$(window).on("resize", this.update.bind(this));
$(this.el).find("img").on("load", this.update.bind(this))
},
update: function() {
if (this.updatePlaned) {
return
}
this.updatePlaned = true;
setTimeout(function() {
this.updatePlaned = false;
this.updateAspectRatio();
let Scwidth = this.el[0].clientWidth/this.el.find('img')[0].naturalWidth;
let ScHeight = this.el[0].clientHeight / this.el.find('img')[0].naturalHeight;
var zoomFactor = this.getInitialZoomFactor() * this.zoomFactor,
offsetX = -this.offset.x / zoomFactor,
offsetY = -this.offset.y / zoomFactor,
transform3d = "scale3d(" + zoomFactor + ", " + zoomFactor + ",1) " + "translate3d(" + offsetX + "px," + offsetY + "px,0px)",
transform2d = "scale(" + zoomFactor + ", " + zoomFactor + ") " + "translate(" + offsetX + "px," + offsetY + "px)",
removeClone = function() {
if (this.clone) {
this.clone.remove();
delete this.clone
}
}.bind(this);
if (!this.options.use2d || this.hasInteraction || this.inAnimation) {
this.is3d = true;
removeClone();
this.el.css({
"-webkit-transform": transform3d,
"-o-transform": transform2d,
"-ms-transform": transform2d,
"-moz-transform": transform2d,
transform: transform3d
})
} else {
if (this.is3d) {
this.clone = this.el.clone();
this.clone.css("pointer-events", "none");
this.clone.appendTo(this.container);
setTimeout(removeClone, 200)
}
this.el.css({
"-webkit-transform": transform2d,
"-o-transform": transform2d,
"-ms-transform": transform2d,
"-moz-transform": transform2d,
transform: transform2d
});
this.is3d = false
}
this.el.find('.paperMap').css('opacity','1');
this.el.parents('.imgBoxCont').find('.pichmask').css('opacity','0');
}.bind(this), 0)
},
enable: function() {
this.enabled = true
},
disable: function() {
this.enabled = false
}
};
var detectGestures = function(el, target) {
var interaction = null,
fingers = 0,
lastTouchStart = null,
startTouches = null,
setInteraction = function(newInteraction, event) {
if (interaction !== newInteraction) {
if (interaction && !newInteraction) {
switch (interaction) {
case "zoom":
target.handleZoomEnd(event);
break;
case "drag":
target.handleDragEnd(event);
break
}
}
switch (newInteraction) {
case "zoom":
target.handleZoomStart(event);
break;
case "drag":
target.handleDragStart(event);
break
}
}
interaction = newInteraction
},
updateInteraction = function(event) {
if (fingers === 2) {
setInteraction("zoom")
} else if (fingers === 1 && target.canDrag()) {
setInteraction("drag", event)
} else {
setInteraction(null, event)
}
},
targetTouches = function(touches) {
return Array.prototype.slice.call(touches).map(function(touch) {
return {
x: touch.pageX,
y: touch.pageY
}
})
},
getDistance = function(a, b) {
var x, y;
x = a.x - b.x;
y = a.y - b.y;
return Math.sqrt(x * x + y * y)
},
calculateScale = function(startTouches, endTouches) {
var startDistance = getDistance(startTouches[0], startTouches[1]),
endDistance = getDistance(endTouches[0], endTouches[1]);
return endDistance / startDistance
},
cancelEvent = function(event) {
event.stopPropagation();
event.preventDefault()
},
detectDoubleTap = function(event) {
var time = (new Date).getTime();
if (fingers > 1) {
lastTouchStart = null
}
if (time - lastTouchStart < 300) {
cancelEvent(event);
target.handleDoubleTap(event);
switch (interaction) {
case "zoom":
target.handleZoomEnd(event);
break;
case "drag":
target.handleDragEnd(event);
break
}
}
if (fingers === 1) {
lastTouchStart = time
}
},
firstMove = true;
el.addEventListener("touchstart", function(event) {
if (target.enabled) {
firstMove = true;
fingers = event.touches.length;
detectDoubleTap(event)
}
});
el.addEventListener("touchmove", function(event) {
if (target.enabled) {
if (firstMove) {
updateInteraction(event);
if (interaction) {
cancelEvent(event)
}
startTouches = targetTouches(event.touches)
} else {
switch (interaction) {
case "zoom":
target.handleZoom(event, calculateScale(startTouches, targetTouches(event.touches)));
break;
case "drag":
target.handleDrag(event);
break
}
if (interaction) {
cancelEvent(event);
target.update()
}
}
firstMove = false
}
});
el.addEventListener("touchend", function(event) {
if (target.enabled) {
fingers = event.touches.length;
updateInteraction(event)
}
})
};
return PinchZoom
};
if (typeof define !== "undefined" && define.amd) {
define(["jquery"], function($) {
return definePinchZoom($)
})
} else {
window.RTP = window.RTP || {};
window.RTP.PinchZoom = definePinchZoom(window.$)
}
}).call(this);