3 changed files with 156 additions and 243 deletions
@ -0,0 +1,151 @@ |
|||||||
|
<template> |
||||||
|
<div id="TestImg"> |
||||||
|
<img ref="img" src="../../assets/img/poster/0.jpg" alt="" |
||||||
|
@touchstart="start" |
||||||
|
@touchmove="move" |
||||||
|
@touchend="end" |
||||||
|
> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import globalMethods from "../../utils/global-methods"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "TestImg", |
||||||
|
data() { |
||||||
|
return { |
||||||
|
result: { |
||||||
|
width: 393, |
||||||
|
height: 699 |
||||||
|
}, |
||||||
|
x: 0, |
||||||
|
y: 0, |
||||||
|
scale: 1, |
||||||
|
maxScale: 3, |
||||||
|
minScale: 0.5, |
||||||
|
point1: {x: 0, y: 0}, |
||||||
|
point2: {x: 0, y: 0}, |
||||||
|
diff: {x: 0, y: 0}, |
||||||
|
lastPointermove: {x: 0, y: 0}, |
||||||
|
lastPoint1: {x: 0, y: 0}, |
||||||
|
lastPoint2: {x: 0, y: 0}, |
||||||
|
lastCenter: {x: 0, y: 0}, |
||||||
|
touches: [], |
||||||
|
} |
||||||
|
}, |
||||||
|
created() { |
||||||
|
}, |
||||||
|
mounted() { |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
getCenter(a, b) { |
||||||
|
const x = (a.x + b.x) / 2; |
||||||
|
const y = (a.y + b.y) / 2; |
||||||
|
return {x, y} |
||||||
|
}, |
||||||
|
getDistance(start, stop) { |
||||||
|
return Math.hypot(stop.x - start.x, stop.y - start.y); |
||||||
|
}, |
||||||
|
handlePointers(e, type) { |
||||||
|
for (let i = 0; i < this.touches.length; i++) { |
||||||
|
if (this.touches[i].pointerId === e.pointerId) { |
||||||
|
if (type === 'update') { |
||||||
|
this.touches[i] = e; |
||||||
|
} else if (type === 'delete') { |
||||||
|
this.touches.splice(i, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
start(e) { |
||||||
|
// console.log('start') |
||||||
|
if (e.touches && e.touches.length === 1) { |
||||||
|
} else { |
||||||
|
this.isTwo = true |
||||||
|
this.$refs.img.style['transition-duration'] = '0ms'; |
||||||
|
|
||||||
|
let events = e.touches[0]; |
||||||
|
let events2 = e.touches[1]; |
||||||
|
|
||||||
|
this.lastPoint1 = this.point1 = {x: events.pageX, y: events.pageY}; |
||||||
|
this.lastPoint2 = this.point2 = {x: events2.pageX, y: events2.pageY}; |
||||||
|
this.lastCenter = this.getCenter(this.lastPoint1, this.lastPoint2) |
||||||
|
// console.log('lastPoint1', this.lastPoint1) |
||||||
|
|
||||||
|
} |
||||||
|
}, |
||||||
|
move(e) { |
||||||
|
if (e.touches && e.touches.length === 1) { |
||||||
|
} else { |
||||||
|
this.isTwo = true |
||||||
|
|
||||||
|
let current1 = {x: e.touches[0].pageX, y: e.touches[0].pageY} |
||||||
|
let current2 = {x: e.touches[1].pageX, y: e.touches[1].pageY} |
||||||
|
|
||||||
|
// 双指缩放比例计算 |
||||||
|
let ratio = this.getDistance(current1, current2) / this.getDistance(this.lastPoint1, this.lastPoint2); |
||||||
|
|
||||||
|
|
||||||
|
this.scale = this.scale * ratio |
||||||
|
// 计算当前双指中心点坐标 |
||||||
|
// let center = this.getCenter(current1, current2) |
||||||
|
// console.log('center', center) |
||||||
|
|
||||||
|
// 计算图片中心偏移量,默认transform-origin: 50% 50% |
||||||
|
// 如果transform-origin: 30% 40%,那origin.x = (ratio - 1) * result.width * 0.3 |
||||||
|
// origin.y = (ratio - 1) * result.height * 0.4 |
||||||
|
// 如果通过修改宽高或使用transform缩放,但将transform-origin设置为左上角时。 |
||||||
|
// 可以不用计算origin,因为(ratio - 1) * result.width * 0 = 0 |
||||||
|
// const origin = { |
||||||
|
// x: (ratio - 1) * this.result.width * 0.5, |
||||||
|
// y: (ratio - 1) * this.result.height * 0.5 |
||||||
|
// }; |
||||||
|
// 计算偏移量,认真思考一下为什么要这样计算(带入特定的值计算一下) |
||||||
|
// this.x -= (ratio - 1) * (center.x - this.x) - origin.x - (center.x - this.lastCenter.x); |
||||||
|
// this.y -= (ratio - 1) * (center.y - this.y) - origin.y - (center.y - this.lastCenter.y); |
||||||
|
|
||||||
|
// console.log('this.x', this.x) |
||||||
|
// console.log('this.y', this.y) |
||||||
|
|
||||||
|
// 图像应用缩放效果 |
||||||
|
this.$refs.img.style.transform = |
||||||
|
// `translate3d(${this.x}px,${this.y}px,0) scale(${this.scale})`; |
||||||
|
`scale(${this.scale})`; |
||||||
|
|
||||||
|
// this.lastCenter = {x: center.x, y: center.y}; |
||||||
|
this.lastPoint1 = {x: current1.x, y: current1.y}; |
||||||
|
this.lastPoint2 = {x: current2.x, y: current2.y}; |
||||||
|
} |
||||||
|
}, |
||||||
|
end(e) { |
||||||
|
// console.log('end', e) |
||||||
|
// 图像应用缩放效果 |
||||||
|
this.scale = 1 |
||||||
|
this.x = this.y = 0 |
||||||
|
this.$refs.img.style.transform = |
||||||
|
`translate3d(0px,0px,0) scale(1)`; |
||||||
|
// this.point1 = {x: e.touches[0].pageX, y: e.touches[0].pageY} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="less"> |
||||||
|
|
||||||
|
</style> |
||||||
|
|
||||||
|
<style scoped lang="less"> |
||||||
|
#TestImg { |
||||||
|
height: 100%; |
||||||
|
width: 100%; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
} |
||||||
|
|
||||||
|
img { |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
</style> |
@ -1,241 +0,0 @@ |
|||||||
<!DOCTYPE html> |
|
||||||
<html lang="en"> |
|
||||||
|
|
||||||
<head> |
|
||||||
<meta charset="UTF-8"> |
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|
||||||
<meta name="viewport" |
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> |
|
||||||
<title>pinch</title> |
|
||||||
<style> |
|
||||||
* { |
|
||||||
margin: 0; |
|
||||||
padding: 0; |
|
||||||
} |
|
||||||
|
|
||||||
.container { |
|
||||||
height: 100vh; |
|
||||||
background: #000; |
|
||||||
overflow: hidden; |
|
||||||
} |
|
||||||
|
|
||||||
img { |
|
||||||
touch-action: none; |
|
||||||
} |
|
||||||
|
|
||||||
.log { |
|
||||||
position: fixed; |
|
||||||
top: 0; |
|
||||||
left: 0; |
|
||||||
right: 0; |
|
||||||
bottom: 0; |
|
||||||
z-index: 99; |
|
||||||
padding: 5px; |
|
||||||
color: #FFF; |
|
||||||
font-size: 12px; |
|
||||||
line-height: 18px; |
|
||||||
pointer-events: none; |
|
||||||
} |
|
||||||
</style> |
|
||||||
</head> |
|
||||||
|
|
||||||
<body> |
|
||||||
<div class="container"> |
|
||||||
<img id="image" alt=""> |
|
||||||
</div> |
|
||||||
<div class="log"></div> |
|
||||||
|
|
||||||
<script> |
|
||||||
// 获取dom |
|
||||||
const image = document.getElementById('image'); |
|
||||||
const log = document.querySelector('.log'); |
|
||||||
// 全局变量 |
|
||||||
let result, |
|
||||||
x, |
|
||||||
y, |
|
||||||
scale = 1, |
|
||||||
maxScale, |
|
||||||
minScale = 0.5; |
|
||||||
// 图片加载完成后再操作,否则naturalWidth为0 |
|
||||||
image.addEventListener('load', function () { |
|
||||||
result = getImgSize(image.naturalWidth, image.naturalHeight, window.innerWidth, window.innerHeight); |
|
||||||
image.style.width = result.width + 'px'; |
|
||||||
image.style.height = result.height + 'px'; |
|
||||||
x = (window.innerWidth - result.width) * 0.5; |
|
||||||
y = (window.innerHeight - result.height) * 0.5; |
|
||||||
image.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0) scale(1)'; |
|
||||||
maxScale = Math.max(Math.round(image.naturalWidth / result.width), 3); |
|
||||||
log.innerHTML = `x = ${x.toFixed(0)}<br>y = ${y.toFixed(0)}<br>scale = ${scale.toFixed(5)}`; |
|
||||||
}); |
|
||||||
image.src = 'http://jsdemo.codeman.top/images/liya.jpg'; |
|
||||||
/** |
|
||||||
* 获取图片缩放尺寸 |
|
||||||
* @param {number} naturalWidth |
|
||||||
* @param {number} naturalHeight |
|
||||||
* @param {number} maxWidth |
|
||||||
* @param {number} maxHeight |
|
||||||
* @returns |
|
||||||
*/ |
|
||||||
function getImgSize(naturalWidth, naturalHeight, maxWidth, maxHeight) { |
|
||||||
const imgRatio = naturalWidth / naturalHeight; |
|
||||||
const maxRatio = maxWidth / maxHeight; |
|
||||||
let width, height; |
|
||||||
// 如果图片实际宽高比例 >= 显示宽高比例 |
|
||||||
if (imgRatio >= maxRatio) { |
|
||||||
if (naturalWidth > maxWidth) { |
|
||||||
width = maxWidth; |
|
||||||
height = maxWidth / naturalWidth * naturalHeight; |
|
||||||
} else { |
|
||||||
width = naturalWidth; |
|
||||||
height = naturalHeight; |
|
||||||
} |
|
||||||
} else { |
|
||||||
if (naturalHeight > maxHeight) { |
|
||||||
width = maxHeight / naturalHeight * naturalWidth; |
|
||||||
height = maxHeight; |
|
||||||
} else { |
|
||||||
width = naturalWidth; |
|
||||||
height = naturalHeight; |
|
||||||
} |
|
||||||
} |
|
||||||
return { width: width, height: height } |
|
||||||
} |
|
||||||
|
|
||||||
// 全局变量 |
|
||||||
let isPointerdown = false, // 按下标识 |
|
||||||
pointers = [], // 触摸点数组 |
|
||||||
point1 = { x: 0, y: 0 }, // 第一个点坐标 |
|
||||||
point2 = { x: 0, y: 0 }, // 第二个点坐标 |
|
||||||
diff = { x: 0, y: 0 }, // 相对于上一次pointermove移动差值 |
|
||||||
lastPointermove = { x: 0, y: 0 }, // 用于计算diff |
|
||||||
lastPoint1 = { x: 0, y: 0 }, // 上一次第一个触摸点坐标 |
|
||||||
lastPoint2 = { x: 0, y: 0 }, // 上一次第二个触摸点坐标 |
|
||||||
lastCenter; // 上一次中心点坐标 |
|
||||||
// 绑定 pointerdown |
|
||||||
image.addEventListener('pointerdown', function (e) { |
|
||||||
pointers.push(e); |
|
||||||
point1 = { x: pointers[0].clientX, y: pointers[0].clientY }; |
|
||||||
if (pointers.length === 1) { |
|
||||||
isPointerdown = true; |
|
||||||
image.setPointerCapture(e.pointerId); |
|
||||||
lastPointermove = { x: pointers[0].clientX, y: pointers[0].clientY }; |
|
||||||
} else if (pointers.length === 2) { |
|
||||||
point2 = { x: pointers[1].clientX, y: pointers[1].clientY }; |
|
||||||
lastPoint2 = { x: pointers[1].clientX, y: pointers[1].clientY }; |
|
||||||
lastCenter = getCenter(point1, point2); |
|
||||||
} |
|
||||||
lastPoint1 = { x: pointers[0].clientX, y: pointers[0].clientY }; |
|
||||||
}); |
|
||||||
// 绑定 pointermove |
|
||||||
image.addEventListener('pointermove', function (e) { |
|
||||||
if (isPointerdown) { |
|
||||||
handlePointers(e, 'update'); |
|
||||||
const current1 = { x: pointers[0].clientX, y: pointers[0].clientY }; |
|
||||||
if (pointers.length === 1) { |
|
||||||
diff.x = current1.x - lastPointermove.x; |
|
||||||
diff.y = current1.y - lastPointermove.y; |
|
||||||
lastPointermove = { x: current1.x, y: current1.y }; |
|
||||||
x += diff.x; |
|
||||||
y += diff.y; |
|
||||||
image.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0) scale(' + scale + ')'; |
|
||||||
log.innerHTML = `x = ${x.toFixed(0)}<br>y = ${y.toFixed(0)}<br>scale = ${scale.toFixed(5)}`; |
|
||||||
} else if (pointers.length === 2) { |
|
||||||
const current2 = { x: pointers[1].clientX, y: pointers[1].clientY }; |
|
||||||
// 计算相对于上一次移动距离比例 ratio > 1放大,ratio < 1缩小 |
|
||||||
let ratio = getDistance(current1, current2) / getDistance(lastPoint1, lastPoint2); |
|
||||||
// 缩放比例 |
|
||||||
const _scale = scale * ratio; |
|
||||||
if (_scale > maxScale) { |
|
||||||
scale = maxScale; |
|
||||||
ratio = maxScale / scale; |
|
||||||
} else if (_scale < minScale) { |
|
||||||
scale = minScale; |
|
||||||
ratio = minScale / scale; |
|
||||||
} else { |
|
||||||
scale = _scale; |
|
||||||
} |
|
||||||
// 计算当前双指中心点坐标 |
|
||||||
const center = getCenter(current1, current2); |
|
||||||
// 计算图片中心偏移量,默认transform-origin: 50% 50% |
|
||||||
// 如果transform-origin: 0% 0%,那origin.x = (ratio - 1) * result.width * 0 |
|
||||||
// origin.y = (ratio - 1) * result.height * 0 |
|
||||||
// 如果transform-origin: 30% 40%,那origin.x = (ratio - 1) * result.width * 0.3 |
|
||||||
// origin.y = (ratio - 1) * result.height * 0.4 |
|
||||||
const origin = { x: (ratio - 1) * result.width * 0.5, y: (ratio - 1) * result.height * 0.5 }; |
|
||||||
// 计算偏移量 |
|
||||||
x -= (ratio - 1) * (center.x - x) - origin.x - (center.x - lastCenter.x); |
|
||||||
y -= (ratio - 1) * (center.y - y) - origin.y - (center.y - lastCenter.y); |
|
||||||
image.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0) scale(' + scale + ')'; |
|
||||||
lastCenter = { x: center.x, y: center.y }; |
|
||||||
lastPoint1 = { x: current1.x, y: current1.y }; |
|
||||||
lastPoint2 = { x: current2.x, y: current2.y }; |
|
||||||
log.innerHTML = `x = ${x.toFixed(0)}<br>y = ${y.toFixed(0)}<br> |
|
||||||
scale = ${scale.toFixed(5)}<br> |
|
||||||
centerX = ${center.x.toFixed(0)}<br>centerY = ${center.y.toFixed(0)}<br>`; |
|
||||||
} |
|
||||||
} |
|
||||||
e.preventDefault(); |
|
||||||
}); |
|
||||||
// 绑定 pointerup |
|
||||||
image.addEventListener('pointerup', function (e) { |
|
||||||
if (isPointerdown) { |
|
||||||
handlePointers(e, 'delete'); |
|
||||||
if (pointers.length === 0) { |
|
||||||
isPointerdown = false; |
|
||||||
} else if (pointers.length === 1) { |
|
||||||
point1 = { x: pointers[0].clientX, y: pointers[0].clientY }; |
|
||||||
lastPointermove = { x: pointers[0].clientX, y: pointers[0].clientY }; |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
// 绑定 pointercancel |
|
||||||
image.addEventListener('pointercancel', function (e) { |
|
||||||
if (isPointerdown) { |
|
||||||
isPointerdown = false; |
|
||||||
pointers.length = 0; |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
/** |
|
||||||
* 更新或删除指针 |
|
||||||
* @param {PointerEvent} e |
|
||||||
* @param {string} type |
|
||||||
*/ |
|
||||||
function handlePointers(e, type) { |
|
||||||
for (let i = 0; i < pointers.length; i++) { |
|
||||||
if (pointers[i].pointerId === e.pointerId) { |
|
||||||
if (type === 'update') { |
|
||||||
pointers[i] = e; |
|
||||||
} else if (type === 'delete') { |
|
||||||
pointers.splice(i, 1); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取两点间距离 |
|
||||||
* @param {object} a 第一个点坐标 |
|
||||||
* @param {object} b 第二个点坐标 |
|
||||||
* @returns |
|
||||||
*/ |
|
||||||
function getDistance(a, b) { |
|
||||||
const x = a.x - b.x; |
|
||||||
const y = a.y - b.y; |
|
||||||
return Math.hypot(x, y); // Math.sqrt(x * x + y * y); |
|
||||||
} |
|
||||||
/** |
|
||||||
* 获取中点坐标 |
|
||||||
* @param {object} a 第一个点坐标 |
|
||||||
* @param {object} b 第二个点坐标 |
|
||||||
* @returns |
|
||||||
*/ |
|
||||||
function getCenter(a, b) { |
|
||||||
const x = (a.x + b.x) / 2; |
|
||||||
const y = (a.y + b.y) / 2; |
|
||||||
return { x: x, y: y }; |
|
||||||
} |
|
||||||
</script> |
|
||||||
</body> |
|
||||||
|
|
||||||
</html> |
|
Loading…
Reference in new issue