对于双指缩放,我其实用到了css的transform属性,搭配值matrix(scale(X), skew(X), skew(Y), scale(Y), translate(X), translate(Y));
transform属性它默认是从元素中心点开始向外扩展缩放,且不需要浏览器重绘,由GPU运算渲染,所以我将它的tansform-origin 设置为从左上角开始放大或缩小
transform-origin: 0 0;
缩放比例由双指之间的距离来得出,重要的是要处理:双指落到手机上时中心 如何 位移到 手指拉开或者收缩的两指中心点,这里要注意:div放大缩小 已经改变为 从左上角开始
我定义了 startScale -- 双指开始放到屏幕上div的缩放倍数默认为1 leftNum -- 单次双指离开屏幕后div的左侧偏移量(下次开始操作时左侧偏移量),topNum -- 单次双指离开屏幕后div的上侧偏移量(下次开始操作时上侧偏移量),endScale -- 单次双指离开屏幕后div放大缩小的倍数默认为1(下次开始操作时缩放倍数)
注意:div从始至终在文档上的占位大小并没有变过
![微信图片_20211226214732.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/05bc91edc68647dd918e1fb334ac5226~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?)
//leftNum 和 topNum是偏移量 我们开始设置它们 都为 0
function getOrigin(e){
if(e.touches.length == 2){
// 双指
const org = {
x: (e.touches[0].pageX - 2 * leftNum + e.touches[1].pageX) / 2,
y: (e.touches[0].pageY - 2 * topNum + e.touches[1].pageY) / 2,
distance: Math.sqrt(Math.pow(e.touches[0].pageX - e.touches[1].pageX, 2) + Math.pow(e.touches[0].pageY - e.touches[1].pageY, 2))
}
return org;
}else if(e.touches.length == 1){
//单指
return {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
}
}
绿点代表开始双指中心点位,我们不做处理的话,它放大后,那个点位会移到蓝色点那儿,黑点是现在我们双指在屏幕上的中心点(位移后), 所以我们要做的就是 让 蓝点跑到黑点那儿,
我们开始获取到绿色点位坐标 (不需要我贴代码吧兄弟们,就是两个手指的pageX加起来除以二,pageY加起来除以二)
然后我们再求出先后的距离(根据直角三角形勾股定理)
Math.sqrt(Math.pow(e.touches[0].pageX - e.touches[1].pageX, 2) + Math.pow(e.touches[0].pageY - e.touches[1].pageY, 2))
然后我们可以得到放大或者缩小的倍数,从而得到蓝点的位置,再通过蓝点的位置减去黑点的位置得到偏移量
//获取双指刚放到屏幕的中心点(绿点)
orgData = getOrigin(e);
// 获取新的两指中心点(黑点)
const nowOrg = getOrigin(e);
// 获取放大后的中心点位置(蓝点)
const obj = {
x: orgData * scale,
y: orgData * scale
}
const scale = nowOrg.distance / orgData.distance; //缩放的倍数
我们现在可以得出偏移量
leftNumfalse = nowOrg.x + leftNum - obj.x; //得出双指离开时需要缩放的倍数
topNumfalse = nowOrg.y + topNum - obj.y; //得出双指离开时需要缩放的倍数
endScale = scale * startScale; //得出双指离开时需要缩放的倍数 -- 当前缩放的倍数 * 开始操作的倍数
//这里注意一下:为什么我要再加上 leftNum 跟 topNum, 因为假如上次我们处理过,那么这两个变量会有值, 并且如果放大的话 div 会向 左上 移动, 仔细想想,我们的目的是让放大后的点位移到缩放后的两指中心点
parentNode.style.transform = `matrix(${scale * startScale}, 0, 0, ${scale * startScale}, ${nowOrg.x + leftNum - obj.x}, ${nowOrg.y + topNum - obj.y})`;
现在操作手指离开屏幕的操作:把变化的内容记录下来
parentNode.addEventListener('touchend', (e) => {
parentNode.style.transform = `matrix(${endScale}, 0, 0, ${endScale}, ${leftNumfalse}, ${topNumfalse})`;
leftNum = leftNumfalse; //如果放大并溢出屏幕的话 这个值为负值
topNum = topNumfalse; //如果放大并溢出屏幕的话 这个值为负值
startScale = endScale; //这时候我们要把开始倍数 设置为 缩放后的倍数用来下次缩放
})
现在第一次放大后 我们的布局差不多这样
![微信图片_20211226225422.jpg](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d67231ef1ae84741bcfe40cb5750ace5~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?)
好了,我们开始看图说话吧,哈哈哈哈哈 中间中性笔矩形为手机屏幕,左上方虚线矩形为第一次变换后div的样子,现在我们把双指放到屏幕上 中心点为A点, 放大后 两指中心点为B点, 现在有人问C点是什么呀??嗐,又忘了嘛?我们的div是从左上角开始缩放的呀,并且占的位置还是最开始的位置,就是初始位置,上面提到了,所以要让C走到B,思考 c的坐标怎么来,B的坐标又怎么来(这个直接可以取到 通过上面 方法 当然可以直接获取到 这里要注意:B点的坐标包括偏移量从虚线框框到B点的距离 也就是多了leftNum 跟 topNum)c点的坐标可以通过A点的坐标乘以缩放的倍数得到 所以上面的transform偏移量需要再加上leftNum跟topNum 因为C点的坐标是到实线框的距离
因此得到上一步的代码 把上一次缩放的倍数再乘以这次缩放的倍数,大致逻辑就这样,可能我想复杂了
单指拖动的话比较简单,不用处理缩放大小 只关心偏移量就可以,这里我直接监听的pageX跟pageY变化的距离
// 获取单指放到屏幕的位置
SingleFinger = getOrigin(e);
// move时新节点
const nowObj = {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
parentNode.style.transform = `matrix(${endScale}, 0, 0, ${endScale}, ${nowObj.x - SingleFinger.x + leftNum}, ${nowObj.y - SingleFinger.y + topNum})`;
leftNumfalse = nowObj.x - SingleFinger.x + leftNum;
topNumfalse = nowObj.y - SingleFinger.y + topNum;
这里附上我的代码
DOCTYPE html>
:root{
--foo: red;
--border: 1px solid green;
}
*{
margin: 0;
padding: 0;
}
body{
box-sizing: border-box;
}
.parent{
box-sizing: border-box;
width: 1000px;
height: 500px;
border: var(--border);
transform-origin: 0 0;
position: relative;
}
.imgStyle{
width: 100%;
height: 100%;
}
///////////////////////////////////////
let orgData = null, nowScale = 1, x, y, twoSig, leftNum = 0, topNum = 0,leftNumfalse, topNumfalse, nowScalefalse;
let SingleFinger = null, singLeft, singTop, oneSig;
const parentNode = document.querySelector('.parent');
function getOrigin(e){
if(e.touches.length == 2){
// 双指
const org = {
x: (e.touches[0].pageX - 2 * leftNum + e.touches[1].pageX) / 2,
y: (e.touches[0].pageY - 2 * topNum + e.touches[1].pageY) / 2,
distance: Math.sqrt(Math.pow(e.touches[0].pageX - e.touches[1].pageX, 2) + Math.pow(e.touches[0].pageY - e.touches[1].pageY, 2))
}
return org;
}else if(e.touches.length == 1){
//单指
return {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
}
}
parentNode.addEventListener('touchstart', (e) => {
if(e.touches.length == 2){
let flag = true;
for(let i = 0; i < e.touches.length; i++){
if(e.touches[i].target.nodeName != 'IMG'){
flag = false;
}
}
if(flag) {
//获取双指刚放到屏幕的中心点
twoSig = true;
orgData = getOrigin(e);
x = getOrigin(e).x;
y = getOrigin(e).y;
}
}else if(e.touches.length == 1){
oneSig = true;
SingleFinger = getOrigin(e);
}
})
parentNode.addEventListener('touchmove', (e) => {
e.preventDefault();
if(e.touches.length == 2){
if(twoSig){
let flag = true;
for(let i = 0; i < e.touches.length; i++){
if(e.touches[i].target.nodeName != 'IMG'){
flag = false;
}
}
if(flag) {
// 获取新的两指中心点
const nowOrg = getOrigin(e);
const scale = nowOrg.distance / orgData.distance;
if(scale * nowScale < 1) return false;
// 获取放大后的中心点位置1
const obj = {
x: orgData.x * scale,
y: orgData.y * scale
}
parentNode.style.transform = `matrix(${scale * nowScale}, 0, 0, ${scale * nowScale}, ${nowOrg.x + leftNum - obj.x}, ${nowOrg.y + topNum - obj.y})`;
leftNumfalse = nowOrg.x + leftNum - obj.x;
topNumfalse = nowOrg.y + topNum - obj.y;
nowScalefalse = scale * nowScale;
}
}
}else if(e.touches.length == 1){
if(oneSig){
const nowObj = {
x: e.touches[0].pageX,
y: e.touches[0].pageY
}
parentNode.style.transform = `matrix(${nowScalefalse}, 0, 0, ${nowScalefalse}, ${nowObj.x - SingleFinger.x + leftNum}, ${nowObj.y - SingleFinger.y + topNum})`;
leftNumfalse = nowObj.x - SingleFinger.x + leftNum;
topNumfalse = nowObj.y - SingleFinger.y + topNum;
}
}
})
parentNode.addEventListener('touchend', (e) => {
parentNode.style.transform = `matrix(${nowScalefalse}, 0, 0, ${nowScalefalse}, ${leftNumfalse}, ${topNumfalse})`;
leftNum = leftNumfalse;
topNum = topNumfalse;
nowScale = nowScalefalse;
})
|