画像を右クリックすると塗り絵用の線画を表示します。スライダーで濃さを調整できます。

    
      
  • /*
     * @title 画像を右クリックすると塗り絵用の線画を表示します。スライダーで濃さを調整できます。
     * @description 濃さを調整したあと印刷して、塗り絵として使ってください。
     * @include http://*
     * @license MIT License
     * @require 
     */
    (function() {
        alert("画像を右クリックすると塗り絵用の線画を表示します。\nスライダーで濃さを調整できます。");
    
        function handleContextMenu(event) {
            let target = event.target;
            if (target.tagName === "IMG") {
                event.preventDefault(); // 右クリックのデフォルト動作を防ぐ
                processImage(target.src, target.width, target.height);
            }
        }
    
        document.addEventListener("contextmenu", handleContextMenu, false);
    
        function processImage(imgUrl, width, height) {
            let newTab = window.open(); // ポップアップブロック回避
            if (!newTab) {
                alert("ポップアップブロックを解除してください!");
                return;
            }
    
            let sliderValue = 50;  // 初期値(中間)
    
            function getFilter() {
                let slope, intercept;
                if (sliderValue <= 50) {
                    // 左側: (0.5, -0.25) → (10, -5) の線形変化
                    let t = sliderValue / 50;
                    slope = 0.5 + (10 - 0.5) * t;
                    intercept = -0.25 + (-5 + 0.25) * t;
                } else {
                    // 右側: (10, -5) → (10, 0) の線形変化
                    let t = (sliderValue - 50) / 50;
                    slope = 10;
                    intercept = -5 + (0 + 5) * t;
                }
    
                return `
                    <filter id="edge">
                        <feColorMatrix type="saturate" values="0"></feColorMatrix>
                        <feComponentTransfer>
                            <feFuncR type="linear" slope="${slope}" intercept="${intercept}"></feFuncR>
                            <feFuncG type="linear" slope="${slope}" intercept="${intercept}"></feFuncG>
                            <feFuncB type="linear" slope="${slope}" intercept="${intercept}"></feFuncB>
                        </feComponentTransfer>
                    </filter>`;
            }
    
            let svgTemplate = `
                <div class="print-container">
                    <svg xmlns="http://www.w3.org/2000/svg" id="mainSVG" width="100%" height="100%" viewBox="0 0 ${width} ${height}" preserveAspectRatio="xMidYMid meet">
                        <defs id="defs">${getFilter()}</defs>
                        <image id="image" href="${imgUrl}" x="0" y="0" width="${width}" height="${height}" filter="url(#edge)"/>
                    </svg>
                </div>`;
    
            let style = `
            <style>
                body { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh; background: white; margin: 0; padding: 0; }
                .print-container { width: 100vw; height: 90vh; display: flex; justify-content: center; align-items: center; }
                svg { max-width: 100%; max-height: 100%; }
    
                .controls { display: flex; flex-direction: column; align-items: center; gap: 10px; width: 80%; max-width: 400px; }
                .slider-label { font-size: 14px; font-weight: bold; }
                input[type="range"] { width: 100%; }
    
                @media print {
                    .controls { display: none; } /* 印刷時にスライダーを非表示 */
                    .print-container { width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; }
                    svg { width: 100vw; height: 100vh; max-width: 100%; max-height: 100%; }
                }
            </style>`;
    
            let controls = `
            <div class="controls">
                <label class="slider-label">濃さを調整</label>
                <input type="range" id="contrastSlider" min="0" max="100" value="${sliderValue}" step="1">
            </div>`;
    
            newTab.document.open();
            newTab.document.write(`
                <!DOCTYPE html>
                <html>
                <head>${style}</head>
                <body>${svgTemplate}${controls}</body>
                </html>
            `);
            newTab.document.close();
    
            // スライダーイベントを追加
            newTab.onload = function() {
                let contrastSlider = newTab.document.getElementById("contrastSlider");
    
                function updateFilter() {
                    sliderValue = parseFloat(contrastSlider.value);
                    let newFilter = getFilter();
                    newTab.document.getElementById("defs").innerHTML = newFilter;
                }
    
                contrastSlider.addEventListener("input", updateFilter);
            };
        }
    })();
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2025/02/22 20:37:40 - 02/22
  2. 2025/02/22 20:33:40 - 02/22