大石制作ブログ
デザイナー大石真也が知りたかったこと、見たかったことのメモと、仕事紹介。
https://blog.s0014.com
2018-12-09T15:00:00Z
大石 真也(Shinya Oishi) / 大石制作
フォームのhiddenが設定されたinputタグを可視化するブックマークレット
https://blog.s0014.com/posts/2018-12-10-input-hidden/
2018-12-09T15:00:00Z
2018-12-10T09:14:43+09:00
大石 真也(Shinya Oishi) / 大石制作
たまに欲しくなる、フォームの隠しデータを見やすくする機能をブックマークレット化しました。
<p>フォームのinputタグに type=“hidden” の設定された物を可視化するブックマークレットを作ってみました。<br>
hiddenのinput項目は、Chromeとかのデベロッパーツールでエレメントをほじくればわかるのだけど、項目の多いフォームとかでは把握に苦労することも多いですよね。
そんなときに手軽に使えて便利ですよー。</p>
<h2>できること</h2>
<ul>
<li><input type=“hidden”>の設定されたinputタグエレメントのそばに、その内容を表示するテーブルを生成してくれます。</li>
<li>ブックマークレットをもう一度実行すると、表示していたテーブルをまとめて削除して元通りになります。</li>
<li>テーブルのヘッダー行をクリックすると、テーブルを個別に削除できます。</li>
<li>テーブルにあるデータはにコピペしやすくしています。</li>
<li>“display: none"が設定されたタグの中に配置されたエレメントにも、ある程度表示対応してます。</li>
<li>potision: absoluteやfixedでレイヤー重ねされた構造のページでの動作は苦手です。</li>
</ul>
<p>簡略化すると、こんな感じに見えるようにしてくれます。
こんな感じのHTMLが……</p>
<pre class="highlight html"><code><span class="nt"><form></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"hidden"</span> <span class="na">value=</span><span class="s">"大石制作"</span> <span class="na">data-domain=</span><span class="s">"s0014"</span><span class="nt">></span>
<span class="nt"></form></span>
</code></pre>
<p>↓↓↓</p>
<p>……こんな感じになります。</p>
<pre class="highlight html"><code><span class="nt"><form></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"hidden"</span> <span class="na">value=</span><span class="s">"大石制作"</span> <span class="na">data-domain=</span><span class="s">"s0014.com"</span><span class="nt">></span>
<span class="nt"><table></span>
<span class="nt"><thead></span>
<span class="nt"><tr></span>
<span class="nt"><th</span> <span class="na">colspan=</span><span class="s">"2"</span><span class="nt">></span>hidden data<span class="nt"></th></span>
<span class="nt"></tr></span>
<span class="nt"></thead></span>
<span class="nt"><tbody></span>
<span class="nt"><tr></span>
<span class="nt"><th></span>value<span class="nt"></th></span>
<span class="nt"><td></span>大石制作<span class="nt"></td></span>
<span class="nt"></tr></span>
<span class="nt"><tr></span>
<span class="nt"><th></span>data-domain<span class="nt"></th></span>
<span class="nt"><td></span>s0014.com<span class="nt"></td></span>
<span class="nt"></tr></span>
<span class="nt"></tbody></span>
<span class="nt"></table></span>
<span class="nt"></form></span>
</code></pre>
<p>実際に生成されるコードはこれにcssやjsが組み込まれているので、もっと複雑です。
害は無いので、詳しくは実際にうごかしてみてください。</p>
<p>以前作った<a href="/posts/2016-08-17-js-dummy-input-form/">JavaScriptでフォームに仮入力できるブックマークレット</a>と組み合わせて利用すると、フォームの調査に便利です。</p>
<h2>ブックマークレットはこちら</h2>
<p>下記リンクをブラウザのブックマークバー等にぐぐっとドラッグして登録してみてください。</p>
<p><a id="OutputHref" ng-attr-href="{{ output }}" class="ng-binding" href="javascript:!function(){(function(){function%20e(e){function%20t(e){var%20o=e.parentElement;return%22none%22===window.getComputedStyle(o).display%26%26r.push(o),%22BODY%22===o.tagName%3Fr:t(e.parentElement)}var%20r=[];return%20t(e),r}(function(){var%20t=document.querySelectorAll(%22.s0014com-show-hidden-data%22);if(0%3Ct.length)for(var%20r%20in%20t)t.hasOwnProperty(r)%26%26%22object%22==typeof%20t[r]%26%26t[r].remove();else{r=document.getElementsByTagName(%22input%22);var%20o=0;t=[];for(var%20n%20in%20r)if(0%3C=Number(n)%26%26%22hidden%22===r[n].getAttribute(%22type%22)){var%20i=document.createElement(%22table%22);i.setAttribute(%22style%22,%22border-top:%201px%20solid%20%23666;border-right:%201px%20solid%20%23666;border-bottom:%200;border-left:%200;border-spacing:%200;margin-bottom:%20.5rem;%22),i.setAttribute(%22class%22,%22s0014com-show-hidden-data%22);var%20l=%22Hidden%20data%22,d=%22background:%20%23666;border-top:%200;border-right:%200;border-bottom:%201px%20solid%20%23666;border-left:%201px%20solid%20%23666;color:%20white;font-size:%20.7rem;padding:%20.1rem;text-align:%20center;%22,a=e(r[n]);a.length%3F(a=a.slice(-1)[0],l+='%3Cbr%3E(%22display:%20none%22の設定されたエレメント内)',d+=%22background:%20%23eee;color:%20black;%22):a=r[n],l='%3Cthead%20onclick=%22this.parentElement.remove()%22%20onmouseover=%22this.parentElement.style.opacity%20=%200.5%22%20onmouseout=%22this.parentElement.style.opacity%20=%201%22%3E%3Ctr%3E%3Cth%20colspan=%222%22%20style=%22'+d+'%22%3E'+l+%22%3C/th%3E%3C/tr%3E%3C/thead%3E%22,d=void%200;var%20s=r[n].attributes,p=%22%22;for(d%20in%20s)if(s.hasOwnProperty(d)){var%20c=s[d].value,b=s[d].name;void%200!==c%26%26%22type%22!==b%26%26(p+='%3Ctr%3E%3Cth%20style=%22background:%20%23999;border-top:%200;border-right:%200;border-bottom:%201px%20solid%20%23666;border-left:%201px%20solid%20%23666;color:%20white;padding:%20.2rem%20.5rem;text-align:%20left;%22%3E%3Cinput%20value=%22'+b+'%22%20style=%22background:%20transparent;border:%200;color:%20inherit;width:%20100%25%22%20onclick=%22this.select(0,this.value.length)%22%3E%3C/th%3E%3Ctd%20style=%22background:%20white;border-top:%200;border-right:%200;border-bottom:%201px%20solid%20%23666;border-left:%201px%20solid%20%23666;color:%20%23333;padding:%20.2rem%20.5rem;text-align:%20left;%22%3E%3Cinput%20value=%22'+c+'%22%20style=%22background:%20transparent;border:%200;color:%20inherit;width:%20100%25%22%20onclick=%22this.select(0,this.value.length)%22%3E%3C/td%3E%3C/tr%3E')}i.innerHTML=l+p,t.push([a,i]),o+=1}if(0===o)alert('%22type=%22hidden%22%22の設定されたinputタグが見つかりませんでした');else%20for(var%20h%20in%20t)n=t[h][0],n.parentElement.insertBefore(t[h][1],n.nextSibling)}})()})()}();">input="hidden"の内容を表示</a></p>
<h2>動作デモ</h2>
<p>仮フォームを用意しました。</p>
<p>上記ブックマークレットをブックマークから実行するか、直接クリックすると、下記フォームのinput="hidden"の埋め込まれた箇所にそのデータの内容が表示されます。</p>
<form style="border: solid #999 1px; padding: 20px; margin-bottom: 20px">
<p style="text-align:center; border-bottom: 1px dotted #666; font-weight: bold;">なにも送信しないフォーム</p>
項目1<br>
<input type="text" value="テキストデータ 1"><br>
<input type="hidden" value="隠れデータ A"><br>
項目2<br>
<input type="text" value="テキストデータ 2"><br>
<input type="hidden" value="隠れデータ B"><br>
項目3<br>
<input type="text" value="テキストデータ 3"><br>
<input type="hidden" value="隠れデータ C"><br>
</form>
<h2>ブックマークレット化前のコード</h2>
<p>圧縮前の下のコードはこんな感じです。
ブックマークレットではこれを<a href="https://closure-compiler.appspot.com/home" target="_blank">Closure Compiler Service</a>でコンパイルしています。</p>
<pre class="highlight javascript"><code>
<span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// 挿入するテーブルのクラス</span>
<span class="kr">const</span> <span class="nx">tableClass</span> <span class="o">=</span> <span class="s2">"s0014com-show-hidden-data"</span><span class="p">;</span>
<span class="c1">// テーブルのスタイル</span>
<span class="kr">const</span> <span class="nx">style</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">table</span><span class="p">:</span> <span class="s2">"border-top: 1px solid #666;"</span> <span class="o">+</span>
<span class="s2">"border-right: 1px solid #666;"</span> <span class="o">+</span>
<span class="s2">"border-bottom: 0;"</span> <span class="o">+</span>
<span class="s2">"border-left: 0;"</span> <span class="o">+</span>
<span class="s2">"border-spacing: 0;"</span> <span class="o">+</span>
<span class="s2">"margin-bottom: .5rem;"</span><span class="p">,</span>
<span class="na">thead</span><span class="p">:</span> <span class="p">{</span>
<span class="na">th</span><span class="p">:</span> <span class="s2">"background: #666;"</span> <span class="o">+</span>
<span class="s2">"border-top: 0;"</span> <span class="o">+</span>
<span class="s2">"border-right: 0;"</span> <span class="o">+</span>
<span class="s2">"border-bottom: 1px solid #666;"</span> <span class="o">+</span>
<span class="s2">"border-left: 1px solid #666;"</span> <span class="o">+</span>
<span class="s2">"color: white;"</span> <span class="o">+</span>
<span class="s2">"font-size: .7rem;"</span> <span class="o">+</span>
<span class="s2">"padding: .1rem;"</span> <span class="o">+</span>
<span class="s2">"text-align: center;"</span><span class="p">,</span>
<span class="na">childOfNoDisplay</span><span class="p">:</span> <span class="s2">"background: #eee;"</span> <span class="o">+</span>
<span class="s2">"color: black;"</span><span class="p">,</span>
<span class="p">},</span>
<span class="na">tbody</span><span class="p">:</span> <span class="p">{</span>
<span class="na">th</span><span class="p">:</span> <span class="s2">"background: #999;"</span> <span class="o">+</span>
<span class="s2">"border-top: 0;"</span> <span class="o">+</span>
<span class="s2">"border-right: 0;"</span> <span class="o">+</span>
<span class="s2">"border-bottom: 1px solid #666;"</span> <span class="o">+</span>
<span class="s2">"border-left: 1px solid #666;"</span> <span class="o">+</span>
<span class="s2">"color: white;"</span> <span class="o">+</span>
<span class="s2">"padding: .2rem .5rem;"</span> <span class="o">+</span>
<span class="s2">"text-align: left;"</span><span class="p">,</span>
<span class="na">td</span><span class="p">:</span> <span class="s2">"background: white;"</span> <span class="o">+</span>
<span class="s2">"border-top: 0;"</span> <span class="o">+</span>
<span class="s2">"border-right: 0;"</span> <span class="o">+</span>
<span class="s2">"border-bottom: 1px solid #666;"</span> <span class="o">+</span>
<span class="s2">"border-left: 1px solid #666;"</span> <span class="o">+</span>
<span class="s2">"color: #333;"</span> <span class="o">+</span>
<span class="s2">"padding: .2rem .5rem;"</span> <span class="o">+</span>
<span class="s2">"text-align: left;"</span><span class="p">,</span>
<span class="na">input</span><span class="p">:</span> <span class="s2">"background: transparent;"</span> <span class="o">+</span>
<span class="s2">"border: 0;"</span> <span class="o">+</span>
<span class="s2">"color: inherit;"</span> <span class="o">+</span>
<span class="s2">"width: 100%"</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">};</span>
<span class="c1">// テーブルを表示させている場合は削除</span>
<span class="kd">function</span> <span class="nx">deleteTables</span><span class="p">(</span><span class="nx">tables</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">tables</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="nx">tables</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">i</span><span class="p">)){</span>
<span class="k">if</span><span class="p">(</span><span class="k">typeof</span> <span class="nx">tables</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">===</span> <span class="s2">"object"</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">tables</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">remove</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// parentnoedを検索してdisplay: noneの設定されたエレメントを返す</span>
<span class="kd">function</span> <span class="nx">findHideParentElms</span><span class="p">(</span><span class="nx">elm</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">hideElms</span> <span class="o">=</span> <span class="p">[];</span>
<span class="kd">function</span> <span class="nx">find</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">parent</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">parentElement</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">parentStyle</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">getComputedStyle</span><span class="p">(</span><span class="nx">parent</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">parentStyle</span><span class="p">.</span><span class="nx">display</span> <span class="o">===</span> <span class="s2">"none"</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">hideElms</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">parent</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="nx">parent</span><span class="p">.</span><span class="nx">tagName</span> <span class="o">===</span> <span class="s2">"BODY"</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">hideElms</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">find</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">parentElement</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">find</span><span class="p">(</span><span class="nx">elm</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">hideElms</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">makeTableBody</span><span class="p">(</span><span class="nx">attributes</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">tableBody</span> <span class="o">=</span> <span class="s2">""</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">attributes</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">attributes</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">i</span><span class="p">))</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">val</span> <span class="o">=</span> <span class="nx">attributes</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">value</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">name</span> <span class="o">=</span> <span class="nx">attributes</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">name</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">val</span> <span class="o">===</span> <span class="kc">undefined</span> <span class="o">||</span> <span class="nx">name</span> <span class="o">===</span> <span class="s2">"type"</span><span class="p">)</span> <span class="p">{</span>
<span class="k">continue</span><span class="p">;</span>
<span class="p">}</span>
<span class="kr">const</span> <span class="nx">allSelect</span> <span class="o">=</span> <span class="s2">"onclick=\"this.select(0,this.value.length)\""</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">thInput</span> <span class="o">=</span> <span class="err">`</span><span class="o"><</span><span class="nx">input</span> <span class="nx">value</span><span class="o">=</span><span class="s2">"${name}"</span> <span class="nx">style</span><span class="o">=</span><span class="s2">"${style.tbody.input}"</span> <span class="nx">$</span><span class="p">{</span><span class="nx">allSelect</span><span class="p">}</span><span class="o">></span><span class="err">`</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">th</span> <span class="o">=</span> <span class="err">`</span><span class="o"><</span><span class="nx">th</span> <span class="nx">style</span><span class="o">=</span><span class="s2">"${style.tbody.th}"</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">thInput</span><span class="p">}</span><span class="o"><</span><span class="sr">/th>`</span><span class="err">;
</span> <span class="kr">const</span> <span class="nx">tdInput</span> <span class="o">=</span> <span class="err">`</span><span class="o"><</span><span class="nx">input</span> <span class="nx">value</span><span class="o">=</span><span class="s2">"${val}"</span> <span class="nx">style</span><span class="o">=</span><span class="s2">"${style.tbody.input}"</span> <span class="nx">$</span><span class="p">{</span><span class="nx">allSelect</span><span class="p">}</span><span class="o">></span><span class="err">`</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">td</span> <span class="o">=</span> <span class="err">`</span><span class="o"><</span><span class="nx">td</span> <span class="nx">style</span><span class="o">=</span><span class="s2">"${style.tbody.td}"</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">tdInput</span><span class="p">}</span><span class="o"><</span><span class="sr">/td>`</span><span class="err">;
</span> <span class="nx">tableBody</span> <span class="o">+=</span> <span class="err">`</span><span class="o"><</span><span class="nx">tr</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">th</span><span class="p">}</span><span class="nx">$</span><span class="p">{</span><span class="nx">td</span><span class="p">}</span><span class="o"><</span><span class="sr">/tr>`</span><span class="err">;
</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">tableBody</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// テーブルを作成</span>
<span class="kd">function</span> <span class="nx">makeTables</span><span class="p">()</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">inputs</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="s2">"input"</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">numOfHiddenInputs</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">insertTables</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">inputs</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span> <span class="o">>=</span> <span class="mi">0</span> <span class="o">&&</span> <span class="nx">inputs</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">getAttribute</span><span class="p">(</span><span class="s2">"type"</span><span class="p">)</span> <span class="o">===</span> <span class="s2">"hidden"</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">table</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"table"</span><span class="p">);</span>
<span class="nx">table</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">"style"</span><span class="p">,</span> <span class="nx">style</span><span class="p">.</span><span class="nx">table</span><span class="p">);</span>
<span class="nx">table</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="s2">"class"</span><span class="p">,</span> <span class="nx">tableClass</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">targetElm</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">tableHeadText</span> <span class="o">=</span> <span class="s2">"Hidden data"</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">tableHeadStyle</span> <span class="o">=</span> <span class="nx">style</span><span class="p">.</span><span class="nx">thead</span><span class="p">.</span><span class="nx">th</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">parentHideElms</span> <span class="o">=</span> <span class="nx">findHideParentElms</span><span class="p">(</span><span class="nx">inputs</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">parentHideElms</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">targetElm</span> <span class="o">=</span> <span class="nx">parentHideElms</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
<span class="nx">tableHeadText</span> <span class="o">+=</span> <span class="s2">"<br>(\"display: none\"の設定されたエレメント内)"</span><span class="p">;</span>
<span class="nx">tableHeadStyle</span> <span class="o">+=</span> <span class="nx">style</span><span class="p">.</span><span class="nx">thead</span><span class="p">.</span><span class="nx">childOfNoDisplay</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">targetElm</span> <span class="o">=</span> <span class="nx">inputs</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="kr">const</span> <span class="nx">theadJsProp</span> <span class="o">=</span> <span class="s2">"onclick=\"this.parentElement.remove()\" "</span> <span class="o">+</span>
<span class="s2">"onmouseover=\"this.parentElement.style.opacity = 0.5\" "</span> <span class="o">+</span>
<span class="s2">"onmouseout=\"this.parentElement.style.opacity = 1\""</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">th</span> <span class="o">=</span> <span class="err">`</span><span class="o"><</span><span class="nx">th</span> <span class="nx">colspan</span><span class="o">=</span><span class="err">\</span><span class="s2">"2\" style=\"${tableHeadStyle}\">${tableHeadText}</th>`;
const tableHead = `<thead ${theadJsProp}><tr>${th}</tr></thead>`;
const tableBody = makeTableBody(inputs[i].attributes);
table.innerHTML = tableHead + tableBody;
insertTables.push([targetElm, table]);
numOfHiddenInputs += 1;
}
}
if (numOfHiddenInputs === 0) {
alert(`"</span><span class="nx">type</span><span class="o">=</span><span class="err">\</span><span class="s2">"hidden\""</span><span class="err">の設定された</span><span class="nx">input</span><span class="err">タグが見つかりませんでした`</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">insertTables</span><span class="p">){</span>
<span class="kr">const</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">insertTables</span><span class="p">[</span><span class="nx">i</span><span class="p">][</span><span class="mi">0</span><span class="p">];</span>
<span class="kr">const</span> <span class="nx">table</span> <span class="o">=</span> <span class="nx">insertTables</span><span class="p">[</span><span class="nx">i</span><span class="p">][</span><span class="mi">1</span><span class="p">];</span>
<span class="nx">target</span><span class="p">.</span><span class="nx">parentElement</span><span class="p">.</span><span class="nx">insertBefore</span><span class="p">(</span><span class="nx">table</span><span class="p">,</span> <span class="nx">target</span><span class="p">.</span><span class="nx">nextSibling</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">tables</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="err">`</span><span class="p">.</span><span class="nx">$</span><span class="p">{</span><span class="nx">tableClass</span><span class="p">}</span><span class="err">`</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">tables</span><span class="p">.</span><span class="nx">length</span> <span class="o">></span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">deleteTables</span><span class="p">(</span><span class="nx">tables</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">makeTables</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">main</span><span class="p">();</span>
<span class="p">})();</span>
</code></pre>
Amazon S3の静的サイトをAWS無料SSLとCludFrontでHTTPS化して爆速表示させる
https://blog.s0014.com/posts/2018-12-03-s3-https/
2018-12-03T10:55:00Z
2018-12-10T09:01:29+09:00
大石 真也(Shinya Oishi) / 大石制作
重い腰を上げて、大石制作ブログをSSL対応させました。
<p>サイトのSSL対応。一昔前は、閲覧者が個人データを送信する必要のないサイトでは、わざわざSSL対応させる必要は無く、せいぜいフォームページのみの対応で良かったものだったけど、近頃はどんなサイトでもSSL化していないと、ブラウザがアドレスバーでわざわざ「<strong>保護されていない通信</strong>」とか注意して不安を煽って来ます。</p>
<p>この「大石制作ブログ」も、これまでずっとSSL未対応で運営してきましたが、サイト制作のプロとしてお金を頂く立場である以上、それくらいやっておけよって話なので、重い腰を上げてSSL対応させる事にしました。<br>
SSL対応するだけでサイト表示がちょっと高速化したり、SEO的にも優位だったりするはずなのでがんばろうぜー……という訳で、今回はその作業メモです。</p>
<h2>目的</h2>
<ul>
<li>http通信で表示されていた大石制作ブログをSSL対応し、https通信で表示できるようにする</li>
<li>既存のhttpで表示されていたページを開くとすべてhttpsにリダイレクトされるようにする</li>
</ul>
<aside>ブログをhttps対応する上で、留意しなくてはいけないのは、<strong>はてなブックマーク数やFacebookのいいね数等は原則としてリセットされる</strong>事です。<br>一応、metaタグの情報を触れば対応も可能だけど、個人的にあまりスマートな方法とは感じないし、そこにこだわるほどの規模のサイトでもないしなと、今回は無視します。たぶんいつかサービス側で対応してくれるっしょー(適当)</aside>
<h2>現在のサイト構成</h2>
<ul>
<li>お名前.comで取得したドメインをRoute53で管理</li>
<li>ローカルで作成した静的ウェブサイト(Middleman使用)をS3にアップロードして静的ホスティング運営</li>
</ul>
<p>SSL証明書にこだわらなければ、近頃は大手レンタルサーバーで運営しているサイトとかだと、数クリックであっという間に既存サイトの無料SSL対応が出来て目的達成できるけど、このブログはレンタルサーバーではなくて<a href="https://aws.amazon.com/jp/">Amazon Web Service(AWS)</a>の<a href="https://aws.amazon.com/jp/s3/">S3</a>上に静的ホスティングしているんです。
S3だけではSSL対応出来ないので、その他のAWSと組み合わせる必要があります。</p>
<h2>手順</h2>
<ol>
<li><a href="https://aws.amazon.com/jp/certificate-manager/">AWS Certificate Manager</a>でSSL証明書を発行。</li>
<li>S3に置いてあるサイトを<a href="https://aws.amazon.com/jp/cloudfront/">Amazon CloudFront</a>を利用して閲覧できるようにする。</li>
<li>CloudFrontのディストリビューションにSSLを紐つけて、<a href="https://blog.s0014.com">https://blog.s0014.com</a> でサイトを閲覧できるようにする。</li>
<li>以降、http通信でのアクセスはhttpsにリダイレクトさせるようにする。</li>
</ol>
<p>結果的に行った手続きは簡単なんだけど、流れがわかっていないとハマりポイントが多いです。幸い、この手続きは参考となる情報はすでに色々あり、この記事よりも遥かに詳しい情報がほとんどです。<br>
ただ、AWS管理画面は頻繁に変わるので、スクリーンショット多めで新しめの情報が欲しい方には役立つはずなので、がんばっていきましょー。</p>
<h3>特にお世話になった記事</h3>
<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html">例: 独自ドメインを使用して静的ウェブサイトをセットアップする - Amazon Simple Storage Service</a></li>
<li><a href="https://blog.majimena.co.jp/tech/2016/03/31/aws-ssl.html">Amazon S3でSSL対応の静的ウェブサイトを公開する | マジメナラボ - majimena Inc.</a></li>
<li><a href="https://qiita.com/kazuhei/items/9d72b6bbd4d82abe9e9e">Route53+S3+CloudFrontでhttpsのurlをリダイレクトさせる - Qiita</a></li>
<li><a href="https://christina04.hatenablog.com/entry/2017/02/10/013211">S3 + CloudFrontにした時にハマったこと - Carpe Diem</a></li>
<li><a href="https://qiita.com/jasbulilit/items/73d70a01a5d3b520450f">CloudFrontでS3のウェブサイトをSSL化する - Qiita</a></li>
</ul>
<h3>なぜCloudFrontを利用するの?</h3>
<p>この事を調べ始めたとき、CloudFrontへの理解が足らず「なんでこんなもんを間に噛ませなあかんの?」な恥ずかしい状態でした。<br>
CloudFrontを利用する理由はこうです。</p>
<ul>
<li>S3は直接SSLを利用する事が出来ない</li>
<li>CloudFrontはSSLを利用できる</li>
</ul>
<p>……という訳で、S3に置いているコンテンツをSSLを利用できるCloudFrontを通して配信出来るようにすることで、HTTPS接続が可能になるというわけです。
CloudFrontを利用すると、<strong>コンテンツを爆速で配信できる</strong>ので、SSLを利用しない場合でも利用するメリットは大きいです。利用料金も、S3でかかっていた金額の一部がCloudFrontに充填される事になるので、通常のブログサイトでの利用では、目立って料金が上下することは無いでしょう。 </p>
<h2>やってみよう!</h2>
<h3>AWS Certificate ManagerでSSL証明書を発行</h3>
<p><img src="/posts/2018-12-03-s3-https/cm01.png" /> </p>
<p>AWSのコンソールにログインし、サービス一覧からAWS Certificate Managerを選びます。<br>
……と言ってみたけど、どんどん増えていくAWSサービスの一覧から目当てのサービスを探すのは、めちゃめちゃ大変なので、上の検索窓に「cer」と打ち込んだ方が早いですね(笑)</p>
<p><img src="/posts/2018-12-03-s3-https/cm012.png" /></p>
<h4>【ハマりポイント】リージョンをバージニア北部に変更する</h4>
<p>これが一番分かりやすいハマりポイントでしょう。日本語環境でログインしていると、画面右上のリージョンが「東京」になっているでしょう。これを「米国東部(バージニア北部)」に切り替えます。うっかりバージニア北部以外のリージョンで証明書を作ってしまうと、<strong>おめでとう!もう一度最初から証明書作りを楽しむ事ができるよ!</strong></p>
<p>公式ヘルプ: <a href="https://aws.amazon.com/jp/premiumsupport/knowledge-center/migrate-ssl-cert-us-east/" target="_blank">SSL 証明書を米国東部 (バージニア北部) リージョンに移行するどうすればいいですか。</a></p>
<p>リージョンを変更したら「証明書のプロビジョニング」の「今すぐ始める」ボタンをおして進みます。</p>
<p><img src="/posts/2018-12-03-s3-https/cm02.png" /></p>
<p>「パブリック証明書のリクエスト」を選択してリクエストボタン。<br>
<img src="/posts/2018-12-03-s3-https/cm03.png" /></p>
<p>ドメイン名の追加にSSL対応させるドメインを入力。<br>
今回は「blog.s0014.com」のみを登録したけど、たとえば「s0014.com」とwww付きの「www.s0014.com」の2つを対応させたい場合は、最初に「s0014.com」を入力してから、「この証明書に別の名前を追加」ボタンを押して次に「www.s0014.com」を追加する形で良いでしょう。ワイルドカードでの指定も出来るので、2つめは「*.s0014.com」としてしまった方がらくちんかもしれません。
<img src="/posts/2018-12-03-s3-https/cm04.png" /></p>
<h4>ドメイン所有者の検証(今回はDNS検証)</h4>
<p>「検証方法の選択」はどちらでも良いけど、僕はドメインをAWSのRoute 53で管理しているので「DNSの検証」を選ぶと楽ちんでした。
<img src="/posts/2018-12-03-s3-https/cm05.png" /></p>
<p>「確定とリクエスト」を押します。<br>
<img src="/posts/2018-12-03-s3-https/cm06.png" /></p>
<p>検証ドメインの「▶」を押して開きます。<br>
<img src="/posts/2018-12-03-s3-https/cm08.png" /></p>
<p>「Route 53でのレコードを作成」を押して、らくちん認証に入ります。<br>
<img src="/posts/2018-12-03-s3-https/cm09.png" /></p>
<p>そのまま「作成」を押すと、わざわざRoute 53に移動しなくても、勝手に検証用のCNAMEレコードが登録されます。<br>
あとでRoute 53で確認すると、ごちゃごちゃの文字列の入ったCNAMEレコードか確認できるはずです。
<img src="/posts/2018-12-03-s3-https/cm10.png" /></p>
<p>「状況」が「検証保留中」になります。<br>
「お客さまのアクションは必要ありません」と書いてあるとおり、少し待つだけです。待ち時間は数十分とか数時間かかる例もあるようですが、今回は1分も掛かりませんでした。<br>
このまま次のCloudFrontの設定に移ってしまっても大丈夫でしょう。
<img src="/posts/2018-12-03-s3-https/cm12.png" /></p>
<p>ページを再読込して「状況」が「発行済み」に変わってたら、AWS Certificate Managerでの証明書が無事に発行された事になります。<br>
<img src="/posts/2018-12-03-s3-https/cm13.png" /></p>
<h3>S3に置いている静的サイトをCloudFrontを利用して表示できるようにする</h3>
<p>次はCloudFrontでの作業です。これもサービス一覧から「CloudFront」の文字を探すよりも……
<img src="/posts/2018-12-03-s3-https/cf01.png" /></p>
<p>検索窓に「fro」と打ち込んだ方が手っ取り早いです。
<img src="/posts/2018-12-03-s3-https/cf012.png" /></p>
<p>CloudFrontではリージョンの概念が無いので、右上のリージョン欄は「グローバル」になってます。<br>
「Create Distribution」を押します。<br>
<img src="/posts/2018-12-03-s3-https/cf02.png" /></p>
<p>「Web」の「Get Standard」を押します。<br>
<img src="/posts/2018-12-03-s3-https/cf03.png" /></p>
<p>S3で静的サイトホスティングでサイト運営しているので、「Origin Domain Name」を選ぶとS3のバケットが表示されます。「blog.s0014.com」のバケットを選びます。<br>
<img src="/posts/2018-12-03-s3-https/cf04.png" /></p>
<p>バケットを選択すると「Origin ID」には自動で入力されるはずです。
<img src="/posts/2018-12-03-s3-https/cf06.png" /></p>
<p>次の「Default Cache Behavior Settings」の「Viewer Protocol Policy」で「Redirect HTTP to HTTPS」を選択します。
こうすると設定完了後、「<strong>http</strong>://blog.s0014.com」へのアクセスは自動で「<strong>https</strong>://blog.s0014.com」にリダイレクトされます。
<img src="/posts/2018-12-03-s3-https/cf062.png" /></p>
<p>少し下がって「Distribution Settings」です。<br>
「Alternate Domain Names」には「blog.s0014.com」とそのままドメイン名を入力します。<br>
「SSL Certificate」は「Custom SSL Certificate (example.com):」を選び、テキストエリアをクリックすると、Certificate Managerで作成した証明書が表示されるはずなので選択します。
<img src="/posts/2018-12-03-s3-https/cf072.png" /></p>
<p>さらにもうちょっと下の「Default Root Object」にはS3で設定している静的サイトのインデックスドキュメントのファイル名を入力します。<br>
ほとんどの場合「index.html」になるでしょう。<br>
<img src="/posts/2018-12-03-s3-https/cf08.png" />
<aside>
インデックスドキュメントのファイル名はS3のバケットプロパティから確認できます。
<img src="/posts/2018-12-03-s3-https/cf052.png">
</aside></p>
<h3>Route53のレコードを変更</h3>
<p>最後の仕上げです。今回は最初からS3とRoute53で静的サイトホスティングで運営している前提での流れなので、基本的な説明は端折ります。</p>
<p>Route53の「s0014.com」のHosted Zoneを開き、「blog.s0014.com」レコードセットを選択し、「Alias Target」を変更します。<br>
今まではS3のバケットが選択されていたので、これを「CloudFront Distributions」の、さっき作成したディストリビューションを選択して保存します。
<img src="/posts/2018-12-03-s3-https/r01.png" /></p>
<p>これで準備完了!あとは確認です。</p>
<h2>ついに我が家にhttpsがやってきた!</h2>
<p>……いけましたね!ついにウチもhttpsデビューだ!<br>
今回の環境では特に待ち時間もなく、設定は即時に反映されて無事にhttps接続でサイトが開きました。
<img src="/posts/2018-12-03-s3-https/https.png" /></p>
<h3>忘れずに利用サービスの登録情報を変更しよう</h3>
<p>Googleアナリティクスなど、サイト運営で他に利用しているサービスがあれば、URLの登録情報を忘れずに変更しておきます。<br>
<img src="/posts/2018-12-03-s3-https/ana.png" /></p>
<h2>CloudFrontのおかげで表示速度も短縮</h2>
<p>表示速度もかなり早くなりました。<br>
このサイトは、もともとS3の静的サイトホスティング運営しており、その性能に甘えて大した速度改善の努力をせずとも、良好な速度(トップページなら2秒強程度)でしたが、今回からCloudFrontを利用することで、更に速くなりました。まだ変更したばかりなので、多少のばらつきはありますが、概ね1秒台になったでしょうか。</p>
<p>……という訳で、無事にSSLを利用してhttps通信に対応できました。心理的に抵抗が大きくて腰が重かった作業だけど、やってよかったです。<br>
最初、CloudFrontを間に噛ませる感覚に慣れなくて戸惑ったけど、その効果もかなり有効に感じる良いサービスでした。</p>
<p>S3で静的ホスティングサイトを運営している人にはデメリットがほぼ無い作業なので、ぜひSSL対応に挑戦してみてはどうでしょう?</p>
さくらのレンタルサーバーにPythonのpipとAWS Command Line Interface(aws-cli)をインストールする
https://blog.s0014.com/posts/2018-03-09-sakura-s3-cli/
2018-03-09T11:05:00Z
2018-12-03T19:57:55+09:00
大石 真也(Shinya Oishi) / 大石制作
さくらインターネットのレンタルサーバーのcshでAWSのコマンドラインツールを使えるようにする手順です
<h2>さくらのレンタルサーバにpipとawsのコマンドを使えるようにしたい</h2>
<p><a href="//px.a8.net/svt/ejp?a8mat=1U7RDK+8J7OXE+D8Y+67RK2" target="_blank">さくらのレンタルサーバー</a>は安くて信頼性も高く、SSH接続もスタンダード以上のプランだと使えたりと、もの凄く良いサービスだと思うけど、サーバー環境は結構癖が強いです(笑)</p>
<p>好んで利用ているサーバーのひとつだけど、凝った事をしようとすると、結構ハマります。<br>
今回の件だと、OSがFreeBSDで、標準のシェルがcshだったり、Pythonのバージョンが古かったりとか……。 </p>
<p>という訳で、今回はさくらのレンタルサーバーでaws-cliを設定してS3を外部バックアップ先として送信できるようにできるまでの作業メモを整理してみました。<br>
ちゃんとやれば、これと言ったハマりポイントもなく、スムーズに設定できるはずです。</p>
<aside><p>バックアップをS3とかの外部サービスに送る事にこだわりがなければ、さくら内でバックアップを取ってくれるサービスが最近はじまったので、そちらを利用するのも手です。</p>
<p><a href="https://www.sakura.ad.jp/news/sakurainfo/newsentry.php?id=1848" target="_blank">さくらのレンタルサーバ/マネージドサーバ 「バックアップ&ステージング powered by Snapup」提供開始のお知らせ</a></p>
<p>毎日や毎週、自動でバックアップを取って残す設定も簡単にできちゃいます。</p></aside>
<h2>Pythonのpipをインストールする</h2>
<p>以下、さくらのレンタルサーバーにSSH接続している事が前提のコマンド操作になります。</p>
<h3>Pythonのバージョン確認</h3>
<p>執筆時のPythonの最新バージョンは3.6.4なので、それよりバージョンは古いけど、今回の用途では問題なく動きます。</p>
<pre class="highlight shell"><code><span class="gp">% </span>python -V
Python 2.7.6
</code></pre>
<h3>~/.pydistutils.cfgを作る</h3>
<p>Pythonの設定ファイル「~/.pydistutils.cfg」を用意し、インストールできる権限を付与しておきます。<br>
中身はこんな感じです。</p>
<pre class="highlight plaintext"><code>[install]
user=1
</code></pre>
<p>~/.cshrc を編集し「set path」の行に「$HOME/.local/bin」を追加します。</p>
<p>たぶん、最初はこんな感じなので……</p>
<pre class="highlight plaintext"><code>set path = (/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin $HOME/bin)
</code></pre>
<p>……こんな感じにしておきます。</p>
<pre class="highlight plaintext"><code>set path = (/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin $HOME/bin $HOME/.local/bin)
</code></pre>
<h3>pipをインストール</h3>
<p>aws-cliをインストールするために、eash_installコマンドでpipをインストールします。
警告がでるけど、今回の用途では特に問題ありませんでした。</p>
<pre class="highlight shell"><code><span class="gp">% </span>easy_install pip
Searching <span class="k">for </span>pip
Reading https://pypi.python.org/simple/pip/
Best match: pip 9.0.1
Downloading https://pypi.python.org/packages/11/b6/abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/pip-9.0.1.tar.gz#md5<span class="o">=</span>35f01da33009719497f01a4ba69d63c9
Processing pip-9.0.1.tar.gz
Writing /tmp/easy_install-pMsKpD/pip-9.0.1/setup.cfg
Running pip-9.0.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-pMsKpD/pip-9.0.1/egg-dist-tmp-HDw3FZ
/usr/local/lib/python2.7/distutils/dist.py:267: UserWarning: Unknown distribution option: <span class="s1">'python_requires'</span>
warnings.warn<span class="o">(</span>msg<span class="o">)</span>
warning: no previously-included files found matching <span class="s1">'.coveragerc'</span>
warning: no previously-included files found matching <span class="s1">'.mailmap'</span>
warning: no previously-included files found matching <span class="s1">'.travis.yml'</span>
warning: no previously-included files found matching <span class="s1">'.landscape.yml'</span>
warning: no previously-included files found matching <span class="s1">'pip/_vendor/Makefile'</span>
warning: no previously-included files found matching <span class="s1">'tox.ini'</span>
warning: no previously-included files found matching <span class="s1">'dev-requirements.txt'</span>
warning: no previously-included files found matching <span class="s1">'appveyor.yml'</span>
no previously-included directories found matching <span class="s1">'.github'</span>
no previously-included directories found matching <span class="s1">'.travis'</span>
no previously-included directories found matching <span class="s1">'docs/_build'</span>
no previously-included directories found matching <span class="s1">'contrib'</span>
no previously-included directories found matching <span class="s1">'tasks'</span>
no previously-included directories found matching <span class="s1">'tests'</span>
Adding pip 9.0.1 to easy-install.pth file
Installing pip script to /home/[アカウントID]/.local/bin
Installing pip2.7 script to /home/[アカウントID]/.local/bin
Installing pip2 script to /home/[アカウントID]/.local/bin
Installed /home/[アカウントID]/.local/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg
Processing dependencies <span class="k">for </span>pip
Finished processing dependencies <span class="k">for </span>pip
</code></pre>
<p>この状態でpipコマンドを打ってみても、たぶんそんなコマンドねーよと怒られます。</p>
<pre class="highlight shell"><code><span class="gp">% </span>pip -V
pip: Command not found.
</code></pre>
<p>……なので、sourceコマンドで再読込します。</p>
<pre class="highlight shell"><code><span class="gp">% </span><span class="nb">source</span> ~/.cshrc
</code></pre>
<p>いけましたね。</p>
<pre class="highlight shell"><code><span class="gp">% </span>pip -V
pip 9.0.1 from /home/[アカウントID]/.local/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg <span class="o">(</span>python 2.7<span class="o">)</span>
</code></pre>
<h2>aws-cliをインストール</h2>
<p>あとはAWSのドキュメント
「<a href="https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/installing.html" target="_blank">AWS Command Line Interface のインストール</a>」の手順にしたがって、インストールします。</p>
<pre class="highlight shell"><code><span class="gp">% </span>pip install awscli --upgrade --user
Collecting awscli
/home/[アカウントID]/.local/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py:318: SNIMissingWarning: An HTTPS request has been made, but the SNI <span class="o">(</span>Subject Name Indication<span class="o">)</span> extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/security.html#snimissingwarning.
SNIMissingWarning
/home/[アカウントID]/.local/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A <span class="nb">true </span>SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/security.html#insecureplatformwarning.
InsecurePlatformWarning
Downloading awscli-1.14.32-py2.py3-none-any.whl <span class="o">(</span>1.2MB<span class="o">)</span>
100% |████████████████████████████████| 1.2MB 619kB/s
Collecting docutils><span class="o">=</span>0.10 <span class="o">(</span>from awscli<span class="o">)</span>
Downloading docutils-0.14-py2-none-any.whl <span class="o">(</span>543kB<span class="o">)</span>
100% |████████████████████████████████| 552kB 1.4MB/s
Collecting colorama<<span class="o">=</span>0.3.7,><span class="o">=</span>0.2.5 <span class="o">(</span>from awscli<span class="o">)</span>
Downloading colorama-0.3.7-py2.py3-none-any.whl
Collecting PyYAML<<span class="o">=</span>3.12,><span class="o">=</span>3.10 <span class="o">(</span>from awscli<span class="o">)</span>
Downloading PyYAML-3.12.tar.gz <span class="o">(</span>253kB<span class="o">)</span>
100% |████████████████████████████████| 256kB 3.6MB/s
Collecting <span class="nv">botocore</span><span class="o">==</span>1.8.36 <span class="o">(</span>from awscli<span class="o">)</span>
Downloading botocore-1.8.36-py2.py3-none-any.whl <span class="o">(</span>4.1MB<span class="o">)</span>
100% |████████████████████████████████| 4.1MB 215kB/s
Collecting s3transfer<0.2.0,><span class="o">=</span>0.1.12 <span class="o">(</span>from awscli<span class="o">)</span>
Downloading s3transfer-0.1.12-py2.py3-none-any.whl <span class="o">(</span>59kB<span class="o">)</span>
100% |████████████████████████████████| 61kB 4.4MB/s
Collecting rsa<<span class="o">=</span>3.5.0,><span class="o">=</span>3.1.2 <span class="o">(</span>from awscli<span class="o">)</span>
Downloading rsa-3.4.2-py2.py3-none-any.whl <span class="o">(</span>46kB<span class="o">)</span>
100% |████████████████████████████████| 51kB 6.9MB/s
Collecting python-dateutil<3.0.0,><span class="o">=</span>2.1 <span class="o">(</span>from <span class="nv">botocore</span><span class="o">==</span>1.8.36->awscli<span class="o">)</span>
Downloading python_dateutil-2.6.1-py2.py3-none-any.whl <span class="o">(</span>194kB<span class="o">)</span>
100% |████████████████████████████████| 194kB 3.8MB/s
Collecting jmespath<1.0.0,><span class="o">=</span>0.7.1 <span class="o">(</span>from <span class="nv">botocore</span><span class="o">==</span>1.8.36->awscli<span class="o">)</span>
Downloading jmespath-0.9.3-py2.py3-none-any.whl
Collecting futures<4.0.0,><span class="o">=</span>2.2.0; python_version <span class="o">==</span> <span class="s2">"2.6"</span> or python_version <span class="o">==</span> <span class="s2">"2.7"</span> <span class="o">(</span>from s3transfer<0.2.0,><span class="o">=</span>0.1.12->awscli<span class="o">)</span>
Downloading futures-3.2.0-py2-none-any.whl
Collecting pyasn1><span class="o">=</span>0.1.3 <span class="o">(</span>from rsa<<span class="o">=</span>3.5.0,><span class="o">=</span>3.1.2->awscli<span class="o">)</span>
Downloading pyasn1-0.4.2-py2.py3-none-any.whl <span class="o">(</span>71kB<span class="o">)</span>
100% |████████████████████████████████| 71kB 6.6MB/s
Collecting six><span class="o">=</span>1.5 <span class="o">(</span>from python-dateutil<3.0.0,><span class="o">=</span>2.1->botocore<span class="o">==</span>1.8.36->awscli<span class="o">)</span>
Downloading six-1.11.0-py2.py3-none-any.whl
Installing collected packages: docutils, colorama, PyYAML, six, python-dateutil, jmespath, botocore, futures, s3transfer, pyasn1, rsa, awscli
Running setup.py install <span class="k">for </span>PyYAML ... <span class="k">done
</span>Successfully installed PyYAML-3.12 awscli-1.14.32 botocore-1.8.36 colorama-0.3.7 docutils-0.14 futures-3.2.0 jmespath-0.9.3 pyasn1-0.4.2 python-dateutil-2.6.1 rsa-3.4.2 s3transfer-0.1.12 six-1.11.0
/home/[アカウントID]/.local/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A <span class="nb">true </span>SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.io/en/latest/security.html#insecureplatformwarning.
InsecurePlatformWarning
</code></pre>
<p>もう一回sourceコマンドを入れます。</p>
<pre class="highlight shell"><code><span class="gp">% </span><span class="nb">source</span> ~/.cshrc
</code></pre>
<p>aws-cliのコマンドが使えようになりました。</p>
<pre class="highlight shell"><code><span class="gp">% </span>aws --version
aws-cli/1.14.32 Python/2.7.6 FreeBSD/9.1-RELEASE-p24 botocore/1.8.36
</code></pre>
<h2>ついでにaws-cliでS3を操作してみる</h2>
<p>MacやLinuxにインストールしたaws-cli同様に、さくらのレンタルサーバーでも問題なく動きました。</p>
<p>ついでに初期設定とS3を操作する様子をちょっと紹介します。</p>
<h3>設定</h3>
<p>あらかじめ「<a href="https://docs.aws.amazon.com/ja_jp/general/latest/gr/managing-aws-access-keys.html" target="_blank">AWS アカウントのアクセスキー管理</a>」の記事を参考にアクセスキーIDとシークレットアクセスキーを用意しておきます。</p>
<p>用意できたら、
「<a href="https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-getting-started.html" target="_blank">AWS CLI の設定</a>」の記事を見ながら入力します。</p>
<pre class="highlight shell"><code><span class="gp">% </span>aws configure
AWS Access Key ID <span class="o">[</span>None]: <span class="o">[</span>アクセスキーIDを入力]
AWS Secret Access Key <span class="o">[</span>None]: <span class="o">[</span>シークレットアクセスキーを入力]
Default region name <span class="o">[</span>None]: <span class="o">[</span>リージョン名を入力。東京ならap-northeast-1]
Default output format <span class="o">[</span>None]: <span class="o">[</span>出力フォーマットを入力。jsonとかtextとか]
</code></pre>
<h3>S3のバケットを作る</h3>
<p>今回はwwwディレクトリをS3にまるごと送ってバックアップがしたかったんです。<br>
S3は格安でバージョニングまで対応されてるのでこういった用途にぴったりですね。</p>
<p>「<a href="https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/BucketRestrictions.html" target="_blank">バケットの制約と制限</a>」の記事を参考にバケットの作成をします。</p>
<pre class="highlight shell"><code><span class="gp">% </span>aws s3 mb s3://[バケット名を入力]
</code></pre>
<p>バケットの作成に成功していたら、lsで表示されます。</p>
<pre class="highlight shell"><code><span class="gp">% </span>aws s3 ls
2018-01-23 12:34:56 <span class="o">[</span>バケット名]
</code></pre>
<h3>バケットをバージョニングに対応させる</h3>
<p>バージョニング対応もコマンドでできます。<br>
「<a href="https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-versioning.html" target="_blank">put-bucket-versioning(英語のみ)</a>」を見ながら、こんなコマンドで設定しました。</p>
<pre class="highlight shell"><code><span class="gp">% </span>aws s3api put-bucket-versioning --bucket <span class="o">[</span>バケット名を入力。<span class="s2">"s3://"</span>は付けない] --versioning-configuration <span class="nv">Status</span><span class="o">=</span>Enabled
</code></pre>
<h3>S3にファイル送信</h3>
<p>最後にファイルを送信します。今回はバージョニングをしているバケットなので、sync(同期するコマンド)でいきます。<br>
wwwディレクトリ以下をどかんと送ってましいます。</p>
<pre class="highlight shell"><code><span class="gp">% </span>aws s3 sync ~/www/ s3://[バケット名]
</code></pre>
<p>wwwディレクトリの大きさによっては、しばらく待つ必要があるけど、結構早いはずです。<br>
syncで送っているので、もう一度実行したら、差分だけの送信になります。 </p>
<p>–deleteオプションをつけると、wwwディレクトリで削除したファイルをs3内でも削除することができます。</p>
<pre class="highlight shell"><code><span class="gp">% </span>aws s3 sync ~/www/ s3://[バケット名] --delete
</code></pre>
<p>バージョニングをしていると、間違った変更をしてしまったり、必要なファイルを削除してしまった時も安心ですね。</p>
<p>このコマンドを定期実行させれば、手軽にさくらのレンタルサーバー外に送る事が出来ますね。
「crontab -e」のコマンドでも、サーバーコントロールパネルからでも設定しておきましょう。</p>
<p>こんな感じで、一回準備してしまえば、あとはいつでもawsの機能をさくらのレンタルサーバーで使えるので楽ちんです。</p>
<p>こうやってAWSとさくらのレンタルサーバーを上手く組み合わせれば、おこづかいレベルのランニングコストで結構凝ったサービスとか作れそうですね。(たぶんやらない)</p>
【iOS/Android/PC対応】ページを開くと動画を自動再生させる方法を調べてみた
https://blog.s0014.com/posts/2017-12-05-autoplay/
2017-12-04T16:00:00Z
2018-11-15T03:15:26+09:00
大石 真也(Shinya Oishi) / 大石制作
ちょっとした事なんだけど、調べ物の検証に随分時間を使ってしまったので、記事化しておいたら役に立つ人もいるかなと。
<h2>はじまり・ページを開くとYouTube埋め込み動画を自動再生させる方法?</h2>
<p>動画を自動再生させる事自体の行儀良し悪しについては、ここでは置いておきます。
とりあえず、僕個人は特にモバイル機器ではユーザーが望まないタイミングで<strong>ギガを消費</strong>させるのは(言ってみたかっただけ)サイトの印象悪化につながりかねないので、やめておくのが無難だと考えてます。</p>
<p>所要で、ウェブページに埋め込まれたYouTubeの動画を自動再生出来る方法を調べる機会があったので、その調査のメモです。</p>
<h2>先に結論・全環境対応させるには、多分videoタグを使うしか無い(消音限定)</h2>
<p>執筆日の時点では、iOS/Android/PCの全環境で<a href="https://www.youtube.com/" target="_blank">YouTube</a>の自動再生は多分無理です。
<a href="https://help.vimeo.com/hc/ja/articles/115004485728-%E5%9F%8B%E3%82%81%E8%BE%BC%E3%81%BF%E5%8B%95%E7%94%BB%E3%81%AE%E8%87%AA%E5%8B%95%E5%86%8D%E7%94%9F%E3%81%A8%E3%83%AB%E3%83%BC%E3%83%97" target="_blank">vimeo(ヘルプセンター)</a>や<a href="https://developer.dailymotion.com/player/faq" target="_blank">DailyMotion(FAQ)</a>等、他の動画サービスでも難しそうです。
自分のサーバー等に動画ファイルをアップロードして、そのURLを指定して<strong>videoタグを使うしかありません。</strong></p>
<p>具体的には……</p>
<pre class="highlight html"><code><span class="nt"><video</span> <span class="na">src=</span><span class="s">"動画のURL"</span> <span class="na">muted</span> <span class="na">autoplay</span><span class="nt">></video></span>
</code></pre>
<p>……と記述するこで、どの環境でも共通してvideoタグで指定した動画を自動再生させる事ができます。
なお、iOS/Androidのモバイル機器のブラウザでの自動再生には<strong>「muted」属性の指定が必須です。自動再生で音を出す事はできません。</strong></p>
<p>これも、<em>最近のバージョンのブラウザ限定</em>となります。検索してみても、少し前の記事までは、videoタグを使っても不可能な旨の記事がわんさか出てきます。
消音限定ながらもiOS/Androidで自動再生できるようになったのは、最近のブラウザからとなります。</p>
<h2>Android以外ならvideoタグでなくても自動再生できる</h2>
<h3>PC限定なら公式の埋め込みコードベースでOK</h3>
<p>たとえば、<a href="/posts/2017-12-05-awamori">前回の記事</a>で埋め込んだ、僕が描いている様子を載せたYouTube動画の場合、こんなコードになります。</p>
<pre class="highlight html"><code><span class="nt"><iframe</span> <span class="na">width=</span><span class="s">"560"</span> <span class="na">height=</span><span class="s">"315"</span>
<span class="na">src=</span><span class="s">"https://www.youtube.com/embed/2wsQQgsXagc?rel=0"</span>
<span class="na">frameborder=</span><span class="s">"0"</span> <span class="na">allowfullscreen</span><span class="nt">></iframe></span>
</code></pre>
<p>普通にYouTubeの動画ページの「共有>埋め込む」と選んで出て来るコードですね。</p>
<iframe width="560" height="315"
src="https://www.youtube.com/embed/2wsQQgsXagc?rel=0"
frameborder="0" allowfullscreen></iframe>
<p>このコードで自動再生に対応させると、こうなります。</p>
<pre class="highlight html"><code><span class="nt"><iframe</span> <span class="na">width=</span><span class="s">"560"</span> <span class="na">height=</span><span class="s">"315"</span>
<span class="na">src=</span><span class="s">"https://www.youtube.com/embed/2wsQQgsXagc?rel=0&autoplay=1"</span>
<span class="na">frameborder=</span><span class="s">"0"</span> <span class="na">allowfullscreen</span><span class="nt">></iframe></span>
</code></pre>
<p>src属性で指定するURLに「autoplay=1」の パラメータを追加する事で、PCでは自動再生されます。ただし、これではiOS/Androidでは自動再生されません。</p>
<h3>YouTubeのIFrame APIを使えばiOS/PCまでは対応できる(消音限定)</h3>
<p>埋め込みiframeタグではなく、YouTubeが用意している<a href="https://developers.google.com/youtube/iframe_api_reference?hl=ja" target="_blank">IFrame API</a>を使えば、iOSでも自動再生できます。(<strong>もちろん消音限定</strong>)</p>
<p>こんな感じの記述で実行できます。
PCのみの埋め込みiframeタグよりもかなり自由に挙動を設定出来るのが大きなメリットです。</p>
<pre class="highlight html"><code><span class="nt"><div</span> <span class="na">id=</span><span class="s">"auto-play-video"</span><span class="nt">></span><span class="c"><!-- このDIVボックス内にビデオが埋め込まれる --></span><span class="nt"></div></span>
<span class="nt"><script></span>
<span class="kr">const</span> <span class="nx">videoId</span> <span class="o">=</span> <span class="s2">"動画ID"</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">videoPlayDivId</span> <span class="o">=</span> <span class="s2">"auto-play-video"</span><span class="p">;</span>
<span class="c1">// IFrame APIの読み込み</span>
<span class="kr">const</span> <span class="nx">scriptElm</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"script"</span><span class="p">);</span>
<span class="nx">scriptElm</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="s2">"https://www.youtube.com/iframe_api"</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">headElm</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s2">"head"</span><span class="p">);</span>
<span class="nx">headElm</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">scriptElm</span><span class="p">);</span>
<span class="c1">// iframeで埋め込まれるプレイヤーの設定</span>
<span class="kd">function</span> <span class="nx">onYouTubeIframeAPIReady</span><span class="p">()</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">player</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">YT</span><span class="p">.</span><span class="nx">Player</span><span class="p">(</span><span class="nx">videoPlayDivId</span><span class="p">,</span> <span class="p">{</span>
<span class="na">playerlets</span> <span class="p">:</span> <span class="p">{</span>
<span class="na">playlist</span> <span class="p">:</span> <span class="nx">videoId</span><span class="p">,</span>
<span class="na">autoplay</span> <span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="p">},</span>
<span class="na">events</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">"onReady"</span><span class="p">:</span> <span class="nx">onPlayerReady</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="c1">// プレイヤーの準備完了時に実行される関数</span>
<span class="kd">function</span> <span class="nx">onPlayerReady</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">player</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span>
<span class="nx">player</span><span class="p">.</span><span class="nx">mute</span><span class="p">();</span> <span class="c1">// 消音しないと自動再生されない</span>
<span class="nx">player</span><span class="p">.</span><span class="nx">playVideo</span><span class="p">();</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre>
<p>なお、このコードでは、<strong>ページの読み込み時のスクロール位置の画面内に動画が入っていないと自動再生が開始しましせん。</strong>
もし、ページの下の方に置いたものを自動再生させたい場合は、スクロールイベントを取得して実行するなどの一工夫が必要になります。</p>
<h3>iOSができて、Androidができない事があったなんて……!</h3>
<p>ChromeならiOSもAndroidも同じ動作をするはず……<a href="https://www.google.co.jp/search?safe=off&tbm=isch&sa=1&ei=XkUlWqCwHMPH0AS4prjYBQ&q=%E3%81%9D%E3%82%93%E3%81%AA%E3%81%B5%E3%81%86%E3%81%AB%E8%80%83%E3%81%88%E3%81%A6%E3%81%84%E3%81%9F%E6%99%82%E6%9C%9F%E3%81%8C%E4%BF%BA%E3%81%AB%E3%82%82%E3%81%82%E3%82%8A%E3%81%BE%E3%81%97%E3%81%9F&oq=%E3%81%9D%E3%82%93%E3%81%AA%E3%81%B5%E3%81%86%E3%81%AB%E8%80%83%E3%81%88%E3%81%A6%E3%81%84%E3%81%9F%E6%99%82%E6%9C%9F%E3%81%8C%E4%BF%BA%E3%81%AB%E3%82%82%E3%81%82%E3%82%8A%E3%81%BE%E3%81%97%E3%81%9F&gs_l=psy-ab.3.0.0j0i30k1j0i24k1.34676.38981.0.40984.21.20.1.0.0.0.164.2286.10j9.19.0....0...1c.1j4.64.psy-ab..9.1.161....0.ef6OD2s7L58" target="_blank">そんなふうに考えていた時期が俺にもありました。</a></p>
<p>このコードをiOSのSafari/Chrome環境で実行し、無事に再生された時「やった!これでAndroidでもYouTubeを自動再生できる!」と喜んだのだけど、実際試して調べると、意外にAndroidでは自動再生することが出来ませんでした。</p>
<p>iOSで出来てAndroidで出来ないことなんて、ほぼ無いと思っていたけど、意外な落とし穴で、調べるのに随分時間がかかってしまいました。</p>
<h3>まとめるとこんな感じ</h3>
<p>ブラウザのバージョンはいずれも執筆時点での最新版です。</p>
<table>
<tr>
<th> </th>
<th>YouTube埋め込みiframeタグ<br>(vimeo、dailymotionも同様)</th>
<th>YouTube IFrame API使用</th>
<th>videoタグ</th>
</tr>
<tr>
<td>PC(Chrome)</td>
<td class="text-center">○</td>
<td class="text-center">○</td>
<td class="text-center">○</td>
</tr>
<tr>
<td>iOS(Chrome/Safari)</td>
<td class="text-center">×</td>
<td class="text-center">△<br>(消音なら可)</td>
<td class="text-center">△<br>(消音なら可)</td>
</tr>
<tr>
<td>Android(Chrome)</td>
<td class="text-center">×</td>
<td class="text-center">×</td>
<td class="text-center">△<br>(消音なら可)</td>
</tr>
</table>
<p>とりあえず、今日現在は全環境で動画の自動再生を完全に行う事は無理で、かなり制約がある事がわかりました。</p>
泡盛のラベルデザイン・似顔絵50人入りイラストの制作
https://blog.s0014.com/posts/2017-12-04-awamori/
2017-12-03T16:00:00Z
2018-05-23T16:19:20+09:00
大石 真也(Shinya Oishi) / 大石制作
「泡盛でカリー!ブレンド」と言うお酒のラベルデザイン・イラスト制作の流れの紹介です。「カリー」とは沖縄弁で「乾杯」の意味です。
<p>(今回の記事で使われている写真は、伊藤さんの許可の元、<a href="//awamori-meister.110office.com/" target="_blank">サイト</a>や<a href="//www.facebook.com/kaoru.itou.16" target="_blank">facebook</a>の投稿から利用させていただいてます。)</p>
<h2>司法書士さんからお酒のラベルデザインを依頼されるふしぎ</h2>
<p>縁あって仲良くさせていただいている、大阪・北浜の「<a href="//110office.com/" target="_blank">いとう事務所</a>」の伊藤薫さんは、遺言書、相続関係を中心に活躍している司法書士/行政書士さんです。</p>
<p>通常の法律家としての業務に加え、『終活』の啓蒙活動にも積極的で、エンディングノートに関するセミナー活動、<a href="//110office.com/category/endingnote/" target="_blank">サイト内での講座記事</a>も充実していたりと、柔らかい物腰と打って変わって大変熱いかたです。</p>
<p>そんな伊藤さんには、もう一つの顔があります。<br>
お酒の泡盛好きが高じて、<em>ワインのソムリエの泡盛版である「<strong>泡盛マイスター</strong>」</em>なる資格を持ち、こちらでの活動にも力を入れています。</p>
<div class="row-flex" style="justyfy-content: space-between">
<figure style="flex-basis: 350px">
<a href="//awamori-meister.110office.com/" target="_blank"><img src="/posts/2017-12-04-awamori/01.jpg" alt=""></a>
<figcaption>泡盛マイスターとしてのサイト<a href="//awamori-meister.110office.com/" target="_blank">「大阪の泡盛マイスター・伊東薫の『泡盛の楽しみ方は無限大』」</a></figcaption>
</figure>
<figure style="flex-basis: 350px">
<a href="//110office.com/" target="_blank"><img src="/posts/2017-12-04-awamori/02.jpg" alt=""></a>
<figcaption><strong>本業</strong>の法律家としてのサイト<a href="//110office.com/" target="_blank">「司法書士/行政書士 いとう事務所」</a></figcaption>
</figure>
</div>
<p>その力の入れっぷりは、本業のサイトとは別に、<a href="//awamori-meister.110office.com/" target="_blank">泡盛マイスターとしての活動サイト</a>を立ち上げてしまったり、<a href="//www.facebook.com/groups/1030501420302105/" target="_blank">Facebook上で泡盛の同好グループを主宰</a>してたりと、こちらも熱いです。</p>
<figure>
<div class="row-flex" style="justify-content: space-between;">
<div style="flex-basis: 1"><img src="/posts/2017-12-04-awamori/03.jpg" alt=""></div>
<div style="flex-basis: 1"><img src="/posts/2017-12-04-awamori/04.jpg" alt=""></div>
<div style="flex-basis: 1"><img src="/posts/2017-12-04-awamori/05.jpg" alt=""></div>
</div>
<figcaption>日本泡盛マイスター技能競技大会の全国2位(2015年)に輝き、デパートの沖縄物産展でも沖縄から来られたスタッフに混じって泡盛セミナーを行われたりと、泡盛の伝道師として沖縄人以上に大活躍されている印象があります。<br><br>僕にとって「泡盛マイスター」と言えば、黄色いハッピを着た伊藤さんなんだけど、他の多くのマイスターは普通の格好をしている様です(笑)</figcaption>
</figure>
<h3>泡盛好きが高じてオリジナル泡盛を作る事を決心</h3>
<p>そんな伊藤さんのモチベーションは、大好きな泡盛の素晴らしさをもっと世の中に広めて親しんで貰いたい事にあります。</p>
<p>泡盛は、<em>焼酎と大差無いアルコール度数</em>なのに、沖縄の人の大酒飲みのイメージと合わさって、妙に強くてキツイお酒というイメージが先行してか、あまり飲まれていないのが実情のようです。たしかに飲食店でも焼酎等に比べて、あまり取り扱われているイメージはありませんよね。</p>
<aside>沖縄でも泡盛は水割りで飲むのが基本らしいです。逆に他の地域の人ほど、ストレートやロックで飲んでダウンするから余計に強くてきついお酒のイメージが付いているのだとか。</aside>
<p>しかし実際はそんな事はなく、泡盛がもっとみんなに親しんで貰える魅力的なお酒である事を知ってもらえるような商品を自分で作ろうと思い立つ事から、今回のプロジェクトが始まり、ラベルのザイン役として僕に依頼頂きました。</p>
<figure>
<img src="/posts/2017-12-04-awamori/07.jpg" alt="">
<figcaption>沖縄の新聞でも取り上げられました。<br>こういった試みはなかなか無いようです。</figcaption>
</figure>
<h3>オリジナルのお酒を作って売る、とんでもなく高いハードル</h3>
<p>今回の話をいただいた時、最初「<em>自分が好きで作った<strong>音楽</strong>を皆に聞いて欲しい</em>」からCDを出すミュージシャンのように、伊藤さんも「<em>自分が好きで作った<strong>泡盛</strong>を皆に飲んで欲しい</em>」ような物かなと受け取りました。
過去にインディーズミュージシャンの出すCDジャケットデザインとかに関わった事があるので、なんとなくそんなノリで捉えてました。</p>
<p>ところがCDと違い、お酒だと、販売に向けて大きな関門があります。<br>
<strong>酒類販売免許を取得しなければいけない</strong>のです。
お酒その物の製造は沖縄、那覇市の名門酒造「<a href="//kumesen.co.jp/" target="_blank">久米仙酒造株式会社</a>」で行うのですが、なるほど、たしかにお酒を自分で売るには免許も必要ですよね。ハードルの高さはCDとは比べ物にならない話なのは素人目にも明らかです(笑)</p>
<p>しかし、<em>伊藤さんは法律書類作成のプロ</em>なので、一般人では難しい、新規で酒類販売業者としてはじめるための書類作成や審査もきちんとクリアして、酒類販売免許も取得されました。<strong>このひとの実行力やばい。</strong></p>
<figure>
<img src="/posts/2017-12-04-awamori/06.jpg" alt="">
<figcaption>あっさり免許取得しちゃった。<br>「あわもりdeカリー!」のシールは伊藤さんの奥さんデザインだそうです。プロのデザイナーでは無いそうだけど、凄く使いやすく良いデザインでびっくり。</figcaption>
</figure>
<p>商品の詳細は付属のプロップス(お面)で乾杯している方々の楽しそうな写真一杯の、伊藤さんのサイトの「<a href="//awamori-meister.110office.com/%E3%81%A4%E3%81%AA%E3%81%8C%E3%82%8B%E3%81%82%E3%82%8F%E3%82%82%E3%82%8A%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88/" target="_blank">
つながるあわもりプロジェクト</a>」のページを見てもらうとして、ここでは今回僕が<em>実際にラベルのデザインを制作する上での流れを紹介してみます。</em></p>
<h2>ラベルデザインの流れ</h2>
<p>オリジナル泡盛の制作企画の段階で今回の話のイメージは伺っていた事もあり、制作の方向性で迷う事は無い案件でした。<strong>時間は掛かったけど(笑)</strong></p>
<h3>ヒアリングとラフイメージ作成</h3>
<p>「<strong>つながる</strong>」をキーワードに泡盛の良さをもっと沢山の人に知ってもらい、世間の泡盛に対する「強い、きつい」イメージを変える事を目指すのが商品コンセプトです。</p>
<p>このコンセプトの他、あったら嬉しい要素はこんな感じです。</p>
<ul>
<li>飲み会の一杯目の乾杯に飲みたくなる様なお酒らしいデザイン</li>
<li>大阪発の沖縄を繋ぐようなイメージ</li>
<li>泡盛同好コミュニティを巻き込んで一緒に盛り上げる様な物にしたい</li>
</ul>
<p>……こういった要素が入れば良いなと希望いただきました。<br>
こういった場では勢いが大事なので、打ち合わせ中にも、ラフなイメージを時折描いてイメージの共有を行います。</p>
<p>ヒアリングしながらのラフ描きを通して、デザインの方向性も決まってきました。<br>
こんな感じです。</p>
<ul>
<li>お酒っぽくない絵柄のデザインで興味を持って貰えるように。</li>
<li>イラストの登場人物は、全国の泡盛同好の方々を似顔絵キャラにして配することで、盛り上がり易い流れの形成を狙う。(<strong>似顔絵に登場した人はたぶん買ってくれて周りに勧めてくれるはず!の期待もありますw</strong>)</li>
<li>仮想の沖縄料理と大阪料理の居酒屋でみんなで楽しくカリー(乾杯)しているイメージ。</li>
</ul>
<p>多人数の似顔絵を入れる案は僕が提案した物です。<strong>自分が描くことになることなどすっかり忘れて</strong>ただ、こうしたら盛り上がるだろうな、売れるだろうなの思いつきでした。<br>
実際良い考えだと思うけど、なんで自分が後でヒーヒー言いながら描く姿が想像つかなかったのだろう?(笑)</p>
<p>打ち合わせ前後でのラフ案はこんな感じです。
<div class="row-flex" style="justyfy-content: space-between">
<figure style="flex-basis: 48%">
<img src="/posts/2017-12-04-awamori/08.jpg" alt="">
<figcaption>打ち合わせしながら描いた最初のラフ。人数少ないけど多人数が楽しく乾杯をしている方向性はこの時点で確定。</figcaption>
</figure>
<figure style="flex-basis: 48%">
<img src="/posts/2017-12-04-awamori/09.jpg" alt="">
<figcaption>第2ラフ。割とリアル目な構図で人を増やす事にしたけど、これだと後ろを向いている人が多くなり、20人くらいしか顔が入らない、良い意味で噓を入れた場面設定する必要が。</figcaption>
</figure>
</div></p>
<p>インディーズである以上、今回の販売規模的にデザイン案を沢山作った中から絞り込む様な制作の仕方がし辛い案件です。<br>
しかし、幹となるコンセプトが固まっている上、制作の進め方はかなり自由にやらせて貰えるので、あとは一直線でイメージの具現化に向けて突き進みます。</p>
<p>この時点でラベルに似顔絵登場してくれる人を募った所、<strong>希望者が殺到して出演者枠が一瞬で埋まってしまいました。</strong>ラフ案を描いていた時点ではせいぜい20人くらいの気分でいたけど、もうこうなったら<strong>詰め込めるだけ詰め込んでやろうと腹を括ります(笑)</strong></p>
<figure style="flex-basis: 33%">
<img src="/posts/2017-12-04-awamori/10.jpg" alt="">
<figcaption>似顔絵出演者募集の際に描いた絵柄サンプル。<br>僕にとって、ある程度似せる事が出来つつ作業性の良い絵柄。</figcaption>
</figure>
<h3>3DCGで構図の検討</h3>
<p>本制作開始です。<br>
とにかく賑やかな絵にしたいし、似顔絵で入れる人物はできるだけ多く入れてあげたいです。</p>
<p>単純に顔だけの似顔絵と違い、何十人もの人物が全員で乾杯している画作りを行う場合、どんな構図にすれば良いのか、悩むところです。</p>
<p>そこで、今回は3DCGでレイアウトを検討する事にしました。<br>
オープンソース3DCGソフト・Blenderに簡単なモデルを作成し、ざっくりとポーズをつけて配置したときにいい感じにハマる構図ができれば勝ちです。</p>
<p>せっかく3DCGを使うのだから、カメラの位置やレンズの検討がしやすいメリットもフルに活かします。
レンズを魚眼レンズにすれば、かなりの人数を一度に映しこむ事が出来そうな上、円筒のボトルに貼るラベルとしても、いい感じに広がりのある画になりそうです。テーブルも見せやすいので、そこに大阪と沖縄を感じさせる料理も並べましょうか。</p>
<p>こんな雰囲気で作っていきます。</p>
<figure>
<img src="/posts/2017-12-04-awamori/11.gif" alt="">
<figcaption>簡単な人物モデルを作り、ポーズつけ、それをコピーして1テーブル。そしてテーブルをどんどんコピーして調整し、魚眼レンズでレンダリング。</figcaption>
</figure>
<p>これで、50人くらいは顔が識別出来るレベルで入れる事が出来そうなレイアウトができました。</p>
<h3>約50人の似顔絵を描き込む</h3>
<p>このレイアウトを元に、下描きを描き込んで行きます。
ほとんどの人物は僕がお会いした事の無い人なので、facebookのプロフィール写真等を見ながら、似せるように努力しつつも、人柄を勝手に想像してポーズや表情に少しずつ変化を付けながら描いていきます。(正直、怒られたらどうしようかとビクついてたけど、良い反応が多かったので何よりですw)</p>
<p>描画記録動画を書き出せるiPadのアプリ<a href="https://itunes.apple.com/jp/app/procreate/id425073498?mt=8" target="_blank">Procreate</a>で描いていたので、その様子をどうぞ。</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/2wsQQgsXagc?rel=0" frameborder="0" allowfullscreen></iframe>
<p>早送り動画で見るとすごいスピードで描いている様に見えますが、もちろんそんなことありません。<br>
ただ実際、この辺は勢いが大事なので、気分的には「うぉりゃゃゃゃゃー」っと、どんどん描いていきます。<br>
(途中で番号を書いているのは、どの場所に誰を描くのか記すためです。)</p>
<p>大分固まってきました。下描きはこれくらいでOKとしすます。
<img src="/posts/2017-12-04-awamori/12.png" alt=""></p>
<aside>
ただ、ここでは反省点も。<br>
3DCGの絵の上に描いていった事で、それに引きづられてしまい、少し硬い絵になってしまいました。今回の絵柄的に、デッサンやパースの正確さよりも、全体の雰囲気を重視すべき案件です。所々で下絵を無視して意図的に崩しながら描いていたのだけど、もっと思い切っても良かったかもしれません。<br>
その為には、3DCGの上から描くのでは無く、見ながら描くくらいの使い方をしてみても良かったのかなとは思います(^^;;
</aside>
<h3>レイアウト調整のしやすいように、illustratorパスで清書する</h3>
<p>もうそのままペイントソフトで仕上げるのもありだったけど、後に人物の配置換えがありそうだったり、商品ロゴも加える関係で、色々後調整しやすいデータ作りが得策と判断しました。
清書はillustratorでパスデータにする事にします。</p>
<p>シンプル目な絵柄とは言え、50人以上のキャラをパス化する超絶地味で時間の掛かる作業です。しかも自動化もし辛いので、これは腹をくくってちまちまパス化していくしかありません。</p>
<p>気が遠くなる作業なので、普段はできるだけ避けたい工程です。だだ、<strong>その気持ちとは裏腹に僕はこういう作業は超得意なので(笑)</strong>、気長にベジェをプチプチ打っていきます。
<figure>
<img src="/posts/2017-12-04-awamori/13.gif" alt="">
<figcaption>シンプルな絵柄でも密度がそれなりになるのでたいへん。illustratorだと、見えない部分まで描くことになりがちなので尚更と。</figcaption>
</figure></p>
<h3>商品ロゴはモダンでありながらも、ちょいダサの親しみやすさを狙う</h3>
<p>大まかにキャラクターが出来たところで、商品ロゴにも取り掛かります。
こちらも絵と併せて、どうするか思案してました。
最初、既存フォントをそのまま使用して組むつもりでしたが、あまり馴染むイメージがわかなかったので、僕自身が文字を描いてしまう事にします。</p>
<p>文字の形は、近年リリースされたモダンな形の日本語フォント数種類を並べて参考にしつつも、今回の絵柄にあった、親しみやすい物を目指します。あまりカッチリとは組まず、ちょっと崩してダサめの味付けにする位の方が良さそうに感じました。</p>
<p><img src="/posts/2017-12-04-awamori/14.gif" alt=""></p>
<p>絵の完成度が上がってきた時点から商品ロゴを書き始めたので、ここはあまり迷うポイントにはなりませんでした。</p>
<h2>沖縄と大阪っぽい要素で飾って完成!</h2>
<p>人物と商品ロゴが完成したら、あとは沖縄と大阪っぽい要素を料理、食材、キャラクターとして散りばめます。</p>
<p><img src="/posts/2017-12-04-awamori/16.png" alt=""></p>
<p>このチョイスは結構迷って、首里城や大阪城といったランドマークも入れたくなるも、時間的に諦めたり、くいだおれ太郎やビリケンさんを入れたくなりつつも、実は権利面でダメだという事に気がついたりと、なんでもかんでもという訳にはいきません。<br>
シーサはー描きやすい、通じやすい、権利面大丈夫だったりと優秀(笑)</p>
<figure>
<img src="/posts/2017-12-04-awamori/15.png" alt="">
<figcaption>沖縄感を出すための黒豚。キャラクターはひとつずつデザインしてます。画像検索した黒豚の画像をスケッチしてイメージを
固めつつ、自分なりの絵で構築していく。</figcaption>
</figure>
<figure>
<img src="/posts/2017-12-04-awamori/17.png" alt="">
<figcaption>あらためて確認すると、沖縄の色が強いメニュー構成。お好み焼も考えたけど、タコ焼きあるし、粉もの被りになるからどうかなーと除外(<strong>こんなところで変なこだわりが出る</strong>)。カニ鍋位は追加してもよかったかも。てっちりは絵で表現しづらいからパス(笑)</figcaption>
</figure>
<p>追加要素が上手く馴染むように調整を加えて完成!</p>
<p><img src="/posts/2017-12-04-awamori/catch.png" alt=""></p>
<p>実際にラベルが貼られた瓶は、<a href="//awamori-meister.110office.com/%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97/">伊藤さんのサイト</a>で見れますよ!(ステマ)</p>
<figure>
<img src="/posts/2017-12-04-awamori/18.png" alt="">
<figcaption>伊藤さんのご好意で、乾杯の輪の中に僕も入れさせてもらったり、棚の上にある瓷(かめ)に入った泡盛のラベルに「大石制作」と入れさせてもらってます。<br>
唯一後ろを向いている人物は、このお酒を購入してくれた人自身であり、みんなで乾杯したいと言う意図が入っている、僕と伊藤さんの暖かさに感動するポイントです(笑)</figcaption>
</figure>
<h2>「泡盛でカリー!ブレンド」はガチでうまいよ!</h2>
<p>完成した「泡盛でカリー! ブレンド」のアルコール度数は12度と、一般的な日本酒(15〜16度)より低い、とにかく飲みやすいお酒です。<br>
なので、普通にお酒が好きな人なら冷やしてストレートやロックで飲むと、高級日本酒や焼酎のような口の中をサラッと通り抜ける様な上質で爽快な飲み口ながらも「泡盛の味」も実感する事の出来る絶妙のブレンドです。<strong>これ、贔屓目無しで本当に美味しいですよ!</strong> </p>
<p>お酒が苦手な人は炭酸で割って、さらにアルコール度数を下げてもいい感じです。シュワシュワに混じってほんのりと泡盛の香りと味を感じる事が出来るので、僕も好きな飲み方です。</p>
<p><em>個人的に、泡盛と言うのはどのお酒よりも懐の深いお酒だと思っています。</em>個々人で好きな飲み方、料理と組み合わせて自由に楽しませて貰える印象を持ってます。「泡盛でカリー!ブレンド」は大阪で活動する伊藤さんの感性で、その懐を更に深くした逸品と言えるでしょう。</p>
<p>そして、<strong>インディーズ生産(笑)の商品性質上、いつまで買える物か分かりません。</strong><br>
ここまでの紹介で気になったらぜひどうぞ!→ <a href="//awamori-meister.110office.com/%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97/" target="_blank">泡盛でカリー!オンライショップ</a></p>
<figure>
<div class="row-flex" style="justify-content: space-between;">
<div style="flex-basis: 1"><img src="/posts/2017-12-04-awamori/19.jpg" alt=""></div>
<div style="flex-basis: 1"><img src="/posts/2017-12-04-awamori/20.jpg" alt=""></div>
<div style="flex-basis: 1"><img src="/posts/2017-12-04-awamori/21.jpg" alt=""></div>
</div>
<figcaption><a href="//awamori-meister.110office.com/%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%97/" target="_blank">オンライショップ</a>で注文すると、大阪・北浜から通常業務の合間を縫って伊藤さん自身の手で配送してくれます。<br>
大阪・福島の沖縄料理屋「<a href="//tabelog.com/osaka/A2701/A270108/27087928/" target="_blank">がじゅまる食堂</a>」さんなど、一部居酒屋等では泡盛でカリー!ブレンドを注文して体験することもできます。お近くのかたはぜひ!
</figcaption>
</figure>
<h2>商品の販売戦略から関わるデザインのご依頼もお待ちしています</h2>
<p>「泡盛でカリー!ブレンド」は、伊藤さんの努力と支えてくれる周りの方々のおかげで、めでたく第一回分の生産分が完売し、現在第二回生産分が販売開始されています。</p>
<p>こういった、販売戦略から関わるデザイン制作も大歓迎なので、お気軽に相談くださいね!</p>
<p>僕としても、一生懸命楽しく取り組ませていただいた案件なので、本当に応援しています。この記事を読んで、今度泡盛を見かけたら、今回の泡盛でなくとも、ちょっと飲んでみようかなと思える人が増えれば幸いです。</p>
ImageMagicでPhotoshopより早く、便利に画像処理しちゃおう
https://blog.s0014.com/posts/2017-11-21-imagemagick/
2017-11-20T16:57:00Z
2018-05-23T16:19:20+09:00
大石 真也(Shinya Oishi) / 大石制作
デザイナーもコマンドラインツールで画像処理をする術を知っておいて良いと思うんです。実用的なコマンドのサンプルも。
<h2>みんな、もっと画像処理の単純作業にはImageMagickつかおうぜ!</h2>
<p>僕は多くのデザイナーよりは技術回りに明るいので、単純作業の自動化は得意とする分野です。</p>
<p>グラフィック回りのデザイナーと言えば、AdobeのPhotoshopやIllustratorの専門家で、その他のツールはからっきしと言う人が多く、業務の中で単純かつ大量の画像処理作業が発生しても正面から時間をかけて取り組みがちです。これ、ほんと辛いんですよね。(余裕がある時なら、それはそれで良い気分転換になる時もあるけどw)</p>
<p>こうした状態を見聞きする度に、これを知らないと勿体無いと強く感じているツールのひとつが今回紹介する「<a href="//www.imagemagick.org/" target="_blank">ImageMagick</a>」です。</p>
<p>ImageMagickはCUI(コマンドプロンプトとかターミナルとか端末とか)、いわゆる「黒い画面」での利用が基本になるツールなので、とっつきにくさはあるけど、その価値は十分にあります。</p>
<h2>大量の画像を効率よく処理し、変更にも対応させやすいImageMagickの実例</h2>
<h3>縦横比率の違った画像をすべて同じサイズに揃える(mogrifyコマンド)</h3>
<p>たとえば、こんな感じの、縦横比率、拡張子が違った画像が何十枚もあったとします。
これらを全部680x480pxの大きさに揃える必要があったら、どうしますか?</p>
<p><img src="/posts/2017-11-21-imagemagick/01.png" /></p>
<p>Photoshopで一枚一枚手作業で合わせるならかなり面倒ですよね。(これくらいならPhotoshopのアクション機能でなんとかなるけど)</p>
<h4>長辺ベースでリサイズ</h4>
<p>こんな時に大活躍するのがImageMagickです。これくらいの仕事、コマンドひとつであっというまに完了です。
こんな感じでコマンドを入れると……</p>
<pre class="highlight shell"><code><span class="c"># mogrify => ImageMagickの一括変換コマンド</span>
<span class="c"># -path ./output-images => 出力フォルダ</span>
<span class="c"># -auto-orient => 画像の向きを自動修正</span>
<span class="c"># -resize "640x640>" => 画像の拡大縮小 「>」は長辺基準</span>
<span class="c"># -gravity Center => 切り抜きの基準位置 中央</span>
<span class="c"># -extent 640x480 => 画像の切り抜きサイズ</span>
<span class="c"># -background red => 背景色 今回は分かりやすく赤に</span>
<span class="c"># ./input-images/* => 入力フォルダ</span>
<span class="gp">$ </span>mogrify <span class="se">\</span>
-path ./output-images <span class="se">\</span>
-auto-orient <span class="se">\</span>
-resize <span class="s2">"640x640>"</span> <span class="se">\</span>
-gravity Center <span class="se">\</span>
-extent 640x480 <span class="se">\</span>
-background red <span class="se">\</span>
./input-images/<span class="k">*</span>
</code></pre>
<p>……すぐにこんな画像が生成されます。<br>
背景を分かりやすく赤にしてるので、上の画像の縦長や正方形の画像を見比べて貰うと、違いが分かりやすいはずです。</p>
<p><img src="/posts/2017-11-21-imagemagick/02.png" /></p>
<h4>短辺ベースでリサイズ</h4>
<p>けど、これだと画像によってはリサイズと切り抜き時に発生する余白部分(赤くして目立たせてます)が気になるので、短辺をベースにリサイズした方が良さそうですね。こうしましょうか。</p>
<pre class="highlight shell"><code><span class="c"># mogrify => ImageMagickの一括変換コマンド</span>
<span class="c"># -path ./output-images => 出力フォルダ</span>
<span class="c"># -auto-orient => 画像の向きを自動修正</span>
<span class="c"># -resize "640x640^" => 画像の拡大縮小 「^」は短辺基準</span>
<span class="c"># -gravity Center => 切り抜きの基準位置 中央</span>
<span class="c"># -extent 640x480 => 画像の切り抜きサイズ</span>
<span class="c"># -background transparent => 背景色 透明</span>
<span class="c"># ./input-images/* => 入力フォルダ</span>
<span class="gp">$ </span>mogrify <span class="se">\</span>
-path ./output-images <span class="se">\</span>
-auto-orient <span class="se">\</span>
-resize <span class="s2">"640x640^"</span> <span class="se">\</span>
-gravity Center <span class="se">\</span>
-extent 640x480 <span class="se">\</span>
-background transparent <span class="se">\</span>
./input-images/<span class="k">*</span>
</code></pre>
<p>余白が出ないように切り抜かれましたね。
<img src="/posts/2017-11-21-imagemagick/03.png" /></p>
<h3>揃えたいサイズや切り抜き基準点が変わった場合</h3>
<p>欲しいサイズが少し変更になり、640x480pxじゃなくて、640x360pxに変わった?
切り抜きの基準点も中央ではなく、上部からでやってみたい?
じゃあ、こうしましょうか。</p>
<pre class="highlight shell"><code><span class="c"># mogrify => ImageMagickの一括変換コマンド</span>
<span class="c"># -path ./output-images => 出力フォルダ</span>
<span class="c"># -auto-orient => 画像の向きを自動修正</span>
<span class="c"># -resize "640x360^" => 画像を拡大縮小 「^」は短辺基準</span>
<span class="c"># -gravity North => 切り抜きの基準位置 上部(北)</span>
<span class="c"># -extent 640x360 => 画像の切り抜きサイズ</span>
<span class="c"># -background transparent => 背景色 透明</span>
<span class="c"># ./input-images/* => 入力フォルダ</span>
<span class="gp">$ </span>mogrify <span class="se">\</span>
-path ./output-images <span class="se">\</span>
-auto-orient <span class="se">\</span>
-resize <span class="s2">"640x360^"</span> <span class="se">\</span>
-gravity North <span class="se">\</span>
-extent 640x360 <span class="se">\</span>
-background transparent <span class="se">\</span>
./input-images/<span class="k">*</span>
</code></pre>
<p>画像の上の方を基準に切り抜かれているのがわかりますね。
<img src="/posts/2017-11-21-imagemagick/04.png" /></p>
<p>どうでも良いけど、なぜか「gravity」のパラメータは「top, right, bottom, left」とかでなくて東西南北の方向「North, NorthEast, West, Center, East, SouthWest, South, SouthEast」なんですよね。<br>
<a href="//www.imagemagick.org/script/command-line-options.php#gravity" target="_blank">パラメータ一覧は公式ドキュメントでどうぞ。</a></p>
<h3>正方形に切り抜く</h3>
<p>……え? 300x300pxの正方形でも欲しい?
まぁ、いいですよ。</p>
<pre class="highlight shell"><code><span class="c"># mogrify => ImageMagickの一括変換コマンド</span>
<span class="c"># -path ./output-images => 出力フォルダ</span>
<span class="c"># -auto-orient => 画像の向きを自動修正</span>
<span class="c"># -resize "300x300^" => 画像を拡大縮小 「^」は短辺基準</span>
<span class="c"># -gravity Center => 切り抜きの基準位置</span>
<span class="c"># -extent 300x300 => 画像の切り抜きサイズ</span>
<span class="c"># -background transparent => 背景色 透明</span>
<span class="c"># ./input-images/* => 入力フォルダ</span>
<span class="gp">$ </span>mogrify <span class="se">\</span>
-path ./output-images <span class="se">\</span>
-auto-orient <span class="se">\</span>
-resize <span class="s2">"300x300^"</span> <span class="se">\</span>
-gravity Center <span class="se">\</span>
-extent 300x300 <span class="se">\</span>
-background transparent <span class="se">\</span>
./input-images/<span class="k">*</span>
</code></pre>
<p>resizeとextentの数字を調整しなおすだけの簡単なお仕事ですね。
<img src="/posts/2017-11-21-imagemagick/05.png" /></p>
<h3>タイル状に並べる(montageコマンド)</h3>
<p>せっかくなので、この300x300pxの画像を新しく作り直したinputs-imagesフォルダに入れ、これらの画像をタイル状にドッキングした画像でも作りましょうか?</p>
<p>300x300pxの画像が32枚入った「input-images」フォルダの中にある画像をタイル1枚辺り80x80pxの大きさで8x4のタイルで並べて出力してみます。</p>
<pre class="highlight shell"><code><span class="c"># montage \ => ImageMagickのモンタージュ画像生成コマンド</span>
<span class="c"># input-images/* \ => 入力フォルダ</span>
<span class="c"># -background white \ => 背景色を白に</span>
<span class="c"># -geometry 80x80+0+0 \ => 1タイル80x80pxの大きさで並べる</span>
<span class="c"># -tile 8x \ => 一列あたりのタイル数</span>
<span class="c"># ./output-images/montage.png => 出力フォルダ</span>
<span class="gp">$ </span>montage <span class="se">\</span>
input-images/<span class="k">*</span> <span class="se">\</span>
-background white <span class="se">\</span>
-geometry 80x80+0+0 <span class="se">\</span>
-tile 8x <span class="se">\</span>
./output-images/montage.png
</code></pre>
<p>このあたりからはPhotoshopのアクションだけでは対応が難しい領域です。<br>
<img src="/posts/2017-11-21-imagemagick/06.png" /></p>
<p>タイルの並べ方の変更もかんたんです。
1列8枚だったのを4枚に変形する場合はこんな感じです。</p>
<pre class="highlight shell"><code><span class="c"># 上のコマンドの『-tile』のオプションを『8x』から『4x』に変えただけ。</span>
<span class="gp">$ </span>montage <span class="se">\</span>
input-images/<span class="k">*</span> <span class="se">\</span>
-background white <span class="se">\</span>
-geometry 80x80+0+0 <span class="se">\</span>
-tile 4x <span class="se">\</span>
./output-images/montage.png
</code></pre>
<p>こんな感じですね。<br>
<img src="/posts/2017-11-21-imagemagick/07.png" /></p>
<h4>決め打ちしたタイルのサイズからはみ出すと、画像が分割されて出力される</h4>
<p>タイルのパラメータここまでのタイルのパラメータは「-tile 8x」とか「-tile 4x」と言った具合に横の枚数だけしてしてますが、これを「-tile 4x2」などと決め打ちすると、こんな感じで分割して出力されます。</p>
<pre class="highlight shell"><code><span class="c"># 今度は『-tile』のオプションを『4x3』に変えてみる。</span>
<span class="gp">$ </span>montage <span class="se">\</span>
input-images/<span class="k">*</span> <span class="se">\</span>
-background white <span class="se">\</span>
-geometry 80x80+0+0 <span class="se">\</span>
-tile 4x3 <span class="se">\</span>
./output-images/montage.png
</code></pre>
<p>「montage-0.png」<br>
<img src="/posts/2017-11-21-imagemagick/06-0.png" /></p>
<p>「montage-1.png」<br>
<img src="/posts/2017-11-21-imagemagick/06-1.png" /></p>
<p>「montage-2.png」<br>
4x3の枚数に満たない分は、そのスペースを空白で開けた画像が生成される様ですね。
<img src="/posts/2017-11-21-imagemagick/06-2.png" /></p>
<h4>タイルは名前順で並んでいるので、調整するのもかんたん</h4>
<p>Macでの作業なら、「<a href="/posts/2017-06-11-random/">MacのフォルダやファイルをJavaScriptでランダムに並べ替える</a>」のスクリプトを使うと、手軽に順番をランダムに変更することができます。</p>
<p>元々こうだった物が……
<img src="/posts/2017-11-21-imagemagick/06.png" /></p>
<p>あっという間に順番バラバラに。
<img src="/posts/2017-11-21-imagemagick/08.png" /></p>
<p>もちろん、デザイナーらしく、きちんとタイルの並べ方にこだわるのも良いけど、要所でこういった道具を柔軟に使えるようでありたいですよね。</p>
<h3>タイルの隙間を開ける</h3>
<p>画像同士の隙間を開けてみましょうか?</p>
<pre class="highlight shell"><code><span class="c"># 『-geometory』のオプションの『80x80+0+0』だった部分を」『80x80+10+5』に変える事で</span>
<span class="c"># 各タイルに横10px 縦5pxの隙間が空くように。</span>
<span class="c"># 『-background gray80』で隙間が開いているのを分かりやすく。</span>
<span class="gp">$ </span>montage <span class="se">\</span>
input-images/<span class="k">*</span> <span class="se">\</span>
-background gray80 <span class="se">\</span>
-geometry 80x80+10+5 <span class="se">\</span>
-tile 8x <span class="se">\</span>
./output-images/montage.png
</code></pre>
<p>隙間が空きましたね。
<img src="/posts/2017-11-21-imagemagick/09.png" /></p>
<h3>枠線をつける</h3>
<p>空いた隙間に枠線をつけてみましょうか。</p>
<pre class="highlight shell"><code><span class="c"># 『-bordercolor white』と『-border 5』のパラメータで白い枠線を追加。</span>
<span class="gp">$ </span>montage <span class="se">\</span>
input-images/<span class="k">*</span> <span class="se">\</span>
-background gray80 <span class="se">\</span>
-geometry 80x80+10+5 <span class="se">\</span>
-tile 8x <span class="se">\</span>
-bordercolor white <span class="se">\</span>
-border 5 <span class="se">\</span>
./output-images/montage.png
</code></pre>
<p>枠線がつくとポラロイド写真っぽくなりますね。
<img src="/posts/2017-11-21-imagemagick/10.png" /></p>
<h4>bordercolorを設定すると、写真の透過背景にも適用される</h4>
<p>ここで注意したいのは、枠線の色(bordercolor)を設定すると、その色はPNGやGIF画像の透過部分にも適用される事でしょうか。<br>
けど、これはこれで便利とも言えるので、そういう挙動がある事を頭の片隅に置いておくだけで十分でしょう。</p>
<pre class="highlight shell"><code><span class="c"># 各タイルの背景を赤に設定することで透過部分も赤く。</span>
<span class="gp">$ </span>montage <span class="se">\</span>
input-images/<span class="k">*</span> <span class="se">\</span>
-background gray80 <span class="se">\</span>
-geometry 80x80+10+5
-tile 8x <span class="se">\</span>
-bordercolor red <span class="se">\</span>
-border 5 <span class="se">\</span>
./output-images/montage.png
</code></pre>
<p>枠線の色を赤に設定してみた例。PNG画像の透過部分まで赤くなってますね。
<img src="/posts/2017-11-21-imagemagick/11.png" /></p>
<h3>ポラロイド写真っぽくする</h3>
<p>せっかくだから、写真を傾けたり重ねたりして、もう少しポラロイドっぽくしましょうか。</p>
<pre class="highlight shell"><code><span class="c"># 『-geometry +10+10』でタイル間の隙間を設定。</span>
<span class="c"># 『+polaroid』で各タイルを自動でランダムで回転させ、</span>
<span class="c"># 湾曲させた上に影も落としてくれる。</span>
<span class="gp">$ </span>montage <span class="se">\</span>
input-images/<span class="k">*</span> <span class="se">\</span>
-background gray50 <span class="se">\</span>
-geometry +10+10 <span class="se">\</span>
-tile 8x <span class="se">\</span>
-bordercolor white <span class="se">\</span>
-border 0 <span class="se">\</span>
+polaroid <span class="se">\</span>
-background none <span class="se">\</span>
./output-images/montage.png
</code></pre>
<p>各画像はプリントした写真っぽく、微妙に湾曲しており、自動で±15度の範囲で回転が加えられています。意外に芸が細かいですね。
<img src="/posts/2017-11-21-imagemagick/12.png" /></p>
<h2>コマンドライン操作の敷居の高さはあるけど……</h2>
<p>どうでしょう?
これくらいの事がすぐにできるようになるのなら、ちょっと触ってみる価値ありそうですよね。</p>
<p><strong>パラメータを微調整するだけで、いろんなバリエーションの画像を作る事ができるのはコマンドラインツールの大きなメリットです。</strong></p>
<p>伸縮、切り抜き、タイル並べなんて、ImageMagickの膨大な機能の欠片です。この記事の役割上、わりとシンプルなコマンドでの例を紹介しましたが、ImageMagicには単純な画像フォーマット変換や表示をはじめ、何に使えば良いのか思いつかない加工機能が盛りだくさんです。<br>
ここまで紹介した機能のパラメータを調整したり、組み合わせる事によって、かなり複雑な加工を施した画像でもすぐに生成する事が出来るようになるはずです。</p>
<p>まさに、使い方はあなたのアイデア次第!<strong>(よくある投げっぱなしワード)</strong></p>
<p>画像加工と言えばPhotoshopしか知らない人にとっては、コマンドラインの敷居の高さはあるけど、ぜひそこを超えて便利に活用して欲しいなと思います。</p>
<p>……本当はCUIの使い方説明からするべきなんだろうけど、ごめんなさい、そうなるといつまで経っても記事が書き上がらなそうなので、別のサイトにおまかせします(笑)</p>
<ul>
<li><a href="//www.google.co.jp/search?q=%E5%A3%8A%E3%82%8C%E3%81%9Fphotoshop&oq=%E5%A3%8A%E3%82%8C%E3%81%9Fphotoshop&aqs=chrome..69i57.5319j0j4&sourceid=chrome&ie=UTF-8#safe=off&q=windows+%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%97%E3%83%AD%E3%83%B3%E3%83%97%E3%83%88+%E5%85%A5%E9%96%80" target="_blank">「windows コマンドプロンプト 入門」で検索</a></li>
<li><a href="//www.google.co.jp/search?q=mac+%E3%82%BF%E3%83%BC%E3%83%9F%E3%83%8A%E3%83%AB+%E5%85%A5%E9%96%80&oq=mac+%E3%82%BF%E3%83%BC%E3%83%9F%E3%83%8A%E3%83%AB+%E5%85%A5%E9%96%80&aqs=chrome..69i57.7513j0j1&sourceid=chrome&ie=UTF-8" target="_blank">「mac ターミナル 入門」で検索</a></li>
</ul>
<p><strong>ちょっと工夫して自動化すればあっという間に終わる加工作業を従業員さんに途方もない単純作業をさせる現場が減れば良いなと思います。</strong></p>
<h2>自動処理における ImageMagic VS Photoshop</h2>
<p>どちらもそれなりに使える事を前提とし(ImageMagickはCUIの操作に抵抗のない程度、Photoshopはアクション機能を活用できる程度)
、自動処理が有効になる数十ファイル以上の処理をする場合の僕の肌感覚による比較になります。</p>
<table><thead>
<tr>
<th></th>
<th>ImageMagick</th>
<th>Photoshop</th>
</tr>
</thead><tbody>
<tr>
<td>処理速度</td>
<td><p class="text-center mgb-0">◎</p></td>
<td><p class="text-center mgb-0">×</p></td>
</tr>
<tr>
<td>画像品質</td>
<td><p class="text-center mgb-0">○</p></td>
<td><p class="text-center mgb-0">◎</p></td>
</tr>
<tr>
<td>使い分けの判断</td>
<td><ul><li>速度重視。</li><li>ゼロからファイルを生成したい時</li><li>シェルコマンドや他プログラムと組み合わせたい時。</li><li>Exif等のプロパティ情報によって処理内容を変えたい時。</li><li>Photoshopでは「壊れている」と言われて開けなくなった画像を扱えることも。</li></ul></td>
<td><ul><li>高品質で手軽に凝った加工をしたい時。</li></ul></td>
</tr>
</tbody></table>
<p>少数のファイルで、Photoshopならではの加工機能を使いたい場合や、生成画像のクオリティを重視したい時以外は、概ねImageMagickの方が自動処理には有利です。
やっぱり画像を画面に開かずに処理できるCUIツールはとにかく仕事の速さが異次元です。ざっくりとでも、使い所をわかっていれば大活躍してくれるはずですよ。</p>
<p>あと、上の表だと、ImageMagickが画像品質をかなり犠牲にするように見えるけど、実際はこの手のツールの中では画質が優秀だとされているものです。そこはやっぱりPhotoshopの強さが飛び抜けてるって話ですね。</p>
<aside>
ちなみに、英Wikipediaによると、ImageMagick(<a href="//en.wikipedia.org/wiki/ImageMagick">Wikipedia</a></li>)の初回リリースは1990年8月。Photoshop(<a href="//en.wikipedia.org/wiki/Adobe_Photoshop">Wikipedia</a>)は1990年2月。学年違いの同い年かぁ。
</aside>
<h2>ImageMagickのインストール</h2>
<p>CUIの基本的な使い方が分かっている事が前提となる紹介ですが、こんな感じです。</p>
<h3>Windows10環境</h3>
<p>こちらの記事で丁寧にインストール方法が解説されてました。→ <a href="https://i-think-it.net/windows10-imagemagick-gif-anime/" target="_blank">windows10でimagemagickを使ってコマンド一発で超簡単にGIFアニメを作成してみた! | あいしんくいっと</a></p>
<p>(このままだとRAWが扱えない可能性があるけど、その場合は<a href="http://ufraw.sourceforge.net/)を併せてインストールすればいけるかな?" target="_blank">UFRaw</a></p>
<h3>Mac環境</h3>
<p>こっちも簡単です。</p>
<ol>
<li><a href="https://brew.sh/index_ja.html" target="_blank">Homebrew</a>をインストール</li>
<li>ターミナルを開き、下記コマンドを実行</li>
</ol>
<p> </p>
<pre class="highlight shell"><code><span class="gp">$ </span>brew install imagemagick
</code></pre>
<p>RAW画像も扱う予定があるのなら、併せて<a href="//ufraw.sourceforge.net/" target="_blank">UFRaw</a>もインストールします。</p>
<pre class="highlight shell"><code><span class="gp">$ </span>brew install ufraw
</code></pre>
<h3>レンタルサーバーには最初から入っている事が多い</h3>
<p>ちなみにImageMagicは格安レンタルサーバーにもあらかじめインストールされている事が多いので、サーバー内でバラバラのディレクトリに分散して置いてある全画像のサムネイル画像をまとめて一気に生成なんて事もできちゃいます。</p>
<p>サーバーによってはWordpresやEC-CUBEのバックの画像処理回りでImageMagickが仕事してる事もあります。PHP製プログラムに限らず本当にあちこちで使われているので、知らず知らずのうちにImageMagicの仕事によって生成された画像を目にしているはずです。
そりゃ1990年生まれだもんねぇ。一杯仕事してますよ。</p>
<h4>レンタルサーバーでImageMagicが使えるか調べる方法</h4>
<p>SSH接続できるレンタルサーバーなら接続して「convert -version」と打てばバージョン情報が帰ってくるはずです。</p>
<pre class="highlight shell"><code><span class="gp">$ </span>convert -version
Version: ImageMagick 6.8.0-7 2014-04-18 Q16 http://www.imagemagick.org
Copyright: Copyright <span class="o">(</span>C<span class="o">)</span> 1999-2012 ImageMagick Studio LLC
Features:
</code></pre>
<p>SSH接続出来ないサーバーでは、まず、こんな感じ1行のPHPファイルを用意してアップロード。</p>
<pre class="highlight php"><code>// ファイル名はcheck-image-magick.phpとか、自分がわかる名前で。
<span class="cp"><?php</span> <span class="k">echo</span> <span class="nb">system</span><span class="p">(</span><span class="s2">"convert -version"</span><span class="p">);</span> <span class="cp">?></span>
</code></pre>
<p>そのファイルに755なり644なりのパーミッションを割り当ててブラウザで開くと「Version: ImageMagick 6.2.8 05/07/12 Q16 file:/usr/share/ImageMagick-6.2.8/doc/index.html Copyright: Copyright © 1999-2006 ImageMagick Studio LLC」とか表示されるはずです。
ただ、その環境ではじめてImageMagickを使うのは敷居が高すぎるので、おとなしくコマンドプロンプトやターミナルで使うのが吉です(笑)</p>
<h2>ImageMagicを詳しく知りたくなった時のリンク</h2>
<p>ImageMagicの便利さ、伝わりましたか?</p>
<p>きっと、画像に関する単純作業があるのなら、ImageMagicで楽が出来るケースがかなりあるはずです。</p>
<p>この記事で便利さの欠片でも感じて貰えたのなら、下記サイト/記事でもっと詳細に解説されているので、ぜひ参考にしてみてくださいね。</p>
<dl>
<dt><a href="//www.imagemagick.org/">ImageMagick</a></dt><dd>公式。英語だけど、読み込めばもっと便利な使い方があるはず……。</dd>
<dt><a href="//qiita.com/mtakizawa/items/a74bd91f7b3835976461">ImageMagickの使用例 – 入門 - Qiita</a></dt><dd>主に公式サイトにある実例ページへの各リンクが日本語でインデックスされてて便利です。</dd>
<dt><a href="//image-magick.com/">ImageMagick コマンドリファレンス</a></dt><dd>日本語では一番分かりやすくまとまっているように見えます。ブラウザ上で実際に動く神サンプルが各種揃ってます。</dd>
<dt><a href="//imagemagick.rulez.jp/">Imagemagickの使い方日本語マニュアル</a></dt><dd>チュートリアルから段階を追って紹介。特殊効果系のサンプルが充実してます。</dd>
</dl>