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

kp-image-crop

v0.1.0

Published

零依赖的原生图片裁剪组件:指定比例 / 任意比例两种模式,按原图像素无损导出

Readme

KPImageCrop

零依赖的原生图片裁剪组件。只做裁剪,两种模式:指定比例 与 任意比例。

  • 不依赖 Vue / React,纯 TypeScript + DOM,任何项目都能用
  • 组件自己只渲染两样东西:图片显示面 和 裁剪框。文件选择按钮、确认/取消、上传逻辑都由宿主项目自己的 UI 承担,组件只提供函数调用
  • 裁剪结果按裁剪区在原图上的真实像素导出,显示缩放不影响输出分辨率

安装

npm i kp-image-crop

包名不能含大写,因此 npm 包名是 kp-image-crop,类名仍为 KPImageCrop。

快速开始

import { KPImageCrop } from 'kp-image-crop';

// 宿主的文件选择容器由项目自己提供,组件只接收选出来的 File
const crop = new KPImageCrop({
  container: document.querySelector('#crop-host'),
  ratio: 'free', // 任意比例;改成 1 / 16 / 9 / 4:3 写法见下
});

fileInput.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  if (file) await crop.load(file);
});

// 宿主的「确定」按钮
confirmBtn.addEventListener('click', async () => {
  const { blob, width, height } = await crop.getCropBlob();
  await upload(blob);
  console.log(width, height);
});

container 也可传选择器字符串。样式默认自动注入一次,无需额外 import 'kp-image-crop/style.css'。

两种模式

crop.setRatio('free');      // 任意比例:8 个把手各走各的
crop.setRatio(1);           // 指定比例:正方形
crop.setRatio(16 / 9);
crop.setRatio([4, 3]);      // 等价于 4/3
crop.getRatio();            // 读回当前设置

指定比例时,把手拖动会保持对侧边/对角为锚点,另一轴按比例联动;裁剪框永远不会超出图片边界;装不下时等比缩框而不是破坏比例。切换比例会保持框的中心,取该中心下能容纳的最大面积。

API

| 方法 | 说明 | | --- | --- | | load(src, opts?) | 载入 File/Blob/URL/dataURL,返回原图 {width,height}(已按 EXIF 转正)。重复调用会释放上一张 | | setRatio(ratio) / getRatio() | 切换 1、[4,3]、16/9、'free'、null | | getRect() | 当前裁剪区,原图自然像素、浮点;未载入时为 null | | setRect(rect) | 回填裁剪区,可只给部分字段;越界会被夹回图内 | | fitToImage() | 裁剪框撑满图片(固定比例时为该比例下的最大内接框) | | getNaturalSize() | 原图像素尺寸 | | getDisplayScale() | 显示面相对原图的缩放系数,宿主要叠加自定义标记时用 | | getCropBlob(opts?) | 裁剪为 Blob,返回 {blob,width,height,capped,rect} | | getCropFile(name, opts?) | 同上但返回 File;name 不带扩展名时按输出 MIME 自动补 | | recompute() | 容器宽度变化、或从 display:none 变为可见后重新计算显示缩放 | | on(event, cb) | 'change'(裁剪框变化,回传 rect)与 'load';返回取消监听的函数 | | destroy() | 摘除监听、释放位图与 objectURL、移除自身 DOM |

导出选项

await crop.getCropBlob({
  type: 'image/jpeg',  // 默认跟随原图格式
  quality: 0.95,       // JPEG/WebP 编码质量
  width: 512,          // 倍图输出宽度;只给一个维度时另一维按裁剪区比例推导
  height: 512,
  allowUpscale: false, // 默认不允许放大,见下
});

无头裁剪

已经知道矩形(自己画选区、或复用上次位置)时可以完全绕开组件 UI:

import { cropImage } from 'kp-image-crop';

const { blob } = await cropImage(file, { x: 100, y: 50, width: 640, height: 320 });

关于「不影响画质」

裁剪框只是取景建议,导出时重新按原图采样:

  • 几何状态(x/y/width/height)一直保存在原图自然像素空间,DOM 位置每帧由 scale 派生;导出不读 DOM 尺寸
  • 因此图片显示得多小都一样:把 4000px 的图缩到 300px 宽显示,框出 640px 区域,输出的仍是 640×N 的原始像素,1:1 拷贝、零重采样
  • 不指定 width/height 就是无损路径;PNG → 裁剪 → PNG 像素完全一致

需要如实说明的三点边界:

  1. JPEG 源无法做到字节无损。浏览器只能把画布重新编码,重编码即有损。默认 quality: 0.95 并跟随源格式;要严格无损像素请显式 type: 'image/png'(体积会明显变大)。
  2. 倍图不会造出细节。指定的输出宽高超过裁剪区原生像素时默认封顶到原生分辨率,返回结果里 capped: true;传 allowUpscale: true 才会插值放大。
  3. 超大画布会主动报错。Safari/iOS 的画布面积约 1670 万像素上限,超限时浏览器会静默产出空白图,组件改为抛出 CanvasTooLargeError(带 suggestedWidth/suggestedHeight),宁可失败也不静默降质。

主题

覆盖 CSS 自定义属性即可,无需 !important:

#crop-host .kpic-root {
  --kpic-handle-color: #fff;
  --kpic-handle-size: 14px;
  --kpic-border-color: rgba(255, 255, 255, 0.9);
  --kpic-mask-opacity: 0.55;   /* 框外压暗 */
  --kpic-grid-color: rgba(255, 255, 255, 0.35); /* 三分线 */
}

DOM 结构类名前缀 kpic-:.kpic-root、.kpic-image、.kpic-box、.kpic-handle[data-dir]。

已知边界

  • 不做缩放、平移、旋转、形状蒙版——这些不在本组件范围内
  • 容器初始宽度必须大于 0。若组件挂在 display:none 的弹窗里,显示后要调一次 recompute(),或传 autoResize: true 交给 ResizeObserver
  • EXIF 方向由 createImageBitmap(..., {imageOrientation:'from-image'}) 处理;个别老浏览器识别错时可传 load(src, { orientation: 6 }) 手工纠正
  • 只支持浏览器端,无 SSR 用途;destroy() 后实例不可复用

本地开发

npm run demo           # 起 demo 页(文件选择按钮在组件外部,演示宿主 UI 的分工)
npm test               # 几何纯函数单测(含随机拖拽 fuzz)
npm run typecheck
npm run build
npm run check           # 无头 Chrome 真实跑裁剪管线 + 合成指针拖拽
npm run check:dist      # 同一套断言改跑打包产物 dist 而不是 src

check 需要 npm run demo 已在跑。脚本通过 CDP awaitPromise 等待异步链路完成——--dump-dom 配 --virtual-time-budget 会在 toBlob/createImageBitmap 跑完前就截断,只能拿到前几项结果。