Javascript 检测捏合的最简单方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11183174/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Simplest way to detect a pinch
提问by Fresheyeball
This is a WEB APPnot a native app. Please no Objective-C NS commands.
这是一个WEB应用程序,而不是本机应用程序。请不要使用 Objective-C NS 命令。
So I need to detect 'pinch' events on iOS. Problem is every plugin or method I see for doing gestures or multi-touch events, is (usually) with jQuery and is a whole additional pluggin for every gesture under the sun. My application is huge, and I am very sensitive to deadwood in my code. All I need is to detect a pinch, and using something like jGesture is just way to bloated for my simple needs.
所以我需要在 iOS 上检测“捏”事件。问题是我看到的用于执行手势或多点触控事件的每个插件或方法(通常)都使用 jQuery,并且是阳光下每个手势的完整附加插件。我的应用程序很大,而且我对代码中的死木非常敏感。我所需要的只是检测捏合,使用 jGesture 之类的东西只是为了满足我的简单需求而膨胀。
Additionally, I have a limited understanding of how to detect a pinch manually. I can get the position of both fingers, can't seem to get the mix right to detect this. Does anyone have a simple snippet that JUST detects pinch?
此外,我对如何手动检测捏合的了解有限。我可以得到两个手指的位置,似乎无法正确混合以检测到这一点。有没有人有一个简单的片段来检测捏合?
采纳答案by Dan Herbert
You want to use the gesturestart
, gesturechange
, and gestureend
events. These get triggered any time 2 or more fingers touch the screen.
你想用的gesturestart
,gesturechange
和gestureend
事件。每当 2 个或更多手指触摸屏幕时,这些就会被触发。
Depending on what you need to do with the pinch gesture, your approach will need to be adjusted. The scale
multiplier can be examined to determine how dramatic the user's pinch gesture was. See Apple's TouchEvent documentationfor details about how the scale
property will behave.
根据您需要对捏合手势执行的操作,您的方法需要进行调整。该scale
乘法器可以进行检查,以确定用户的缩放手势如何戏剧性了。请参阅Apple 的 TouchEvent 文档,了解有关该scale
属性如何表现的详细信息。
node.addEventListener('gestureend', function(e) {
if (e.scale < 1.0) {
// User moved fingers closer together
} else if (e.scale > 1.0) {
// User moved fingers further apart
}
}, false);
You could also intercept the gesturechange
event to detect a pinch as it happens if you need it to make your app feel more responsive.
gesturechange
如果您需要它使您的应用程序感觉更灵敏,您还可以拦截该事件以检测发生的捏合。
回答by Jeffrey Sweeney
Think about what a pinch
event is: two fingers on an element, moving toward or away from each other.
Gesture events are, to my knowledge, a fairly new standard, so probably the safest way to go about this is to use touch events like so:
想想什么是pinch
事件:两个手指在一个元素上,彼此靠近或远离。据我所知,手势事件是一个相当新的标准,所以可能最安全的方法是使用这样的触摸事件:
(ontouchstart
event)
(ontouchstart
事件)
if (e.touches.length === 2) {
scaling = true;
pinchStart(e);
}
(ontouchmove
event)
(ontouchmove
事件)
if (scaling) {
pinchMove(e);
}
(ontouchend
event)
(ontouchend
事件)
if (scaling) {
pinchEnd(e);
scaling = false;
}
To get the distance between the two fingers, use the hypot
function:
要获得两个手指之间的距离,请使用以下hypot
函数:
var dist = Math.hypot(
e.touches[0].pageX - e.touches[1].pageX,
e.touches[0].pageY - e.touches[1].pageY);
回答by Bruno
Hammer.js all the way! It handles "transforms" (pinches). http://eightmedia.github.com/hammer.js/
Hammer.js 一路走好!它处理“转换”(捏)。 http://eightmedia.github.com/hammer.js/
But if you wish to implement it youself, i think that Jeffrey's answer is pretty solid.
但是如果你想自己实现它,我认为杰弗里的答案非常可靠。
回答by redgeoff
Unfortunately, detecting pinch gestures across browsers is a not as simple as one would hope, but HammerJS makes it a lot easier!
不幸的是,跨浏览器检测捏合手势并不像人们希望的那么简单,但是 HammerJS 让它变得更容易!
Check out the Pinch Zoom and Pan with HammerJS demo. This example has been tested on Android, iOS and Windows Phone.
查看带有 HammerJS的捏缩放和平移演示。此示例已在 Android、iOS 和 Windows Phone 上进行了测试。
You can find the source code at Pinch Zoom and Pan with HammerJS.
您可以在Pinch Zoom and Pan with HammerJS 中找到源代码。
For your convenience, here is the source code:
为了您的方便,这里是源代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<title>Pinch Zoom</title>
</head>
<body>
<div>
<div style="height:150px;background-color:#eeeeee">
Ignore this area. Space is needed to test on the iPhone simulator as pinch simulation on the
iPhone simulator requires the target to be near the middle of the screen and we only respect
touch events in the image area. This space is not needed in production.
</div>
<style>
.pinch-zoom-container {
overflow: hidden;
height: 300px;
}
.pinch-zoom-image {
width: 100%;
}
</style>
<script src="https://hammerjs.github.io/dist/hammer.js"></script>
<script>
var MIN_SCALE = 1; // 1=scaling when first loaded
var MAX_SCALE = 64;
// HammerJS fires "pinch" and "pan" events that are cumulative in nature and not
// deltas. Therefore, we need to store the "last" values of scale, x and y so that we can
// adjust the UI accordingly. It isn't until the "pinchend" and "panend" events are received
// that we can set the "last" values.
// Our "raw" coordinates are not scaled. This allows us to only have to modify our stored
// coordinates when the UI is updated. It also simplifies our calculations as these
// coordinates are without respect to the current scale.
var imgWidth = null;
var imgHeight = null;
var viewportWidth = null;
var viewportHeight = null;
var scale = null;
var lastScale = null;
var container = null;
var img = null;
var x = 0;
var lastX = 0;
var y = 0;
var lastY = 0;
var pinchCenter = null;
// We need to disable the following event handlers so that the browser doesn't try to
// automatically handle our image drag gestures.
var disableImgEventHandlers = function () {
var events = ['onclick', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover',
'onmouseup', 'ondblclick', 'onfocus', 'onblur'];
events.forEach(function (event) {
img[event] = function () {
return false;
};
});
};
// Traverse the DOM to calculate the absolute position of an element
var absolutePosition = function (el) {
var x = 0,
y = 0;
while (el !== null) {
x += el.offsetLeft;
y += el.offsetTop;
el = el.offsetParent;
}
return { x: x, y: y };
};
var restrictScale = function (scale) {
if (scale < MIN_SCALE) {
scale = MIN_SCALE;
} else if (scale > MAX_SCALE) {
scale = MAX_SCALE;
}
return scale;
};
var restrictRawPos = function (pos, viewportDim, imgDim) {
if (pos < viewportDim/scale - imgDim) { // too far left/up?
pos = viewportDim/scale - imgDim;
} else if (pos > 0) { // too far right/down?
pos = 0;
}
return pos;
};
var updateLastPos = function (deltaX, deltaY) {
lastX = x;
lastY = y;
};
var translate = function (deltaX, deltaY) {
// We restrict to the min of the viewport width/height or current width/height as the
// current width/height may be smaller than the viewport width/height
var newX = restrictRawPos(lastX + deltaX/scale,
Math.min(viewportWidth, curWidth), imgWidth);
x = newX;
img.style.marginLeft = Math.ceil(newX*scale) + 'px';
var newY = restrictRawPos(lastY + deltaY/scale,
Math.min(viewportHeight, curHeight), imgHeight);
y = newY;
img.style.marginTop = Math.ceil(newY*scale) + 'px';
};
var zoom = function (scaleBy) {
scale = restrictScale(lastScale*scaleBy);
curWidth = imgWidth*scale;
curHeight = imgHeight*scale;
img.style.width = Math.ceil(curWidth) + 'px';
img.style.height = Math.ceil(curHeight) + 'px';
// Adjust margins to make sure that we aren't out of bounds
translate(0, 0);
};
var rawCenter = function (e) {
var pos = absolutePosition(container);
// We need to account for the scroll position
var scrollLeft = window.pageXOffset ? window.pageXOffset : document.body.scrollLeft;
var scrollTop = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;
var zoomX = -x + (e.center.x - pos.x + scrollLeft)/scale;
var zoomY = -y + (e.center.y - pos.y + scrollTop)/scale;
return { x: zoomX, y: zoomY };
};
var updateLastScale = function () {
lastScale = scale;
};
var zoomAround = function (scaleBy, rawZoomX, rawZoomY, doNotUpdateLast) {
// Zoom
zoom(scaleBy);
// New raw center of viewport
var rawCenterX = -x + Math.min(viewportWidth, curWidth)/2/scale;
var rawCenterY = -y + Math.min(viewportHeight, curHeight)/2/scale;
// Delta
var deltaX = (rawCenterX - rawZoomX)*scale;
var deltaY = (rawCenterY - rawZoomY)*scale;
// Translate back to zoom center
translate(deltaX, deltaY);
if (!doNotUpdateLast) {
updateLastScale();
updateLastPos();
}
};
var zoomCenter = function (scaleBy) {
// Center of viewport
var zoomX = -x + Math.min(viewportWidth, curWidth)/2/scale;
var zoomY = -y + Math.min(viewportHeight, curHeight)/2/scale;
zoomAround(scaleBy, zoomX, zoomY);
};
var zoomIn = function () {
zoomCenter(2);
};
var zoomOut = function () {
zoomCenter(1/2);
};
var onLoad = function () {
img = document.getElementById('pinch-zoom-image-id');
container = img.parentElement;
disableImgEventHandlers();
imgWidth = img.width;
imgHeight = img.height;
viewportWidth = img.offsetWidth;
scale = viewportWidth/imgWidth;
lastScale = scale;
viewportHeight = img.parentElement.offsetHeight;
curWidth = imgWidth*scale;
curHeight = imgHeight*scale;
var hammer = new Hammer(container, {
domEvents: true
});
hammer.get('pinch').set({
enable: true
});
hammer.on('pan', function (e) {
translate(e.deltaX, e.deltaY);
});
hammer.on('panend', function (e) {
updateLastPos();
});
hammer.on('pinch', function (e) {
// We only calculate the pinch center on the first pinch event as we want the center to
// stay consistent during the entire pinch
if (pinchCenter === null) {
pinchCenter = rawCenter(e);
var offsetX = pinchCenter.x*scale - (-x*scale + Math.min(viewportWidth, curWidth)/2);
var offsetY = pinchCenter.y*scale - (-y*scale + Math.min(viewportHeight, curHeight)/2);
pinchCenterOffset = { x: offsetX, y: offsetY };
}
// When the user pinch zooms, she/he expects the pinch center to remain in the same
// relative location of the screen. To achieve this, the raw zoom center is calculated by
// first storing the pinch center and the scaled offset to the current center of the
// image. The new scale is then used to calculate the zoom center. This has the effect of
// actually translating the zoom center on each pinch zoom event.
var newScale = restrictScale(scale*e.scale);
var zoomX = pinchCenter.x*newScale - pinchCenterOffset.x;
var zoomY = pinchCenter.y*newScale - pinchCenterOffset.y;
var zoomCenter = { x: zoomX/newScale, y: zoomY/newScale };
zoomAround(e.scale, zoomCenter.x, zoomCenter.y, true);
});
hammer.on('pinchend', function (e) {
updateLastScale();
updateLastPos();
pinchCenter = null;
});
hammer.on('doubletap', function (e) {
var c = rawCenter(e);
zoomAround(2, c.x, c.y);
});
};
</script>
<button onclick="zoomIn()">Zoom In</button>
<button onclick="zoomOut()">Zoom Out</button>
<div class="pinch-zoom-container">
<img id="pinch-zoom-image-id" class="pinch-zoom-image" onload="onLoad()"
src="https://hammerjs.github.io/assets/img/pano-1.jpg">
</div>
</div>
</body>
</html>
回答by Andrey
detect two fingers pinch zoom on any element, easy and w/o hassle with 3rd party libs like Hammer.js (beware, hammer has issues with scrolling!)
检测任何元素上的两个手指捏缩放,使用像 Hammer.js 这样的 3rd 方库轻松且没有麻烦(注意,锤子有滚动问题!)
function onScale(el, callback) {
let hypo = undefined;
el.addEventListener('touchmove', function(event) {
if (event.targetTouches.length === 2) {
let hypo1 = Math.hypot((event.targetTouches[0].pageX - event.targetTouches[1].pageX),
(event.targetTouches[0].pageY - event.targetTouches[1].pageY));
if (hypo === undefined) {
hypo = hypo1;
}
callback(hypo1/hypo);
}
}, false);
el.addEventListener('touchend', function(event) {
hypo = undefined;
}, false);
}
回答by gcdev
None of these answers achieved what I was looking for, so I wound up writing something myself. I wanted to pinch-zoom an image on my website using my MacBookPro trackpad. The following code (which requires jQuery) seems to work in Chrome and Edge, at least. Maybe this will be of use to someone else.
这些答案都没有达到我想要的,所以我自己写了一些东西。我想使用我的 MacBookPro 触控板在我的网站上捏缩放图像。以下代码(需要 jQuery)似乎至少在 Chrome 和 Edge 中有效。也许这对其他人有用。
function setupImageEnlargement(el)
{
// "el" represents the image element, such as the results of document.getElementByd('image-id')
var img = $(el);
$(window, 'html', 'body').bind('scroll touchmove mousewheel', function(e)
{
//TODO: need to limit this to when the mouse is over the image in question
//TODO: behavior not the same in Safari and FF, but seems to work in Edge and Chrome
if (typeof e.originalEvent != 'undefined' && e.originalEvent != null
&& e.originalEvent.wheelDelta != 'undefined' && e.originalEvent.wheelDelta != null)
{
e.preventDefault();
e.stopPropagation();
console.log(e);
if (e.originalEvent.wheelDelta > 0)
{
// zooming
var newW = 1.1 * parseFloat(img.width());
var newH = 1.1 * parseFloat(img.height());
if (newW < el.naturalWidth && newH < el.naturalHeight)
{
// Go ahead and zoom the image
//console.log('zooming the image');
img.css(
{
"width": newW + 'px',
"height": newH + 'px',
"max-width": newW + 'px',
"max-height": newH + 'px'
});
}
else
{
// Make image as big as it gets
//console.log('making it as big as it gets');
img.css(
{
"width": el.naturalWidth + 'px',
"height": el.naturalHeight + 'px',
"max-width": el.naturalWidth + 'px',
"max-height": el.naturalHeight + 'px'
});
}
}
else if (e.originalEvent.wheelDelta < 0)
{
// shrinking
var newW = 0.9 * parseFloat(img.width());
var newH = 0.9 * parseFloat(img.height());
//TODO: I had added these data-attributes to the image onload.
// They represent the original width and height of the image on the screen.
// If your image is normally 100% width, you may need to change these values on resize.
var origW = parseFloat(img.attr('data-startwidth'));
var origH = parseFloat(img.attr('data-startheight'));
if (newW > origW && newH > origH)
{
// Go ahead and shrink the image
//console.log('shrinking the image');
img.css(
{
"width": newW + 'px',
"height": newH + 'px',
"max-width": newW + 'px',
"max-height": newH + 'px'
});
}
else
{
// Make image as small as it gets
//console.log('making it as small as it gets');
// This restores the image to its original size. You may want
//to do this differently, like by removing the css instead of defining it.
img.css(
{
"width": origW + 'px',
"height": origH + 'px',
"max-width": origW + 'px',
"max-height": origH + 'px'
});
}
}
}
});
}