
【UI改善】【片手操作】写真をボタン操作だけで少しずつ削って自動で微調整してくれるiPhoneショートカットレシピ
昨日ご紹介したこちらの記事ですが。
すっかりScriptableのUIをいじくることにハマってしまい一晩でかなり変わったのでご紹介させてください。
見た目は以下のように変わりました。

【変更点】
✅画像を削る値をボタンで変更できるようにしました。ボタン長押し高速変更もできます。
✅ボタン横並びにして、画像を大きめに。
✅ボタン以外の箇所を誤ってダブルタップしたら画面の縮尺が変更されるのを抑制。これでボタン連打しての変更が安定。
✅保存後は写真アプリに移動。
✅異常終了後の回避処理を実装(昨日の記事追記を参照)
今回のレシピ本体
(レシピとは別にScriptableというアプリとコードの設定が必要となります。設定は前回の記事をご参照ください)
【Scriptableのコード】
// ========= 画像 =========
Pasteboard.copyString("")
const img = args.images[0]
const base64 = Data.fromPNG(img).toBase64String()
// ★ 自分のショートカット名
const shortcutName = "ImageCropFlow"
// ========= HTML =========
const html = `
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>
body { margin:0; text-align:center; font-family:-apple-system; }
.notice {
background:#ffecec;
color:#c00;
padding:6px;
font-size:13px;
}
img {
width:100%;
max-height:52vh;
object-fit:contain;
}
button {
width:100%;
margin:3px 0;
padding:10px;
font-size:14px;
}
.row {
display:flex;
justify-content:center;
gap:6px;
padding:0 6px;
}
.col {
width:48%;
}
input {
width:60px;
text-align:center;
}
body {
touch-action: manipulation;
-webkit-user-select: none;
-webkit-touch-callout: none;
}
</style>
</head>
<body>
<div class="notice">
⚠ closeは使わないでください(下のボタンで終了)
</div>
<img id="img" src="data:image/png;base64,${base64}">
<!-- 削り値 -->
<div class="row">
<div class="col">
<button ontouchstart="startDown()" ontouchend="stopStep()" ontouchcancel="stopStep()">
削り値(px) −
</button>
</div>
<input id="stepInput" value="20">
<div class="col">
<button ontouchstart="startUp()" ontouchend="stopStep()" ontouchcancel="stopStep()">
削り値(px) +
</button>
</div>
</div>
<!-- 左右 -->
<div class="row">
<div class="col"><button onclick="cutDir('left')">⏪ 左</button></div>
<div class="col"><button onclick="cutDir('right')">➡ 右</button></div>
</div>
<!-- 上下 -->
<div class="row">
<div class="col"><button onclick="cutDir('top')">🔼 上</button></div>
<div class="col"><button onclick="cutDir('bottom')">🔽 下</button></div>
</div>
<!-- リセット / 戻る(←ここだけ変更) -->
<div class="row">
<div class="col"><button onclick="reset()">🔙 リセット</button></div>
<div class="col"><button onclick="undo()">↩ 戻る</button></div>
</div>
<!-- 保存 / 終了 -->
<div class="row">
<div class="col"><button onclick="save()">💾 保存</button></div>
<div class="col"><button onclick="finish()">🛑 終了</button></div>
</div>
<script>
let original = new Image()
original.src = document.getElementById("img").src
let cut = {l:0,t:0,r:0,b:0}
let history = []
original.onload = () => draw()
function getStep() {
let val = parseInt(document.getElementById("stepInput").value)
if (isNaN(val) || val <= 0) return 20
return val
}
function draw() {
const canvas = document.createElement("canvas")
const ctx = canvas.getContext("2d")
const w = original.width
const h = original.height
const newW = w - cut.l - cut.r
const newH = h - cut.t - cut.b
if (newW <= 0 || newH <= 0) return
canvas.width = newW
canvas.height = newH
ctx.drawImage(original, -cut.l, -cut.t)
document.getElementById("img").src = canvas.toDataURL()
}
function saveHistory() {
history.push({...cut})
}
function undo() {
if (history.length === 0) return
cut = history.pop()
draw()
}
function reset() {
cut = {l:0,t:0,r:0,b:0}
draw()
}
function cutDir(dir) {
saveHistory()
const step = getStep()
if (dir==="left") cut.l += step
if (dir==="right") cut.r += step
if (dir==="top") cut.t += step
if (dir==="bottom") cut.b += step
draw()
}
function save() {
const data = document.getElementById("img").src
if (!data || !data.startsWith("data:image")) return
window.result = data
}
function finish() {
window.result = "NO_SAVE"
}
function stepUp() {
let input = document.getElementById("stepInput")
let val = parseInt(input.value) || 0
input.value = val + 1
}
function stepDown() {
let input = document.getElementById("stepInput")
let val = parseInt(input.value) || 0
if (val > 1) input.value = val - 1
}
let interval = null
function startUp() {
stepUp()
interval = setInterval(stepUp, 100)
}
function startDown() {
stepDown()
interval = setInterval(stepDown, 100)
}
function stopStep() {
clearInterval(interval)
}
</script>
</body>
</html>
`
// ========= WebView =========
let wv = new WebView()
await wv.loadHTML(html)
wv.present(true)
// ========= 結果待ち =========
let result = null
while (true) {
await new Promise(resolve => Timer.schedule(0.3, false, resolve))
try {
result = await wv.evaluateJavaScript("window.result", false)
} catch(e) {
break
}
if (result) break
}
// ========= 終了分岐 =========
if (result === "NO_SAVE") {
Pasteboard.copyString("NO_SAVE")
try { wv.dismiss() } catch(e) {}
Safari.open(
"shortcuts://run-shortcut?name=" + encodeURIComponent(shortcutName)
)
Script.complete()
return
}
if (!result) {
try { wv.dismiss() } catch(e) {}
Script.complete()
return
}
// ========= 保存 =========
Pasteboard.copyString(result)
try { wv.dismiss() } catch(e) {}
Safari.open(
"shortcuts://run-shortcut?name=" + encodeURIComponent(shortcutName)
)
Script.complete()今回はこのアプリが必要となります。
設定方法は前回記事をご参照ください。
まとめ
だいぶ削る操作に安定感が出てきた感じです☺️
iPhoneショートカットアプリ関連