# 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` 是一个用于管理吸附轴(如水平轴或垂直轴)的类,支持吸附点的添加、删除、更新以及吸附逻辑的实现。适用于拖拽、对齐、辅助线等场景。 ![snap-axis](./examples/images/demos.gif) ## 安装 使用 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 } // ... ``` [![Edit SnapAxis](https://codesandbox.io/static/img/play-codesandbox.svg)](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)