共计 4523 个字符,预计需要花费 12 分钟才能阅读完成。
以挪动一个 SVG 图形为例,残缺代码如下:
<!doctype html> | |
<html> | |
<head> | |
<style> | |
body {margin: 0;} | |
/* 最外层容器 */ | |
#content { | |
/* 阻止页面大小随着 SVG 挪动而变动 */ | |
overflow: hidden; | |
} | |
/* 固定 Header */ | |
#header { | |
left: 0; | |
right: 0; | |
position: fixed; | |
height: 50px!important; | |
background-color: rgb(124, 252, 45); | |
z-index: 10; | |
} | |
/* 包容须要追随手势挪动的 SVG 的容器 */ | |
#panel_container { | |
height: 100vh; | |
z-index: 5; | |
background-color: rgb(248, 213, 98); | |
} | |
/* 固定 Footer */ | |
#footer { | |
left: 0; | |
right: 0; | |
bottom: 0; | |
position: fixed; | |
height: 50px!important; | |
background-color: rgb(124, 252, 45); | |
z-index: 10; | |
} | |
/* 须要追随手势挪动的 SVG */ | |
svg {background-color: rgb(250, 250, 153); | |
width: 100%; | |
height: 100%; | |
} | |
</style> | |
<script> | |
// 须要追随手势挪动的 SVG。页面加载后赋值 | |
var svg = null; | |
// 用户操作后冀望的 SVG 缩放比例 | |
var scale = 1; | |
// 用户操作后冀望的 SVG 中心点的横向偏移 | |
var posX = 0; | |
// 用户操作后冀望的 SVG 中心点的纵向偏移 | |
var posY = 0; | |
// 咱们不心愿 SVG 比例过小或偏出屏幕,所以在 SVG 被挪动或缩放到合乎用户冀望的比例和地位后,还须要让 SVG 回弹到以下合乎咱们冀望的比例和地位 | |
// 通过修改后的 SVG 缩放比例 | |
var finalScale = 1; | |
// 通过修改后的 SVG 中心点的横向偏移 | |
var finalPosX = 0; | |
// 通过修改后的 SVG 中心点的纵向偏移 | |
var finalPosY = 0; | |
// 上次依据用户操作进行渲染的工夫戳。每次执行 render()时被设置。用户的操作优先于零碎的回弹修改,所以只有以后工夫间隔该工夫戳曾经过来了一段时间 (200 毫秒) 后,咱们才能够开始对 SVG 进行回弹修改。详见 bounce() | |
var lastRenderingTime; | |
// 监听页面加载实现事件。为 SVG 加点元素以便地位参考 | |
window.addEventListener("load", function() {svg = document.querySelector("svg"); | |
// 简略加一个小方块 | |
let newRect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); | |
newRect.setAttribute("x", "150"); | |
newRect.setAttribute("y", "150"); | |
newRect.setAttribute("width", "100"); | |
newRect.setAttribute("height", "100"); | |
newRect.setAttribute("fill", "#5cceee"); | |
svg.appendChild(newRect); | |
}); | |
// 依据用户的操作进行渲染 | |
var render = () => { | |
// 更新渲染的工夫戳 | |
lastRenderingTime = new Date().getTime(); | |
window.requestAnimationFrame(() => { | |
// 依据新的地位和缩放比例渲染 SVG | |
svg.style.transform = `translate3D(${posX}px, ${posY}px, 0px) scale(${scale})`; | |
// 如果不想要回弹成果,以下代码能够去掉 | |
// 让零碎稍后尝试回弹修改(有必要的话) | |
setTimeout(bounce, 500); | |
}); | |
}; | |
// 如果不想要回弹成果,以下 bounce()代码能够去掉 | |
// 依据修改值回弹修改 SVG | |
var bounce = () => { | |
// 用户操作后的缩放和地位没有问题,用户冀望与零碎冀望雷同,间接返回 | |
if (scale == finalScale && posX == finalPosX && posY == finalPosY) return; | |
// 用户可能还在操作中,按优先级规定不进行零碎操作,而是期待片刻再尝试 | |
if (new Date().getTime() - lastRenderingTime < 200) { | |
// 期待 500 毫秒 | |
setTimeout(bounce, 500); | |
// 返回 | |
return; | |
} | |
// 包容 SVG 的容器。联合 SVG 和包容 SVG 的容器的绝对地位和大小来计算回弹量 | |
var ref = document.getElementById("panel_container").getBoundingClientRect(); | |
// SVG 以后的缩放比例 | |
var lastScale = svg.getBoundingClientRect().width / ref.width; | |
// SVG 以后的中心点横向偏移 | |
var lastPosX = svg.getBoundingClientRect().x + svg.getBoundingClientRect().width / 2 - ref.width / 2; | |
// SVG 以后的中心点纵向偏移 | |
var lastPosY = svg.getBoundingClientRect().y + svg.getBoundingClientRect().height / 2 - ref.height / 2; | |
// 该次回弹的指标比例。与最终修改值差距过小则间接置为最终修改值 | |
var stepScale = Math.abs(finalScale - lastScale) > 0.1 ? (lastScale + Math.sign(finalScale - lastScale) * 0.1) : finalScale; | |
// 该次回弹的指标中心点横向偏移。与最终修改值差距过小则间接置为最终修改值 | |
var stepPosX = Math.abs(finalPosX - lastPosX) > 10 ? (lastPosX + (finalPosX - lastPosX) / 10 + Math.sign(finalPosX - lastPosX) * 10) : finalPosX; | |
// 该次回弹的指标中心点纵向偏移。与最终修改值差距过小则间接置为最终修改值 | |
var stepPosY = Math.abs(finalPosY - lastPosY) > 10 ? (lastPosY + (finalPosY - lastPosY) / 10 + Math.sign(finalPosY - lastPosY) * 10) : finalPosY; | |
window.requestAnimationFrame(() => { | |
// 依据指标地位和缩放比例渲染 SVG | |
svg.style.transform = `translate3D(${stepPosX}px, ${stepPosY}px, 0px) scale(${stepScale})`; | |
}); | |
// 如果曾经修改实现则返回 | |
if (stepScale == finalScale && stepPosX == finalPosX && stepPosY == finalPosY) return; | |
// 持续尝试进行下一次修改 | |
setTimeout(bounce, 500); | |
}; | |
// 监听触控板手势事件。理论为鼠标滚轮事件 | |
window.addEventListener('wheel', (e) => { | |
// 包容 SVG 的容器。联合 SVG 和包容 SVG 的容器的绝对地位和大小来计算修改值 | |
var ref = document.getElementById("panel_container").getBoundingClientRect(); | |
// SVG 以后的缩放比例 | |
var lastScale = svg.getBoundingClientRect().width / ref.width; | |
// SVG 以后的中心点横向偏移 | |
var lastPosX = svg.getBoundingClientRect().x + svg.getBoundingClientRect().width / 2 - ref.width / 2; | |
// SVG 以后的中心点纵向偏移 | |
var lastPosY = svg.getBoundingClientRect().y + svg.getBoundingClientRect().height / 2 - ref.height / 2; | |
if (e.ctrlKey) { | |
// 缩放事件 | |
// 依据操作确定缩放值。地位不变 | |
finalScale = scale = lastScale - e.deltaY * 0.01; | |
finalPosX = posX = lastPosX; | |
finalPosY = posY = lastPosY; | |
} else { | |
// 挪动事件 | |
// 依据操作确定地位。缩放值不变 | |
finalScale = scale = lastScale; | |
finalPosX = posX = lastPosX - e.deltaX * 2; | |
finalPosY = posY = lastPosY - e.deltaY * 2; | |
} | |
// 如果不想要回弹成果,以下对 finalXXX 的赋值能够间接批改为对 XXX 的赋值 | |
// 不容许 SVG 过小(缩放值小于 1) | |
if (scale < 1) finalScale = 1; | |
// 不容许 SVG 右边界进入屏幕内 | |
var minPosX = ref.width / 2 - ref.width * finalScale / 2; | |
// 不容许 SVG 左边界进入屏幕内 | |
var maxPosX = ref.width * finalScale / 2 - ref.width / 2; | |
// 不容许 SVG 下边界进入屏幕内 | |
var minPosY = ref.height / 2 - ref.height * finalScale / 2; | |
// 不容许 SVG 上边界进入屏幕内 | |
var maxPosY = ref.height * finalScale / 2 - ref.height / 2; | |
// 依据以上限度值确定零碎最终修改值 | |
if (posX < minPosX) finalPosX = minPosX; | |
if (posX > maxPosX) finalPosX = maxPosX; | |
if (posY < minPosY) finalPosY = minPosY; | |
if (posY > maxPosY) finalPosY = maxPosY; | |
// 依据用户操作进行渲染 | |
render();}); | |
</script> | |
</head> | |
<body> | |
<!-- 最外层容器 --> | |
<div id='content'> | |
<!-- 搁置一个固定 Header 为肉眼察看位移作参考 --> | |
<div id='header'> | |
Header | |
</div> | |
<!-- 包容须要追随手势挪动的 SVG 的容器 --> | |
<div id='panel_container'> | |
<!-- 须要追随手势挪动的 SVG --> | |
<svg></svg> | |
</div> | |
<!-- 搁置一个固定 Footer 为肉眼察看位移作参考 --> | |
<div id='footer'> | |
Footer | |
</div> | |
</div> | |
</body> | |
</html> |
正文完
发表至: javascript
2023-03-24