@sano1023/rs-image
v0.9.0
Published
依存ゼロの画像処理&合成エディタ(リサイズ・圧縮・トリミング・円形切り抜き・WebP変換・レイヤー合成)
Maintainers
Readme
配布版(ビルド済み) — 本パッケージはビルド済みファイルのみを含みます。 利用は無償(商用可)ですが、改変・再配布はできません(LICENSE.txt 参照)。 機能追加・改修のご依頼は有償で承ります → https://parelabo.com ([email protected])
インストール
npm install @sano1023/rs-imagenpmjs.com の公開パッケージです。ログインやトークンは不要です。 更新・旧レジストリからの移行の詳細は 共通インストール手順 を参照してください。登録済みバージョンは npm で確認できます。以下のパッケージ名による import は Vite などのバンドラ向けです。
npm install https://github.com/sano1023/rs-package/raw/main/tarballs/rs-image-0.9.0.tgz使い方
バニラ JS(ESM・バンドラあり)
import { createRSImageEditor } from '@sano1023/rs-image';
import '@sano1023/rs-image/rs-image.css'; // スタイル(バンドラ経由)
createRSImageEditor(document.querySelector('#app'), { /* オプション */ });<script> タグ(CDN・ビルド環境不要)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@sano1023/[email protected]/dist/rs-image.css">
<script src="https://cdn.jsdelivr.net/npm/@sano1023/[email protected]/dist/rs-image.min.js"></script>
<script>
// 公開APIはグローバル RSImage に載る
RSImage.createRSImageEditor(document.querySelector('#app'), { /* オプション */ });
</script>Vue 3
import { RsImageEditor } from '@sano1023/rs-image/vue';
import '@sano1023/rs-image/rs-image.css'; // スタイル(バンドラ経由)<template>
<RsImageEditor />
</template>React 18 / 19
import { RsImageEditor } from '@sano1023/rs-image/react';
import '@sano1023/rs-image/rs-image.css'; // スタイル(バンドラ経由)
export default function App() {
return <RsImageEditor />;
}
vue/reactは peerDependency です(バンドルには含みません)。アプリ側のものが使われます。
rs-image
依存ゼロの画像処理&合成エディタライブラリ。多機能なインタラクティブ画像エディタ(トリミング・自動トリミング・ペン切り抜き・マジックワンド・選択して消す・自動切り抜き(MLアダプタ注入)・消しゴム・回転/反転・色調整・トーンカーブ/レベル補正・色域別HSL調整・コピースタンプ・覆い焼き/焼き込み/ぼかしブラシ・フィルタ・文字・スタンプ・図形(ブレンドモード・影/縁取り/グロー・グラデ塗り・破線対応)・ペン・スポイト・モザイク・フレーム、すべてリアルタイム反映)と、リサイズ・圧縮・WebP変換の処理エンジンをブラウザ完結で提供します。表示する機能は tools オプションで自由に構成できます。ビルド不要・ESモジュール。
- 高品質縮小: 段階縮小(半分ずつ)+
imageSmoothingQuality:'high'で大縮小でもジャギらない - EXIF正立: スマホ写真の Orientation を読み込み時に自動補正
- 形式・圧縮: png / jpeg / webp、
quality指定、targetBytes(目標バイト数に品質二分探索で寄せる) - 円形切り抜き: 透過付き(png/webp)。プロフィール画像に
- 出力自由: Blob / dataURL(base64) / HTMLCanvasElement を選べる。結果に width/height/bytes を含む
デモ
# このリポジトリで
php -S localhost:8099
# → http://localhost:8099/rs-image/demo/ を開くimport { processImage } from '@sano1023/rs-image';
import '@sano1023/rs-image/rs-image.css';クイックスタート
// 選択画像を長辺1200pxにリサイズして WebP 圧縮、Blob で受け取る
const { blob, width, height, bytes } = await processImage(file, {
resize: { width: 1200, height: 1200, fit: 'contain' }, // 比率維持で内接
format: 'webp',
quality: 0.8,
output: 'blob',
});
// 目標バイト数に寄せる(品質を自動調整)
await processImage(file, { format: 'jpeg', targetBytes: 100 * 1024, output: 'blob' });
// 円形アイコン(透過PNG)
const dataURL = await processImage(file, {
circle: true,
resize: { width: 256, height: 256, fit: 'cover' },
format: 'png',
output: 'dataURL',
});インタラクティブエディタ — createRSImageEditor(el, opts)
画像は常にキャンバスに表示され、すべての操作がリアルタイムに反映されます(WYSIWYG)。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sano1023/rs-package@main/rs-image/dist/rs-image.css">import { createRSImageEditor } from '@sano1023/rs-image';
const editor = createRSImageEditor('#editor', {
height: 460,
// 表示する機能と順序を構成できる(省略時は全ツール)
tools: ['select', 'crop', 'rotate', 'adjust', 'filter', 'text', 'stamp', 'shape', 'draw', 'mosaic', 'frame'],
crop: { ratios: [['フリー', null], ['正方形', 1]], circle: true },
adjust: { sliders: ['brightness', 'contrast', 'saturate'] },
filter: { presets: ['mono', 'sepia', { key: 'night', label: 'ナイト', css: 'brightness(0.75) hue-rotate(30deg)' }] },
shape: { kinds: ['rect', 'arrow'] },
stamps: [
{ url: '/stamps/heart.png', category: 'きもち', name: 'ハート' },
{ url: '/stamps/sale.png', category: 'ラベル', name: 'SALE' },
],
fonts: [{ name: 'Kosugi Maru', url: '/fonts/KosugiMaru.woff2' }],
});
await editor.setImage(file); // ベース画像(最背面・固定)
const { blob } = await editor.export({ format: 'webp', quality: 0.9 });ツール(ツールバー・すべてリアルタイム)
| ツール | 動作 |
| --- | --- |
| 選択 | レイヤークリックで選択。ドラッグ移動・四隅ハンドルで拡縮・上部ハンドルで回転・Delete削除。Shift+クリックで複数選択(整列・分布・一括移動/削除)、Alt+ドラッグで複製、Shift+ドラッグで水平/垂直拘束、移動中はベース/他レイヤーの端・中央にスナップ(ガイド線表示・Ctrlで無効)、矢印キー微調整(Shiftで10px)・Ctrl+Z/Y |
| トリミング | ドラッグ枠・8ハンドル・暗転+三分割グリッド。比率プリセット(crop.ratios で構成可)・円形 |
| 自動トリミング | 開いた瞬間に四隅の色を背景とみなして対象を検出し、囲むトリミング枠を自動で出します(うまく拾えなければ対象をクリック)。枠の形は長方形(検出範囲の縦横比を保って拡縮)/ 正方形 / フリー(縦横比自由)。枠はトリミングと同じくドラッグ移動・8ハンドルで調整でき、余白もライブ反映。透過はせず、画像サイズを切り詰めるだけ |
| ペン切り抜き | ベジェのペンツール型。クリックでアンカーを置いて囲み、始点クリックで閉じて内側を残す/消す。なめらか補間(Catmull-Rom)・境界ぼかし・余白の切り詰め・アンカーのドラッグ調整 |
| マジックワンド | クリックした場所と近い色で繋がっている範囲を透過(許容度スライダ+数値入力・ソフト境界・1クリック=1履歴) |
| 選択して消す | マジックワンドの「確認してから消す」版。クリックで近い色の範囲を選択し、ペン切り抜きと同じ見え方(選択範囲を白の点線で囲み、外側を暗転)でプレビューしながら許容度をライブ調整。Shift+クリックで範囲を追加(カーソルに+)/ Alt+クリックで除外(−)・ブラシで追加/除外・境界ぼかし・「隣接のみ」OFF・選択範囲を消す / 選択範囲以外を消すを選んで「消す」で確定(1回=1履歴)。ベース画像にも画像レイヤーにも使える |
| 自動切り抜き | セグメンテーションアダプタ(ML)で被写体を自動検出して背景を透過。反転(被写体を消す)・境界ぼかし。アダプタは注入式でコアは依存ゼロのまま |
| 回転 | 90°左右・自由角度スライダ(劣化しない・数値入力可)・左右/上下反転 |
| 調整 | 明るさ・コントラスト・彩度・色相・ぼかし・ビネットのスライダ(非破壊・adjust.sliders で構成可) |
| トーン補正 | レベル補正(黒点/白点/ガンマ)+トーンカーブ(ヒストグラム表示・RGB/R/G/Bチャンネル別・点をクリック追加/ドラッグ/ダブルクリック削除)+オートコントラスト・ホワイトバランス+シャープ(アンシャープマスク)。全部ライブプレビュー→適用で焼き込み |
| 色域調整 | 8色域別のHSL調整(レッド〜マゼンタの色域を選んで色相±60/彩度/明度。「赤だけ彩度を下げる」等。無彩色は巻き込まない) |
| フィルタ | モノクロ/セピア/フェード/ウォーム/クール/ビビッド/反転のプリセット(filter.presets で絞り込み・独自CSS filterの追加可) |
| 文字追加 | 内容・色・サイズ・フォントをタイプした瞬間に反映 |
| スタンプ | stamps をカテゴリタブで分類表示・クリック挿入 |
| 画像追加 | ファイル選択でオーバーレイ画像レイヤーを追加 |
| 図形 | 四角(角丸スライダ)・円・台形・星(頂点数/尖りスライダ)・吹き出し(しっぽドラッグ)・多角形・線・矢印(両端対応)(shape.kinds で構成可)。線色・太さ・破線(実線/破線/点線/一点鎖線)・塗り(単色/線形グラデ/放射グラデ)。四角・円・吹き出しは辺ハンドルで幅・高さを個別変更(縦横比が変えられる)、線・矢印は左右ハンドルで長さ変更。台形・星・多角形は頂点をドラッグして自由に変形+なめらか曲線化(Catmull-Rom)。四角・円は「頂点編集できる図形に変換」でベジェ風の自由変形へ。多角形はペンで描く(クリックで頂点を置き、始点クリックで図形化) |
| ペン | フリーハンド描画。1ストローク=1レイヤーなので後から移動・削除できる |
| スポイト | キャンバスの合成後の色を拾い、選択中の図形/文字/ペンの色(未選択ならペンの描画色)に反映 |
| コピースタンプ | Alt+クリック(または最初のクリック)でコピー元を採取→ドラッグで転写(サイズ・硬さ)。ゴミ消し・オブジェクト複製に |
| ブラシ補正 | なぞった部分だけ明るく(覆い焼き)/暗く(焼き込み)/ぼかし(サイズ・強さ、1ストローク=1履歴) |
| モザイク | ドラッグ範囲をピクセル化(粗さ設定可) |
| 消しゴム | なぞった部分を透明化(部分透過)。サイズ・硬さ調整、「戻す」で消しすぎを復元。ベース画像にも画像レイヤーにも使える |
| フレーム | なし/単色/ライン/二重の縁取り(色・太さリアルタイム) |
- スタンプ・画像レイヤーは選択後「この画像をトリミング」で個別トリミング(切り抜き後も位置がずれない)
- スライダーは数値でも入力できる: 許容度・太さ・サイズ・角度などすべてのスライダーに数値入力欄が並びます。打ち込んで Enter(またはフォーカスを外す)で確定し、範囲外の値は自動でクランプ。スライダーを動かせば数値欄も追従します
- 透明度スライダ: どのレイヤーも 5〜100% でリアルタイムに半透明化
- ブレンドモード: どのレイヤーも乗算/スクリーン/オーバーレイ/覆い焼き/焼き込み/差の絶対値/色相/彩度/カラー/輝度など16種で合成できる
- レイヤー効果: 影(色/ぼかし/距離XY)・縁取り(文字/図形/ペン)・グロー を全レイヤーに(チェックひとつ・リアルタイム)
- 背景透過スライダ: 画像の外周から繋がっている背景をflood-fillで透過(グラデーション背景対応・背景色に似た被写体内部は巻き込まない)。許容度を上げ下げしてもセッション元画像から再計算するので劣化しない
- レイヤー操作: 前面へ / 背面へ / 削除
- 円形トリミングや自由回転で透過が生じた場合は PNG/WebP で書き出し
自動トリミング
「被写体の大きさに合わせて余白を切り詰めたい」ときの道具です。透過はしません(背景を消すなら「選択して消す」かマジックワンド)。
- ツールバーの自動トリミングを選ぶ — 四隅の色を背景とみなして対象を検出し、囲む枠がすぐ出ます(オレンジの細い点線=検出範囲)
- 背景が単色でない・別の対象を切りたいときは、対象をクリックすると近い色で繋がっている範囲に枠を合わせ直します(「自動検出に戻す」で元に戻る)
- 長方形 / 正方形 / フリー で枠の形を選び、必要ならドラッグ移動・ハンドルで調整
- 適用
| 設定 | 説明 | | --- | --- | | 長方形 | 検出範囲にぴったりの枠。拡縮しても縦横比を保つ | | 正方形 | 対象を中心に 1:1 の枠(画像からはみ出す分は内側へ寄せる) | | フリー | 縦横比自由で拡縮できる | | 余白 | 検出範囲から外側に広げる px 数 |
- 余白・形を変えると枠は検出範囲から作り直されます(手で調整した枠は上書き)
- 実行は1段の履歴になるので「元に戻す」で取り消せます。切り詰めに合わせてレイヤー位置も追従します
editor.setMode('objtrim'); // 開いた時点で自動検出・枠を表示
editor.objTrimStyle.shape = 'square'; // 'rect'=長方形 / 'square'=正方形 / 'free'=フリー
editor.objTrimStyle.pad = 16; // 余白
editor.fitObjTrimRect(); // 形・余白を変えたら枠を合わせ直す
editor.pickObjRegion({ x: 320, y: 240 }); // 対象をクリック(image座標)。null で自動検出に戻す
editor.objTrimRect(); // → トリミング枠 { x, y, width, height }
editor.applyObjTrim(); // 適用選択して消す
マジックワンドと違い、クリックで近い色の範囲を選び、選択範囲をプレビューで確認しながら詰めてから消す道具です。「消す」を押すまで画像は変わりません。
- ツールバーの選択して消すを選ぶ(画像レイヤーを選択中ならそのレイヤー、なければベース画像が対象)
- 消したい(または残したい)部分をクリック — 近い色で繋がっている範囲が選ばれます。ペン切り抜きと同じく、選択範囲は白の点線で囲まれ、外側が暗転します
- 別の場所も選びたいときは Shift+クリックで範囲を追加、選びすぎた部分は Alt+クリックで範囲から除外(押している間はカーソルに+ / −が付きます。後からした操作ほど優先)。普通のクリックは選び直しです
- 許容度を動かすと、クリックしたすべての点の範囲がその場で選び直されます
- 色だけで拾いきれない部分は ブラシで追加 / ブラシで除外 でなぞって修正(「クリックで選択」のままでも Shift/Alt を押したままドラッグすればブラシになります)。ブラシの修正は許容度を動かしても残ります
- 選択範囲を消す / 選択範囲以外を消す を選んで消す。道具は持ったままなので続けて別の場所を選べます
| 設定 | 説明 | | --- | --- | | 許容度 | 0〜100。大きいほど広い色差を同じ範囲とみなす(数値入力可・ライブ反映) | | ペンのサイズ | ブラシで追加・除外するときの直径(画像 px・1〜300) | | 境界ぼかし | 消す境界をぼかす px 数(0 でも外周1pxは色の近さに応じてなめらかに抜く) | | 隣接のみ | OFF にすると繋がっていなくても画像全体から同系色を拾う |
editor.setMode('colorcut');
editor.cutStyle.tol = 30; // 許容度
editor.cutStyle.invert = false; // true=選択範囲以外を消す
editor.cutStyle.brush = 'add'; // 'pick'=クリックで選択 / 'add'=ブラシで追加 / 'sub'=ブラシで除外
editor.pickCutRegion({ x: 10, y: 10 }); // クリック(image座標)= 選び直し
editor.pickCutRegion({ x: 400, y: 200 }, 'add'); // Shift+クリック = 範囲を追加('sub' で Alt+クリック = 除外)
editor.clearCutRegion(); // 選択解除
editor.applyCut(); // 消すヒストリー(編集履歴パネル)
パネル下段に操作の一覧が常設され、1段ずつ戻る/進むだけでなく任意の段をクリックして一気に飛べます。
- 「元に戻す」「やり直す」は1クリック=1段。ボタンのツールチップに残り段数が出ます
- ショートカット: Ctrl+Z(戻る)/ Ctrl+Shift+Z・Ctrl+Y(進む)/ Ctrl+Alt+Z(一段階戻る)。スライダーにフォーカスがあっても効きます
- 各段には操作名(「トリミング」「明るさ」「消しゴム」…)が付き、現在位置が反転表示、やり直しで進める先は淡色表示
- 戻ってから新しい操作をすると、その先の段は破棄されます(履歴の分岐)
- 戻っても道具は持ったまま(消しゴムで戻しても消しゴムのまま。トリミング等のセッション型ツールだけ選択に戻ります)
- 連続入力(文字タイプ・スライダードラッグ)は打ち終わりで1段にまとめて積まれるので、履歴がスライダーの1目盛りで埋まりません
- 既定は30段。
history: { max: 60 }で変更、history: { open: false }でパネルを閉じた状態で開始(ツールバーの「ヒストリー」ボタンで開閉)
const editor = createRSImageEditor('#editor', { history: { max: 60, open: true } });
editor.on('history', (e) => console.log(e.index, e.label)); // 1段積まれるたび
editor.historyList(); // [{ index, label, ts, current }]
await editor.jumpHistory(3); // 4段目まで一気に戻る/進む
editor.clearHistory(); // 現在の状態だけ残して捨てるエディタAPI
setImage(src) / addText(text, opts) / addImageLayer(src, opts) / addShape(kind, opts)(kind: rect | ellipse | trapezoid | star | balloon | poly | line | arrow。台形・星・多角形は opts.pts で初期頂点も指定可) / finishShapePen() / convertToPoly(layer) / pickObjRegion(pt) / fitObjTrimRect() / objTrimRect() / applyObjTrim() / pickCutRegion(pt) / clearCutRegion() / applyCut() / duplicateLayer(layer) / alignSelected(mode) / flip(axis) / export(ops)(processImage の全オプションが使える) / flatten() / undo() / redo() / jumpHistory(index) / historyList() / clearHistory() / toggleHistoryPanel(open?) / getState() / on('change', cb) / on('history', cb) / destroy()
レイヤー共通プロパティ: opacity(透明度) / blend(ブレンドモード) / fx({ shadow: {color, blur, dx, dy}, outline: {color, width}, glow: {color, blur} })。図形はさらに dash(solid|dash|dot|dashdot)・fillType(solid|linear|radial)・fill2・gradAngle・radius(角丸)・smooth(なめらか曲線)など。
自動切り抜き(アダプタ注入)
rs-livecam と同じ契約 { name, segment(canvas) → mask | null } のアダプタを注入します。mask は canvas か ImageData で、アルファ値=被写体度(全画素が不透明ならグレースケール輝度を被写体度として自動解釈。RMBG等のグレーマスク対応)。
editor.setSegmentation({ name: 'my-seg', segment(canvas) { /* ... */ return maskCanvas; } });
await editor.autoCut({ invert: false, feather: 4 }); // true=適用成功。undoで取り消せるMediaPipe Selfie Segmentation(人物・軽量)と RMBG-1.4(汎用・約44MB)の実装例は demo/index.html の「自動切り抜きアダプタ」ボタンを参照。
- マスクがほぼ空(反転時はほぼ全面)のときは画像が丸ごと消える事故を防ぐため適用せず false を返す。失敗理由は
editor.lastAutoCutError('no-adapter' | 'error' | 'no-mask' | 'empty')で判別できる - 人物用(MediaPipe)は人物が写った写真専用・WebGL必須。リモートデスクトップ等WebGLの無い環境では WASM で動く RMBG-1.4 を使う
Vue / React ラッパー
配布ビルドには Vue 3 / React 18 のラッパーコンポーネントが同梱されています(vue / react は peerDependency)。
// Vue 3
import { RsImageEditor } from '@sano1023/rs-image/vue';
// <RsImageEditor :src="fileOrUrl" :height="460" @change="..." ref="ed" />
// await ed.value.export({ format: 'webp' })
// React 18
import { RsImageEditor } from '@sano1023/rs-image/react';
// <RsImageEditor src={fileOrUrl} height={460} onChange={...} ref={ref} />
// await ref.current.export({ format: 'webp' })src(URL / dataURL / File / Blob)の変更を watch してsetImageします- ref 経由で
editor() / setImage / export / flatten / undo / redo / jumpHistory / historyList / clearHistory / toggleHistoryPanel / getState / setSegmentationが使えます historyプロパティでヒストリー設定({ max, open })を渡せます。1段積まれるたびに@history/onHistoryが発火します- アンマウントで自動
destroy()。デモ:demo/vue.html/demo/react.html
処理エンジンAPI
processImage(src, ops): Promise<Result>
src: File / Blob / dataURL / URL文字列 / HTMLImageElement / HTMLCanvasElement / ImageBitmap。
| オプション | 説明 |
| --- | --- |
| resize | { width?, height?, fit? }。片方だけ指定で比率維持。両方+fit: 'contain'(内接)/ 'cover'(枠を埋めて中央切り出し)/ 'fill'(歪める) |
| crop | { x, y, width, height } 矩形トリミング(回転適用後の座標) |
| rotate | 0 / 90 / 180 / 270(度) |
| circle | true で円形切り抜き(透過が要るため png/webp 必須。jpeg指定はエラー) |
| format | 'png' / 'jpeg' / 'webp'(既定 png) |
| quality | 0〜1(jpeg/webp。既定 0.92) |
| targetBytes | 目標バイト数。品質を二分探索で寄せる(到達不能なら最小品質にフォールバック) |
| output | 'blob'(既定) / 'dataURL' / 'canvas' |
| background | 非透過形式の下地色(既定 '#ffffff'。透過画像を jpeg 化する際の背景) |
| maxSourceDimension | 読み込み時の最大辺(既定 4096)。超える画像は事前縮小してメモリを抑える |
| autoOrient | EXIF 正立補正(既定 true) |
戻り値 Result: { blob?, dataURL?, canvas?, width, height, bytes, format, quality }(bytes はエンコード後のサイズ)
ショートカット
import { toBlob, toDataURL } from '@sano1023/rs-image';
const blob = await toBlob(file, { resize: { width: 800 }, format: 'webp' });
const url = await toDataURL(file, { circle: true, format: 'png' });低レベルAPI
loadImage(src, opts)(EXIF正立込みの読み込み)、encodeCanvas(canvas, opts)、canvasToBlob(canvas, mime, quality)、geometry(fitSize / describeCover / clampCrop / downscaleSteps 等の純関数)も公開しています。
仕組み
パイプラインは load → (rotate) → (crop) → resize → (circle) → encode → 出力。
- 段階縮小:
downscaleStepsが「前段の最大1/2まで」の中間サイズ列を作り、各段を canvas に描いて縮める。1発 drawImage の大縮小で出るエイリアシングを防ぐ - EXIF:
createImageBitmapのimageOrientation:'from-image'を優先。非対応環境は APP1 セグメントを自前パースして回転行列を焼き込む - targetBytes: quality を二分探索。非圧縮画像で目標に届かないときは最小サイズの結果を返す(元の高品質は返さない)
- Canvas 2D のみ使用。
OffscreenCanvasがあれば優先
既知の制限(v0.3 時点)
- フィルタ(明るさ等)・ブラシ描画・モザイクは v0.3(REQUIREMENTS.md 参照)
- 文字の縁取り・影、ガンマ/シャープネス調整は未実装
- サーバー送信は行わない(出力の送信は利用側の責務)
- RAW / CMYK / 動画は非対応
検証
Playwright(Chromium)による45項目(エンジン12 + エディタ19 + 高度な編集機能14)の自動テストで確認済み。エディタ側は実際のマウスドラッグで枠リサイズ・レイヤー移動・拡縮・回転・スタンプ挿入・undo/redo・平坦化エクスポートを検証。
ライセンス
MIT
