# snap-axis
**Repository Path**: refline/snap-axis
## Basic Information
- **Project Name**: snap-axis
- **Description**: SnapAxis 是一个用于管理吸附轴(如水平轴或垂直轴)的类,支持吸附点的添加、删除、更新以及吸附逻辑的实现。适用于拖拽、对齐、辅助线等场景。
- **Primary Language**: TypeScript
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-05-25
- **Last Updated**: 2025-05-25
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# SnapAxis
`SnapAxis` 是一个用于管理吸附轴(如水平轴或垂直轴)的类,支持吸附点的添加、删除、更新以及吸附逻辑的实现。适用于拖拽、对齐、辅助线等场景。

## 安装
使用 npm 或 yarn 安装:
```sh
npm install snap-axis
```
或
```sh
yarn add snap-axis
```
## 使用示例
```ts
import { SnapAxis } from "snap-axis";
const snapAxis = new SnapAxis({
snapValues: [
{ value: 10, id: "10" },
{ value: 20, id: "20" },
{ value: 30, id: "30" },
],
});
let startPageX = 0;
let result;
const updater = snapAxis.getSnapGroupUpdater([3, 30, 50], startPageX, { distance: 5 });
startPageX++;
result = updater(startPageX);
console.log(result); // { values: [4,31,51], snapped: false }
startPageX++;
result = updater(startPageX);
console.log(result); // { values: [10,37,57], snapped: true }
startPageX++;
result = updater(startPageX);
console.log(result); // { values: [10,37,57], snapped: false }
// ...
```
[](https://codesandbox.io/p/sandbox/2gzwtx)
## API 示例
### getSnapUpdater 吸附更新器
```ts
const snapAxis = new SnapAxis({
snapValues: [
{ value: 10, id: "10" },
{ value: 20, id: "20" },
{ value: 30, id: "30" },
],
});
let pageX = 1;
const updater = snapAxis.getSnapUpdater(8, pageX);
pageX++;
const result = updater(pageX);
console.log(result); // { value: 10, snapped: true }
pageX++;
const result = updater(pageX);
console.log(result); // { value: 10, snapped: false }
```
### getSnapGroupUpdater 吸附组更新器
```ts
const snapAxis = new SnapAxis({
snapValues: [
{ value: 10, id: "10" },
{ value: 20, id: "20" },
{ value: 30, id: "30" },
],
});
const updater = snapAxis.getSnapGroupUpdater([10, 20], 0);
const result = updater(1);
console.log(result); // { values: [10, 20], snapped: false }
```
### getSnappedValues 获取某个值对应的所有吸附点。
```ts
snapAxis.deleteSnapValue({ value: 10, id: "A" });
snapAxis.deleteSnapValue({ value: 10, id: "B" });
const snappedValues = snapAxis.getSnappedValues(10);
console.log(snappedValues); // [{ value: 10, id: "A" },{ value: 10, id: "B" }]
```
### checkSnapped / checkGroupSnapped
判断某个值或一组值是否处于吸附状态。
```ts
snapAxis.checkSnapped(10);
snapAxis.checkGroupSnapped([10, 20]);
```
[更多 API](./SnapAxis.md)
## 接入示例
> [UMD 使用示例](./umd.demo.html)
```html
SnapAxis Example
```
```js
// index.mjs
import { SnapAxis } from "snap-axis";
const TickWidth = 700;
const BoxWidth = 60;
// 随机生成刻度
function randomRange(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const tickValues = [0, TickWidth, TickWidth / 2];
for (let i = 0; i < 10; i++) {
tickValues.push(randomRange(0, TickWidth));
}
tickValues.sort((a, b) => a - b);
// 初始化 SnapAxis
const snapAxis = new SnapAxis({
snapValues: tickValues.map((value, idx) => ({
id: `snap_${idx}`,
value,
})),
});
// 渲染刻度
const tickEl = document.getElementById("tick");
tickEl.style.width = TickWidth + "px";
tickValues.forEach((value) => {
const div = document.createElement("div");
div.className = "tickItem";
div.style.left = value + "px";
tickEl.appendChild(div);
});
// 拖拽逻辑
let value = 0;
let disableSnap = false;
const dragBox = document.getElementById("dragBox");
dragBox.style.width = BoxWidth + "px";
dragBox.style.left = value + "px";
const snappedLabel = document.getElementById("snappedLabel");
const disableSnapInput = document.getElementById("disableSnap");
function getSnapGroup(val) {
return [val, val + BoxWidth / 2, val + BoxWidth];
}
function updateUI() {
// 检查是否吸附
let isSnapped = false;
let snappedValues = [];
const snapGroup = getSnapGroup(value);
for (let i = 0; i < snapGroup.length; i++) {
const v = snapGroup[i];
if (snapAxis.checkSnapped(v)) {
isSnapped = true;
snappedValues = snapAxis.getSnappedValues(v);
break;
}
}
dragBox.className = "dragBox" + (isSnapped ? " snapped" : "");
dragBox.style.left = value + "px";
if (isSnapped && snappedValues.length) {
dragBox.style.backgroundColor = "red";
snappedLabel.style.display = "";
snappedLabel.innerHTML =
`Snapped Values:
` +
snappedValues
.map((item) => `id=${item.id}, value=${item.value}`)
.join("
");
} else {
dragBox.style.backgroundColor = "";
snappedLabel.style.display = "none";
snappedLabel.innerHTML = "";
}
}
dragBox.addEventListener("mousedown", (e) => {
const snapGroup = getSnapGroup(value);
// 获取吸附更新器
const updater = snapAxis.getSnapGroupUpdater(snapGroup, e.clientX, {
distance: 5,
disableSnap,
});
function onMouseMove(e) {
// 计算拖动偏移量
const result = updater(e.clientX);
const values = result.values;
value = Math.max(0, Math.min(TickWidth, values[0]));
updateUI();
}
function onMouseUp() {
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
document.body.style.cursor = "";
}
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
document.body.style.cursor = "move";
});
disableSnapInput.addEventListener("change", (e) => {
disableSnap = e.target.checked;
});
updateUI();
```
## 相关
- [refline.js](https://github.com/refline/refline.js)