npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

snap-axis

v1.3.0

Published

SnapAxis 是一个用于管理吸附轴(如水平轴或垂直轴)的类,支持吸附点的添加、删除、更新以及吸附逻辑的实现。适用于拖拽、对齐、辅助线等场景。

Readme

SnapAxis

SnapAxis 是一个用于管理吸附轴(如水平轴或垂直轴)的类,支持吸附点的添加、删除、更新以及吸附逻辑的实现。适用于拖拽、对齐、辅助线等场景。

snap-axis

安装

使用 npm 或 yarn 安装:

npm install snap-axis

yarn add snap-axis

使用示例

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

接入示例

UMD 使用示例

<!DOCTYPE html>
<html>
  <head>
    <title>SnapAxis Example</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <style>
      #app {
        font-family: sans-serif;
        text-align: center;
        font-size: 12px;
      }

      .container {
        height: 100px;
        position: relative;
        margin: 0 auto;
        user-select: none;
        width: 700px;
      }

      .tickWarp {
        position: absolute;
        bottom: 0;
        left: 0;
      }

      .tick {
        height: 5px;
        position: "relative";
        border-bottom: 1px solid #757575;
      }

      .tickItem {
        position: absolute;
        bottom: 0;
        left: 0;
        width: 1px;
        height: 5px;
        background-color: #757575;
      }

      .dragBox {
        position: absolute;
        left: 0;
        bottom: 0;
        height: 40px;
        background-color: #0307f2;
        opacity: 0.3;
      }

      .snappedLabel {
        position: absolute;
        top: 0;
        left: 0;
        pointer-events: none;
        font-size: 12px;
        color: #a3a3a3;
        text-align: left;
      }

      .setting {
        position: absolute;
        top: 0;
        right: 0;
        font-size: 12px;
        color: #a3a3a3;
      }

      .setting label {
        display: flex;
        align-items: center;
      }

      .setting input {
        margin: 0;
        padding: 0;
      }
    </style>
    <div id="app">
      <div class="container" id="demo02">
        <div class="snappedLabel" id="snappedLabel" style="display: none"></div>
        <div class="setting">
          <label>
            禁用吸附:
            <input type="checkbox" id="disableSnap" />
          </label>
        </div>
        <div class="tickWarp">
          <div class="tick" id="tick"></div>
        </div>
        <div class="dragBox" id="dragBox"></div>
      </div>
    </div>

    <script src="./index.mjs" type="module"></script>
  </body>
</html>
// 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 =
      `<div>Snapped Values:</div>` +
      snappedValues
        .map((item) => `id=${item.id}, value=${item.value}`)
        .join("<br>");
  } 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();

API 示例

getSnapUpdater 吸附更新器

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 吸附组更新器

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 获取某个值对应的所有吸附点。

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

判断某个值或一组值是否处于吸附状态。

snapAxis.checkSnapped(10);
snapAxis.checkGroupSnapped([10, 20]);

更多 API


## 相关
- [refline.js](https://github.com/refline/refline.js)