tag:crieit.net,2005:https://crieit.net/tags/SVG/feed 「SVG」の記事 - Crieit Crieitでタグ「SVG」に投稿された最近の記事 2022-07-18T17:31:16+09:00 https://crieit.net/tags/SVG/feed tag:crieit.net,2005:PublicArticle/18250 2022-07-18T17:31:16+09:00 2022-07-18T17:31:16+09:00 https://crieit.net/posts/9VAe-62d51a5412271 無料ベクトルアニメソフト9VAeきゅうべえが99ページになった! <p><a href="https://crieit.now.sh/upload_images/b9c2737fcc77ed39a2dec1701dce8f4762d512da1b8a9.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/b9c2737fcc77ed39a2dec1701dce8f4762d512da1b8a9.gif?mw=700" alt="image" /></a><br /> 9VAeきゅうべえが、99ページ作れるようになりました。このアニメは32ページです。<br /> Win / Mac / Linux / iOS / iPad / Android / Chromebook / Amazon Fire / Raspberry Pi でSVG/GIF/MP4動画が作れます。<br /> <a target="_blank" rel="nofollow noopener" href="https://9vae.blogspot.com/p/9vae-download.html">ダウンロードはこちら</a></p> Danjiro Daiwa tag:crieit.net,2005:PublicArticle/16798 2021-03-31T13:16:09+09:00 2021-03-31T13:16:09+09:00 https://crieit.net/posts/AC-ai イラストACの ai イラストから動画をつくる <p>イラストACの ai イラストから動画をつくる解説記事をかきました。その過程でSVG入力のバグを発見。9VAeを、0.7.9 にバージョンアップしました。解説動画は、9VAe+iMovie で作成してます。<a href="">https://dnjiro.hatenablog.com/entry/2021/03/30/142405</a></p> <p><a href="https://crieit.now.sh/upload_images/ded3fc20cafb245fba88456bc30467a86063f6572cdbf.gif" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/ded3fc20cafb245fba88456bc30467a86063f6572cdbf.gif?mw=700" alt="image" /></a></p> Danjiro Daiwa tag:crieit.net,2005:PublicArticle/15723 2020-02-19T19:53:57+09:00 2020-02-19T19:53:57+09:00 https://crieit.net/posts/Cloud-Function-SVG-OGP Cloud FunctionとSVGでOGP画像生成を試行錯誤したまとめ <p>Nuxt+Firebaseで<a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">開発してるサービス</a>のOGP画像を改善しようと、<br /> いろいろ試してみたときの備忘録。</p> <p>OGP画像の生成はクライアント側とサーバ側かがあるが、<br /> Firestoreの変更に合わせて生成したいので、<br /> Cloud Function上で利用できる方法を考えてた。</p> <p>結果的に、</p> <ol> <li><a target="_blank" rel="nofollow noopener" href="https://sharp.pixelplumbing.com/">sharp</a>で画像を合成してベースをつくり</li> <li>ImageMagickで文字を追記していく</li> </ol> <p>という構成になった。その試行錯誤の備忘録です。</p> <h3 id="やりたかったこと"><a href="#%E3%82%84%E3%82%8A%E3%81%9F%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8">やりたかったこと</a></h3> <p>背景画像+本の画像+文字みたいなOGP画像にしたい。</p> <p><a href="https://crieit.now.sh/upload_images/8b51de5926bba4fd12714eb7d06f4d8b5e4d1387cde01.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/8b51de5926bba4fd12714eb7d06f4d8b5e4d1387cde01.png?mw=700" alt="tsundoku_book_start_4873115655.png" /></a></p> <p>あと、座標の指定はめんどくさいので、楽な方法を探してみた。<br /> SVGだとCSSも使えて、ブラウザ上で書くにできるので良さそう。<br /> (と思ったけど、結果、だめだった...)</p> <h3 id="ためした方法とまとめ"><a href="#%E3%81%9F%E3%82%81%E3%81%97%E3%81%9F%E6%96%B9%E6%B3%95%E3%81%A8%E3%81%BE%E3%81%A8%E3%82%81">ためした方法とまとめ</a></h3> <p>試したのは以下の4パターン。<br /> SVG+<a target="_blank" rel="nofollow noopener" href="https://sharp.pixelplumbing.com/">sharp</a>で試した感じ。<br /> node-canvasはおまけな感じ程度。</p> <ol> <li>sharp+SVG(style+foreginObject) <ul> <li>SVGですべて構成する方法。</li> <li>フォントとか位置、サイズはstyleで設定</li> <li>外部URLの画像が取得できずにNG</li> </ul></li> <li>sharp+SVG+画像は別で合成 <ul> <li>1.の画像部分を別で合成するパターン</li> <li>カスタムフォントが設定できずNG</li> </ul></li> <li>sharp+SVG+ImageMagick <ul> <li>画像の合成部分のみsharpをつかい</li> <li>枠線をSVG、文字はImageMagickで書き出し</li> <li>これを採用</li> </ul></li> <li>node-canvas+SVG <ul> <li>node-canvasもSVGを入力にできるようなので試した</li> <li>1.や2.と同様の理由でNG</li> </ul></li> </ol> <p>内部で利用しているSVGライブラリ自体で未対応っぽいので、<br /> SVGのstyleや外部URLの画像、カスタムフォントなどは、<br /> ブラウザ上以外の画像生成ではまだ未対応っぽい。</p> <p>ほかにも、CloudFunction上でpuppeteerを使えるようだけど、<br /> メモリをすごい使うっぽい記事を見てしまい、まだためしてない。。</p> <hr /> <p>以下試したコード。</p> <h4 id="SVG(style+foreginObject)"><a href="#SVG%28style%2BforeginObject%29">SVG(style+foreginObject)</a></h4> <ul> <li>sharpが、foreignObject+sytleに未対応...のためNG</li> <li>imageに外部URLを使っているとダメっぽい...</li> </ul> <pre><code class="typescript">import sharp from "sharp"; await sharp("./input.svg") .png() .toFile("./output.png"); </code></pre> <pre><code class="xml"><?xml version="1.0"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 1200 630" width="1200" height="630"> <style> @import url("https://fonts.googleapis.com/css?family=Noto+Sans+JP:500&amp;display=swap&amp;subset=japanese"); .contents { display: flex; justify-content: center; align-items: flex-end; width: 1200px; height: 630px; position: relative; } .book { height: calc(100% - 100px); } .text-contents { position: absolute; left: 0; right: 0; bottom: 58px; width: 1200px; background-color: rgba(0, 0, 0, 0.6); font-family: "Noto Sans JP", sans-serif; color: white; padding-bottom: 8px; text-align: center; } .text-label { font-size: 60px; font-weight: 500; padding-left: 0.5em; } .title-label { font-size: 28px; font-weight: 500; padding-top: 0.2em; } </style> <image xlink:href="https://.../background.png" width="1200" height="630" /> <foreignObject requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" width="1200" height="630"> <div class="contents"> <img class="book" src="https://.../thumbnail.png" /> <div class="text-contents"> <div class="text-label"> <span>読みます!!</span> </div> <div class="title-label"> <span>リーダブルコード</span> </div> <div class="title-label"> <span>ダスティン・ボズウェル/トレバー・フォシェ</span> </div> </div> </div> </foreignObject> </svg> </code></pre> <h4 id="SVG+外部URLの画像"><a href="#SVG%2B%E5%A4%96%E9%83%A8URL%E3%81%AE%E7%94%BB%E5%83%8F">SVG+外部URLの画像</a></h4> <ul> <li>SVGには文字とかだけにして、画像はファイル読み込みで対応</li> <li>カスタムフォントが使えない...のNG</li> <li>GAEかlamdaだと、fontconfigに関する環境変数を設定できるが、Cloud Functionでは無理そう</li> <li>fonts.confを設定してもダメだった...</li> </ul> <pre><code class="typescript">import axios from "axios"; import sharp from "sharp"; // 埋め込む画像のURL const bookURL = "https://.../thumbnail.png"; const bookBuffer = await axios.get(bookURL, { responseType: "arraybuffer" }); // URLから取得した画像を加工(リサイズ) const bookImage = await sharp(bookBuffer.data) .resize(520, 454, { position: "top" }) .toBuffer(); // sharpで結合 await sharp("./background.png") // 背景画像を読み込み .composite([ { input: bookImage, gravity: "south" }, 本の画像を書き出し { input: "./input.svg" } // SVGの書き出し ]) .png() .toFile(tempFile); </code></pre> <pre><code class="xml"><?xml version="1.0"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 1200 630" width="1200" height="630"> <defs> <!-- Fontを外部URLで指定 --> <font-face font-family="Noto Sans JP"> <font-face-src> <font-face-uri xlink:href="https://fonts.googleapis.com/css?family=Noto+Sans+JP:400,700|Roboto:400,700&amp;display=swap&amp;subset=japanese" /> </font-face-src> </font-face> <!-- フォントをローカルファイルで指定 --> <font-face font-family="Noto Sans JP" font-weight="500"> <font-face-src> <font-face-uri xlink:href="./font/NotoSansJP-Medium.otf"> <font-face-format string="opentype"/> </font-face-uri> </font-face-src> </font-face> <font-face font-family="Noto Sans JP" font-weight="700"> <font-face-src> <font-face-uri xlink:href="./font/NotoSansJP-Bold.otf"> <font-face-format string="opentype"/> </font-face-uri> </font-face-src> </font-face> <!-- ローカルファイルを@font-faceで指定 --> <style type="text/css"> @font-face { font-family: 'Noto Sans JP'; font-style: normal; font-weight: 500; src: url('./font/NotoSansJP-Medium.otf') format("opentype"); } @font-face { font-family: 'Noto Sans JP'; font-style: normal; font-weight: 700; src: url('./font/NotoSansJP-Bold.otf') format("opentype"); } </style> </defs> <rect x="0" y="530" width="1200" height="100" fill="#000000" fill-opacity="0.6" /> <text x="610" y="156" font-size="60" fill="#FFFFFF" text-anchor="middle"> <tspan font-weight="700">読みます!!</tspan> </text> <text x="600" y="574" font-size="28" fill="#FFFFFF" text-anchor="middle"> <tspan font-weight="700">リーダブルコード</tspan> </text> <text x="600" y="610" font-size="24" fill="#FFFFFF" text-anchor="middle"> <tspan font-weight="500">ダスティン・ボズウェル/トレバー・フォシェ</tspan> </text> </svg> </code></pre> <h3 id="SVG+ImageMagick"><a href="#SVG%2BImageMagick">SVG+ImageMagick</a></h3> <ul> <li>画像の加工や合成はsharpを使い、文字の書き出しだけImageMagickを使う</li> <li>画像の加工・合成部分は、上の「SVG+外部URLの画像」と同じ感じ</li> <li>ImageMagickで文字を書くのは以下の記事を参照 <ul> <li><a target="_blank" rel="nofollow noopener" href="https://www.memory-lovers.blog/entry/2019/06/26/194500" target="_blank" rel="noopener">Cloud Functions + ImageMagickでOPG画像の動的生成してCloud Storageにアップロードする - くらげになりたい。</a></li> </ul></li> </ul> <h3 id="node-canvas+SVG"><a href="#node-canvas%2BSVG">node-canvas+SVG</a></h3> <ul> <li>同じくダメ。imageタグの外部URLがNG+foreignObject未対応...</li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/Automattic/node-canvas">Automattic/node-canvas: Node canvas is a Cairo backed Canvas implementation for NodeJS.</a></li> </ul> <h2 id="こんなのつくってます!!"><a href="#%E3%81%93%E3%82%93%E3%81%AA%E3%81%AE%E3%81%A4%E3%81%8F%E3%81%A3%E3%81%A6%E3%81%BE%E3%81%99%21%21">こんなのつくってます!!</a></h2> <p>積読用の読書管理アプリ 『積読ハウマッチ』をリリースしました!<br /> <a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">積読ハウマッチ</a>は、Nuxt.js+Firebaseで開発してます!</p> <p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/572d4947-f40b-e4dc-1c9c-bc584cd2a66c.png" width="200"/></p> <p>もしよかったら、遊んでみてくださいヽ(=´▽`=)ノ</p> <p>要望・感想・アドバイスなどあれば、<br /> 公式アカウント(<a target="_blank" rel="nofollow noopener" href="https://twitter.com/MemoryLoverz">@MemoryLoverz</a>)や開発者(<a target="_blank" rel="nofollow noopener" href="https://twitter.com/kira_puka">@kira_puka</a>)まで♪</p> <h1 id="参考にしたサイト様"><a href="#%E5%8F%82%E8%80%83%E3%81%AB%E3%81%97%E3%81%9F%E3%82%B5%E3%82%A4%E3%83%88%E6%A7%98">参考にしたサイト様</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://blog.kozakana.net/2019/04/sharp-resize/">Node.jsのライブラリsharpでリサイズを試してみる | Simple is Beautiful.</a></li> <li><a target="_blank" rel="nofollow noopener" href="http://cream-worker.blog.jp/archives/1073895056.html">くりーむわーかー : axiosで取得した画像データを保存とか表示とかする</a></li> </ul> きらぷか@積読ハウマッチ/SSSAPIなど tag:crieit.net,2005:PublicArticle/15695 2020-01-24T15:25:45+09:00 2020-01-24T15:25:45+09:00 https://crieit.net/posts/SVG-OGP-PNG-PNG SVGでOGP用のPNG画像を生成してみる(折り返し文字、画像埋め込み、PNG化) <p>SVGでOGP画像を作りたいなと思い、いろいろ調べたときの備忘録。</p> <p>SVGはまるのも多いけど、いろいろできるので、<br /> SVGからOGP画像をつくるのいいかもしれない。</p> <h2 id="とりあえず、SVGを書いてみる"><a href="#%E3%81%A8%E3%82%8A%E3%81%82%E3%81%88%E3%81%9A%E3%80%81SVG%E3%82%92%E6%9B%B8%E3%81%84%E3%81%A6%E3%81%BF%E3%82%8B">とりあえず、SVGを書いてみる</a></h2> <p>ちょろっとなるなら、HTMLでベタ書きしてみるのもいい。<br /> 動作確認とか楽ちん。</p> <pre><code class="html"><html> <body> <svg viewbox="0 0 1200 630" width="1200" height="630" style="background-color: lightgray;"> <rect x="550" y="265" width="100" height="100" fill="blue" /> <circle cx="550" cy="265" r="30" fill="none" stroke="red" stroke-width="5" /> </svg> </body> </html> </code></pre> <p>こんな感じ。</p> <p><a href="https://crieit.now.sh/upload_images/0996c11f41d0943bf90efa5baebbc3af5e2a8d8a9963d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/0996c11f41d0943bf90efa5baebbc3af5e2a8d8a9963d.png?mw=700" alt="スクリーンショット 2020-01-23 16.15.07.png" /></a></p> <p><code><rect></code>で四角を書いて、<code><circle></code>で丸を書いてる。</p> <p>上から順に描画されるので、下にある方が前面にくる感じ。<br /> なので、四角の上に丸が描画されてる。</p> <p><code><rect></code>の座標(x,y)は左上の場所なので、<br /> 横幅と縦幅分の半分だけ、全体の中心からずらしてる。</p> <h2 id="画像化する"><a href="#%E7%94%BB%E5%83%8F%E5%8C%96%E3%81%99%E3%82%8B">画像化する</a></h2> <p>最終的にOGPで使うPNG画像にしたい。</p> <p>画像化する方法について、ローカルとブラウザ上の2つを試した。</p> <h4 id="1. ローカルで画像化する"><a href="#1.+%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%A7%E7%94%BB%E5%83%8F%E5%8C%96%E3%81%99%E3%82%8B">1. ローカルで画像化する</a></h4> <p>いろんな画像フォーマットに対応しているsharpがよさそう。<br /> ・<a target="_blank" rel="nofollow noopener" href="https://sharp.pixelplumbing.com/">sharp - High performance Node.js image processing</a></p> <p>.svgファイルを.pngに簡単にできる。</p> <h5 id="sharpでsvgをpngに変換してみる"><a href="#sharp%E3%81%A7svg%E3%82%92png%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">sharpでsvgをpngに変換してみる</a></h5> <p>まずは、.svgとして扱えるように、<br /> xmlnsとかをちゃんとつけたsample.svgを用意する。</p> <pre><code class="xml"><!-- sample.svg --> <?xml version="1.0"?> <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 1200 630" width="1200" height="630" fill="lightgray"> <rect x="550" y="265" width="100" height="100" fill="blue" /> <circle cx="550" cy="265" r="30" fill="none" stroke="red" stroke-width="5" /> </svg> </code></pre> <p>パッケージをインストール</p> <pre><code class="shell">$ npm i sharp </code></pre> <p>変換するコードはこんな感じ。</p> <pre><code class="javascript">// generate.js const sharp = require("sharp"); async function main() { await sharp("sample.svg") .png() .toFile("output.png"); } main().then(); </code></pre> <p>そして、実行。</p> <pre><code class="shell">$ node generate.js </code></pre> <p>すると、output.pngというPNGファイルを作成してくれる。</p> <p>ただ、<strong><code>svg</code>のstyleは反映してくれないので注意</strong>。</p> <pre><code class="javascript">// generate.js const sharp = require("sharp"); async function main() { await sharp("sample.svg") .png() .toFile("output.png"); } main().then(); </code></pre> <p>背景色をつける場合は、sharp側で設定すればOK</p> <pre><code class="javascript">// generate.js const sharp = require("sharp"); async function main() { await sharp("sample.svg") .flatten({ background: "lightgray" }) // 背景色 .png() .toFile("output.png"); } main().then(); </code></pre> <h3 id="2. ブラウザ上のSVGを画像化する"><a href="#2.+%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E4%B8%8A%E3%81%AESVG%E3%82%92%E7%94%BB%E5%83%8F%E5%8C%96%E3%81%99%E3%82%8B">2. ブラウザ上のSVGを画像化する</a></h3> <p>ユーザに情報を入力してもらった内容をSVGで表示して、<br /> OGP画像にするという感じが多いので、こっちがメイン。</p> <p>保存先のFirebase StorageがData URL形式に対応しているので、<br /> PNGのData URLが取得できればOK。</p> <p>大まかな流れは、こんな感じ。</p> <ol> <li>Canvasを用意する</li> <li>SVGを読み込む<code><image></code>を用意する</li> <li><code><svg></code>を文字列に変換</li> <li>作成した<code><image></code>にDataURL形式でSVGをセットして、読み込み開始</li> <li>読み込みが完了したら、Canvasに書き出して</li> <li>CanvasからDataURLを取得する</li> </ol> <pre><code class="typescript">// svg2DataURL.ts /** * svgをpngに変換 * @param svgElement <svg>のHTML要素 */ export default function svg2DataURL( svgElement: HTMLElement ): Promise<HTMLCanvasElement> { return new Promise((resolve, reject) => { // 1. Canvasを用意する const canvas = document.createElement("canvas"); canvas.width = 1200; canvas.height = 630; const ctx = canvas.getContext("2d"); if (!ctx) { reject(Error("Create Canvas Error...")); return; } // 2. SVGを読み込む<image>を用意する const image = new Image(); image.decoding = "async"; image.onload = () => { // 5. 読み込みが完了したら、Canvasに書き出して、 ctx.drawImage(image, 0, 0, 1200, 630); // 6. CanvasからDataURLを取得する resolve(canvas.toDataURL()); }; image.onerror = e => reject(e); // 3. <svg>を文字列に変換 const svgXml = new XMLSerializer().serializeToString(svgElement); const svgData = btoa(unescape(encodeURIComponent(svgXml))); // 4. 作成した<image>にDataURL形式でセットして、読み込み開始 image.src = `data:image/svg+xml;charset=utf-8;base64,${svgData}`; }); } </code></pre> <p>あとは、好きなタイミングで呼び出せばOK。</p> <p>Nuxt.jsでの例はこんな感じ。</p> <pre><code class="html"><template> <div> <!-- document.getElementByIdできるように、idをつけておく --> <svg id="svg" viewbox="0 0 1200 630" width="1200" height="630" style="background-color: lightgray;"> <rect x="550" y="265" width="100" height="100" fill="blue" /> <circle cx="550" cy="265" r="30" fill="none" stroke="red" stroke-width="5" /> </svg> </div> <div> <a class="button" @click="saveSVG">画像を保存</a> </div> </template> <script lang="ts"> import { Component, Vue } from "nuxt-property-decorator"; // 初期化済みのfirebaseインスタンス。詳細は略 import firebase from "~/plugins/firebase"; import svg2DataURL from "./svg2DataURL"; @Component() export default class SaveSvgPage extends Vue { // 画像を保存する処理 async saveSVG() { // svgのHTML要素を取得 const elm = document.getElementById("svg"); if (!elm) return; // さっきの処理: HTML要素からDataURLを取得 const dataURL = await svg2DataURL(elm); // Cloud Storage for Firebaseへ保存 const filePath = "...保存する先のパス..." const storage = firebase.storage(); const fileRef = storage.ref().child(filePath); await fileRef.putString(dataURL, "data_url"); } </script> </code></pre> <p>これでSVGがCloud Storage for FirebaseにPNG画像で保存できる(<em>´ω`</em>)</p> <h4 id="ハマったポイント"><a href="#%E3%83%8F%E3%83%9E%E3%81%A3%E3%81%9F%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88">ハマったポイント</a></h4> <h5 id="1. 画像化するときにhtml2canvasを使うといいかんじにならない"><a href="#1.+%E7%94%BB%E5%83%8F%E5%8C%96%E3%81%99%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%ABhtml2canvas%E3%82%92%E4%BD%BF%E3%81%86%E3%81%A8%E3%81%84%E3%81%84%E3%81%8B%E3%82%93%E3%81%98%E3%81%AB%E3%81%AA%E3%82%89%E3%81%AA%E3%81%84">1. 画像化するときにhtml2canvasを使うといいかんじにならない</a></h5> <p>画像化する方法で<a target="_blank" rel="nofollow noopener" href="https://html2canvas.hertzen.com/">html2canvas</a>もあるけど、うまくいかなかった。。</p> <p>スクロール位置や画面サイズによって、<br /> うまく撮れるときと撮れない時があって、この方法に。。</p> <h5 id="2. ブラウザ上と保存した画像が違う"><a href="#2.+%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E4%B8%8A%E3%81%A8%E4%BF%9D%E5%AD%98%E3%81%97%E3%81%9F%E7%94%BB%E5%83%8F%E3%81%8C%E9%81%95%E3%81%86">2. ブラウザ上と保存した画像が違う</a></h5> <p>この後出てくる小ネタ集でスタイルで装飾する方法を使ったところ、<br /> うまくいかない感じに。。</p> <p>svg内にsytleをもたせたらうまくいった(<em>´ω`</em>)</p> <p>ブラウザ上 = 全体のCSSが適用<br /> 保存画像 = SVG配下のCSSのみ適用</p> <p>なので、SVGだけで完結しないといけない...</p> <p>フォントとかも指定しないと、見た目が変わってしまう。。</p> <h5 id="3. 背景画像など外部リンクがあるとうまく保存できない..."><a href="#3.+%E8%83%8C%E6%99%AF%E7%94%BB%E5%83%8F%E3%81%AA%E3%81%A9%E5%A4%96%E9%83%A8%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%8C%E3%81%82%E3%82%8B%E3%81%A8%E3%81%86%E3%81%BE%E3%81%8F%E4%BF%9D%E5%AD%98%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84...">3. 背景画像など外部リンクがあるとうまく保存できない...</a></h5> <p>Canvasの仕様っぽく、外部リンクでCORSで引っかかると画像が表示されないよう...</p> <p>SVGで完結するように、読み込んだ画像をDataURL形式で指定するようにしたらうまくいった(<em>´ω`</em>)</p> <p>「<strong>SVGで完結</strong>」が大事らしい。。</p> <h2 id="小ネタ集"><a href="#%E5%B0%8F%E3%83%8D%E3%82%BF%E9%9B%86">小ネタ集</a></h2> <h3 id="svg内でstyleが使える"><a href="#svg%E5%86%85%E3%81%A7style%E3%81%8C%E4%BD%BF%E3%81%88%E3%82%8B">svg内でstyleが使える</a></h3> <p><code><style></code>タグがあるらしい。<br /> classを設定して、いろいろできる。便利。</p> <pre><code class="html"><html> <body> <svg viewbox="0 0 1200 630" width="1200" height="630" style="background-color: lightgray;" > <style> .rect { fill: green; } </style> <rect class="rect" x="600" y="315" width="100" height="100"/> </svg> </body> </html> </code></pre> <h3 id="折り返し文字を表示してみる(foreignObject)"><a href="#%E6%8A%98%E3%82%8A%E8%BF%94%E3%81%97%E6%96%87%E5%AD%97%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B%28foreignObject%29">折り返し文字を表示してみる(foreignObject)</a></h3> <p>SVGにも<code><text></code>があるけど、自動折り返しに対応していない。<br /> 文字数を計算して自分で分割すればできるけど、めんどくさい。。</p> <p>文字の折り返ししたい場合、<code><foreignObject></code>という<br /> HTMLを追加できるのがあるので、それを使うといいらしい。</p> <p>styleでfont-sizeとかも設定できるのでいろいろできそう(<em>´ω`</em>)</p> <pre><code class="html"><html> <body> <svg viewbox="0 0 1200 630" width="1200" height="630" style="background-color: lightgray;" > <style> @import url("https://fonts.googleapis.com/css?family=Noto+Sans+JP:500&display=swap&subset=japanese"); .item { font-family: "Noto Sans JP", sans-serif; font-size: 60px; border: 2px solid green; } </style> <foreignObject requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" width="200" height="200" x="500" y="215" > <div class="item"> こんにちは </div> </foreignObject> </svg> </body> </html> </code></pre> <p>こんな感じ</p> <p><a href="https://crieit.now.sh/upload_images/c4318acbc1f8d438ed7eea4a3e11257a5e2a8da3252f2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/c4318acbc1f8d438ed7eea4a3e11257a5e2a8da3252f2.png?mw=700" alt="スクリーンショット 2020-01-23 17.44.58.png" /></a></p> <p>ただ、foreignObjectはサイズを自動計算してくれるわけではないので、<br /> widthとheightを設定しないといけない。</p> <h3 id="画像を表示する(image)"><a href="#%E7%94%BB%E5%83%8F%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%28image%29">画像を表示する(image)</a></h3> <p>SVGで画像を使うときは、<code><image></code>を使う。<br /> <code><img></code>とは違う。</p> <pre><code class="html"><html> <body> <svg viewbox="0 0 1200 630" width="1200" height="630" style="background-color: lightgray;" > <image xlink:href="https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png" width="200" height="200" x="500" y="215" /> </svg> </body> </html> </code></pre> <p>こんな感じ。</p> <p><a href="https://crieit.now.sh/upload_images/a6f51a0f42f98ba2aa818a0116e59f605e2a8db81f639.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a6f51a0f42f98ba2aa818a0116e59f605e2a8db81f639.png?mw=700" alt="スクリーンショット 2020-01-23 17.48.11.png" /></a></p> <p>ハマったポイントにも書いたとおり、このままCanvasに書き出すと、<br /> 保存時に画像が表示されないので、動的にDataURLを取得してセットするといい感じ。</p> <pre><code class="html"><html> <body> <svg viewbox="0 0 1200 630" width="1200" height="630" style="background-color: lightgray;" > <image xlink:href="data:image/svg+xml;charset=utf-8;base64,...略" width="200" height="200" x="500" y="215" /> </svg> </body> </html> </code></pre> <p>URLからDataURLに変換するのは、こんな感じ。</p> <pre><code class="typescript">// svg2DataURL.ts /** * URLをDataURLに変換 * @param 変換したい画像のURL */ export default function url2DataURL( url: string ): Promise<HTMLCanvasElement> { return new Promise((resolve, reject) => { // 1. Canvasを用意する const canvas = document.createElement("canvas"); canvas.width = 1200; canvas.height = 630; const ctx = canvas.getContext("2d"); if (!ctx) { reject(Error("Create Canvas Error...")); return; } // 2. SVGを読み込む<image>を用意する const image = new Image(); image.decoding = "async"; image.onload = () => { // 4. 読み込みが完了したら、Canvasに書き出して、 ctx.drawImage(image, 0, 0, 1200, 630); // 5. CanvasからDataURLを取得する resolve(canvas.toDataURL()); }; image.onerror = e => reject(e); // 3. 作成した<image>にURLでセットして、読み込み開始 image.src = url; }); } </code></pre> <h3 id="画面サイズに合うように表示する(resize対応)"><a href="#%E7%94%BB%E9%9D%A2%E3%82%B5%E3%82%A4%E3%82%BA%E3%81%AB%E5%90%88%E3%81%86%E3%82%88%E3%81%86%E3%81%AB%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%28resize%E5%AF%BE%E5%BF%9C%29">画面サイズに合うように表示する(resize対応)</a></h3> <p>OGP画像としていい感じのサイズで保存したいけど、<br /> 表示するときは画面サイズにあった感じにしたい。。</p> <p>svgにstyleが使えるので、リサイズ時にスケールを計算して、<br /> <code>transform: scale();</code>で縮小する感じにしてみた。</p> <pre><code class="html"><template> <div id="svg-wrapper" class="svg-wrapper"> <svg class="svg-content" :viewbox="`0 0 ${svgWidth} ${svgHeight}`" :width="svgWidth" :height="svgHeight" :style="style"> <rect x="550" y="265" width="100" height="100" fill="blue" /> <circle cx="550" cy="265" r="30" fill="none" stroke="red" stroke-width="5" /> </svg> </div> </template> <script lang="ts"> import { Component, Vue } from "nuxt-property-decorator"; @Component() export default class SvgPage extends Vue { private svgWidth: number = 1200; private svgHeight: number = 630; private scale: number = 1; mounted() { // マウント時にリサイズする this.$nextTick(() => this.handleResize()); // windowのresizeイベントのリスナーに登録して、 // 画面サイズが変わったら、スケールを再計算するようにする window.addEventListener("resize", this.handleResize); } beforeDestroy() { // 破棄されるときに、リスナーの登録を解除する window.removeEventListener("resize", this.handleResize); } // リサイズ用のスケールを計算する処理 private handleResize() { const elm = document.getElementById("svg-wrapper"); if (!elm) return; this.rect = elm.getBoundingClientRect(); this.scale = this.rect.width / this.svgWidth; } // **************************************************** // * computed // **************************************************** private get style() { // 計算したスケールで縮小するようにtransformを設定する return { transform: `scale(${this.scale})` }; } } </script> <style> svg { transform-origin: 0 0; } .svg-wrapper { position: relative; width: 100%; height: auto; } .svg-wrapper:before { content: ""; display: block; padding-top: 52.5%; /* 630 / 1200 x 100 */ } .svg-content { position: absolute; top: 0; left: 0; } </style> </code></pre> <p>ただ、このまま保存すると縮小されたままになるので、<br /> deepコピーでクローンして、transformをクリアしてから書き出すようにする。</p> <pre><code class="typescript">// svg2DataURL.ts /** * svgをpngに変換 * @param svgElement <svg>のHTML要素 */ export default function svg2DataURL( svgElement: HTMLElement ): Promise<HTMLCanvasElement> { return new Promise((resolve, reject) => { // deepコピーでクローン const elm = svgElement.cloneNode(true) as HTMLElement; // transformをクリア elm.style.transform = ""; // ... 略 // 3. <svg>を文字列に変換 // ※ transformを削除したelmでsvgの文字列を取得 const svgXml = new XMLSerializer().serializeToString(elm); // ... 略 }); } </code></pre> <p>以上!!</p> <h2 id="こんなのつくってます!!"><a href="#%E3%81%93%E3%82%93%E3%81%AA%E3%81%AE%E3%81%A4%E3%81%8F%E3%81%A3%E3%81%A6%E3%81%BE%E3%81%99%21%21">こんなのつくってます!!</a></h2> <p>積読用の読書管理アプリ 『積読ハウマッチ』をリリースしました!<br /> <a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">積読ハウマッチ</a>は、Nuxt.js+Firebaseで開発してます!</p> <p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/572d4947-f40b-e4dc-1c9c-bc584cd2a66c.png" width="200"/></p> <p>もしよかったら、遊んでみてくださいヽ(=´▽`=)ノ</p> <p>要望・感想・アドバイスなどあれば、<br /> 公式アカウント(<a target="_blank" rel="nofollow noopener" href="https://twitter.com/MemoryLoverz">@MemoryLoverz</a>)や開発者(<a target="_blank" rel="nofollow noopener" href="https://twitter.com/kira_puka">@kira_puka</a>)まで♪</p> <h1 id="参考にしたサイト様"><a href="#%E5%8F%82%E8%80%83%E3%81%AB%E3%81%97%E3%81%9F%E3%82%B5%E3%82%A4%E3%83%88%E6%A7%98">参考にしたサイト様</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/SVG/Element/image"> - SVG: Scalable Vector Graphics | MDN</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/HTML/CORS_enabled_image">画像とキャンバスをオリジン間で利用できるようにする - HTML: HyperText Markup Language | MDN</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/SVG/Element/foreignObject"> - SVG: Scalable Vector Graphics | MDN</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://kamoqq.info/post/nodejs-image-processor-sharp/">Node.js向け画像編集ライブラリSharp - kamoqq.info</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://sharp.pixelplumbing.com/">sharp - High performance Node.js image processing</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://forum.vuejs.org/t/does-vue-2-0-break-svg-foreignobject/2566">Does vue 2.0 break SVG foreignObject? - Get Help - Vue Forum</a></li> <li><a target="_blank" rel="nofollow noopener" href="http://nakajmg.hatenablog.com/entry/2019/08/30/133330">Vueでsvgファイルをいい感じに扱う - じまろぐ</a></li> <li><a target="_blank" rel="nofollow noopener" href="http://cocu.hatenablog.com/entry/2014/01/14/214917">svgにhtmlを組み込んで、テキストを折り返したりcanvasを使ったり - cocuh's note</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://inside.pixiv.blog/subal/7123">文字レイヤーを支える技術 - pixiv inside</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/yuukive/items/ede7c087843d2f7ef979">VueコンポーネントでWindowサイズ変更検知&値取得 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/haribote/items/b17d46b9679ce2fb2712">一発芸!SVGでHTMLを画像化する - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/SVG/Tutorial/SVG_Fonts">SVG Fonts - SVG: Scalable Vector Graphics | MDN</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://blanche-toile.com/web/css-font-face">CSSの@font-faceでGoogle Fontsのwebフォントを利用する方法 | Free Style</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/API/Window/onresize">window.onresize - Web API | MDN</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/ryounagaoka/items/a98f59347ed758743b8d">CSSだけでアスペクト比を固定するテク - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/serinuntius/items/3017fb6ef51cd47352f6">Vue.jsとFirebaseでOGP画像生成系のサービスを爆速で作ろう - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/you8/items/bc5cbe887101863b242b">画像生成してOGPに設定する - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://creive.me/archives/17653/#word-breakoverflow-wrap">【CSS】 テキストを折り返す方法!自動で改行・レスポンシブにも対応 | creive【クリーブ】</a></li> </ul> きらぷか@積読ハウマッチ/SSSAPIなど tag:crieit.net,2005:PublicArticle/15577 2019-12-03T19:52:41+09:00 2019-12-03T19:52:41+09:00 https://crieit.net/posts/SVG-Font-Awesome-Nuxt-Bulma SVGなFont AwesomeをNuxt+Bulmaで使ってみた <p>開発している<a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">Webサービス</a>で<a target="_blank" rel="nofollow noopener" href="https://fontawesome.com/">Font Awesome</a>のお世話になってる。<br /> ただ、Web Font版を使っていたので、読み込みが遅い。。</p> <p>SVG版だとアイコンごとにimportできるようなので、<br /> 性能改善の一環でやってみたときの備忘録。</p> <h3 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h3> <p>まずはnpmでパッケージをインストール。<br /> 使うものに応じて、追加が必要らしい。</p> <pre><code class="shell">// 必須 $ npm i --save @fortawesome/fontawesome-svg-core $ npm i --save@fortawesome/free-solid-svg-icons $ npm i --save@fortawesome/vue-fontawesome // オプション(fabとかfarとか使うなら) $ npm i --save @fortawesome/free-brands-svg-icons $ npm i --save @fortawesome/free-regular-svg-icons </code></pre> <h3 id="pluginsの準備"><a href="#plugins%E3%81%AE%E6%BA%96%E5%82%99">pluginsの準備</a></h3> <p>Nuxtで<a target="_blank" rel="nofollow noopener" href="https://github.com/FortAwesome/vue-fontawesome">vue-fontawesome</a>が使えるように、pluginを用意する。<br /> ここで使うアイコンを個別に設定</p> <pre><code class="typescript">// plugins/fontawesome.ts import Vue from "vue"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { config, library } from "@fortawesome/fontawesome-svg-core"; import { faTwitter } from "@fortawesome/free-brands-svg-icons"; import { faPlus } from "@fortawesome/free-solid-svg-icons"; // CSSはnuxt.config.tsで設定するので、vue-fontawesomeでは追加しないようにする config.autoAddCss = false; // 必要なアイコンをlibraryに追加 [ faPlus, faTwitter, ].forEach(v => library.add(v)); // グローバルコンポーネントとして登録 Vue.component("fa", FontAwesomeIcon); </code></pre> <h3 id="nuxt.config.tsの設定"><a href="#nuxt.config.ts%E3%81%AE%E8%A8%AD%E5%AE%9A">nuxt.config.tsの設定</a></h3> <p>cssと用意したプラグインを追加する</p> <pre><code class="typescript">// nuxt.config.ts import NuxtConfiguration from "@nuxt/config"; const config: NuxtConfiguration = { mode: "spa", css: [ // CSSを追加 "@fortawesome/fontawesome-svg-core/styles.css", ], plugins: [ // プラグインを追加 { src: "~/plugins/fontawesome", ssr: false }, ], }; </code></pre> <p>設定はこれでOK</p> <h3 id="使い方"><a href="#%E4%BD%BF%E3%81%84%E6%96%B9">使い方</a></h3> <p>使うときはこんな感じ。<br /> <code>plugins/fontawesome.ts</code>で設定した<code>fa</code>を使う感じ。</p> <pre><code class="html"><!-- xxx.vue --> <template> <div> <!-- fas fa-plusの場合: デフォルトがfas --> <fa icon="plus" aria-hidden="true" /> <!-- fab fa-twitterの場合 --> <fa :icon="['fab', 'twitter']" aria-hidden="true" /> </div> </template> </code></pre> <p>以上!!<br /> シンプルで簡単(<em>´ω`</em>)</p> <h3 id="ハマった..."><a href="#%E3%83%8F%E3%83%9E%E3%81%A3%E3%81%9F...">ハマった...</a></h3> <h4 id="Bulmaと一緒に使うと、うまくいかない?"><a href="#Bulma%E3%81%A8%E4%B8%80%E7%B7%92%E3%81%AB%E4%BD%BF%E3%81%86%E3%81%A8%E3%80%81%E3%81%86%E3%81%BE%E3%81%8F%E3%81%84%E3%81%8B%E3%81%AA%E3%81%84%EF%BC%9F">Bulmaと一緒に使うと、うまくいかない?</a></h4> <p><a target="_blank" rel="nofollow noopener" href="https://bulma.io">Bulma</a>を使ってるけど、一緒に使うとうまくいかない。。<br /> ちょっとだけ上にずれた感じなる。。</p> <pre><code class="html"><span class="icon is-small has-text-twitter"> <fa :icon="['fab', 'twitter']" aria-hidden="true" /> </span> </code></pre> <p>だと、 Font Awesomeの<code>.svg-inline--fa</code>(<a target="_blank" rel="nofollow noopener" href="https://github.com/FortAwesome/Font-Awesome/blob/516a62816c76255dc92ed55b906e9dca5a21b28b/js-packages/%40fortawesome/fontawesome-svg-core/styles.css#L7">GitHub</a>)+<code>.svg-inline--fa.fa-w-16</code>(<a target="_blank" rel="nofollow noopener" href="https://github.com/FortAwesome/Font-Awesome/blob/516a62816c76255dc92ed55b906e9dca5a21b28b/js-packages/%40fortawesome/fontawesome-svg-core/styles.css#L42-L43">GitHub</a>)で想定しているheightと<br /> Bulmaの<code>.icon</code>(<a target="_blank" rel="nofollow noopener" href="https://github.com/jgthms/bulma/blob/f52ac20682f6e86f717069fe274a296c0988fff2/css/bulma.css#L3211-L3217">GitHub</a>)とが異なり、ズレが起こるよう。。</p> <p>なので、Font Awesomeに合わせる形で、すこし変更する。</p> <pre><code class="css">.icon svg { width: 1em; height: 1em; } </code></pre> <p>また、それでもズレが起こる場合は、個別に<code>vertical-align</code>を設定して調整。</p> <pre><code class="css">.icon { vertical-align: -0.1em; } </code></pre> <p>もう少し良いやり方もありそうだけど、とりあえずこれで対処。。<br /> なお、Buefyだとうまくやってくれないみたいなので、<code><b-icon></code>とかは使えない。。</p> <p>以上!!</p> <h2 id="こんなのつくってます!!"><a href="#%E3%81%93%E3%82%93%E3%81%AA%E3%81%AE%E3%81%A4%E3%81%8F%E3%81%A3%E3%81%A6%E3%81%BE%E3%81%99%21%21">こんなのつくってます!!</a></h2> <p>積読用の読書管理アプリ 『積読ハウマッチ』をリリースしました!<br /> <a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">積読ハウマッチ</a>は、Nuxt.js+Firebaseで開発してます!</p> <p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/572d4947-f40b-e4dc-1c9c-bc584cd2a66c.png" width="200"/></p> <p>もしよかったら、遊んでみてくださいヽ(=´▽`=)ノ</p> <p>要望・感想・アドバイスなどあれば、<br /> 公式アカウント(<a target="_blank" rel="nofollow noopener" href="https://twitter.com/MemoryLoverz">@MemoryLoverz</a>)や開発者(<a target="_blank" rel="nofollow noopener" href="https://twitter.com/kira_puka">@kira_puka</a>)まで♪</p> <h1 id="参考にしたサイト"><a href="#%E5%8F%82%E8%80%83%E3%81%AB%E3%81%97%E3%81%9F%E3%82%B5%E3%82%A4%E3%83%88">参考にしたサイト</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://youj.work/post/nuxt0307/">[Nuxt]FontAwesomeで使いたいアイコンだけを使おう[Vue] - The YouJ Times</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/FortAwesome/vue-fontawesome#integrating-with-other-tools-and-frameworks">FortAwesome/vue-fontawesome: Font Awesome 5 Vue component</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/buefy/buefy/issues/554">Font-Awesome v5+ · Issue #554 · buefy/buefy</a></li> </ul> きらぷか@積読ハウマッチ/SSSAPIなど