メインコンテンツへスキップ
見出し画像
Photo bybrandkojo

【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ショートカットアプリ関連


    この記事が参加している募集

     
     
    本当のお仕事は底辺の非正規労働者。東京都在住。現実逃避のためchatGPTと一緒に無駄に真面目な開発ごっこをしています。効率化とタスク管理を好む一方で非効率でも自動で動くカッコよさげな仕組みも好き。実はITスキル低め。趣味はAbemaの報道リアリティショー『アベマプライム』視聴。

    あなたへのおすすめ