CSS、Sassでの変数の書き方

Hugoブログを絶賛カスタム中です。
(テーマは「Clarity」を使っています)

このテーマではSassが使われているのですが、変数を用いて色やwidthの数値を設定しています。

そしてなぜかSassの中でcssのカスタムプロパティを使っていました。
しかも私にとって馴染みの薄いSASS記法...。

Sass(SCSS記法)でSassの変数を使うことはあっても、このテーマのような使い方はしたことがありませんでした。

今までカラーコードの統一ぐらいにしか利用できていないので、この機会に調べてまとめます。

CSSの変数・カスタムプロパティ

cssで変数の定義、呼び出し、ということができます。

参考記事です。

CSSで変数(カスタムプロパティ)を使ってみよう | Webクリエイターボックス

CSSの変数(カスタムプロパティ)便利な使い方を詳しく解説 | コリス

変数の定義

:root {
  --var_name: value;
}

--変数名: カラーコードや単位付きの数値など;

です。

値には''などクォーテーションは必要ありません。
また --sukima: margin などのように、プロパティ名は設定できません。

この場合は :root 以下の子要素で var_name という変数を呼び出すことができる設定になります。

要素に対してプロパティに当たる変数を設定するので「カスタムプロパティ」というのでしょうね。

この↑後に同じ変数に別の値を再定義することができます。

再定義は、子要素に対して、と
同要素に対してウィンドウサイズのメディアクエリなどで、行います。

※「再定義」と書いてますが、これはこのままこのような考え方でいいのか、それともスコープが異なるとはいえ同名の変数に値を入れるので「代入」として考えた方がいいのか、分からなくなってしまいました。

他の言語でもスコープの概念は出てきますし基礎的な部分だと思うので、判明したら修正しようと思います!

:root {
  --font-large: 20px;
  --font-small: 16px;
}

/* 子要素で再代入 */
.child {
  --font-large: 16px;
  --font-small: 14px;
}

/* メディアクエリで再代入 */
@media screen and (min-width: 769px) {
  :root {
    --font-large: 18px;
    --font-small: 12px;
  }
}

実はメディアクエリでの変数の定義・再定義は、CSS変数のみSass変数ではできません。

:root とは

:root - CSS: カスケーディングスタイルシート | MDN

CSS の :root 疑似クラスは、文書を表すツリーのルート要素を選択します。 HTML では、 :root は 要素を表し、詳細度が高いことを除けば html セレクターと同等です。

:root はグローバルの CSS 変数を宣言するのに便利です。

:hover などと同じく擬似クラスなんですね。

:root {} 内で定義した変数はグローバル変数になることは覚えておきます。

ところで、 E:root と書かれている記事もあったように、要素をくっつけて書くこともできるのでしょうか?
検証しました。

html(一部省略)

<html>
   :
  <h1>:rootとhtmlの検証</h1>
  <div>
    <p>あいうえお</p>
  </div>
   :
</html>

css

/* :rootよりも詳細度が高い */
html:root {
  color: pink;
}

/* html要素以外の要素を指定しても無効 */
p:root {
  color: green;
}

/* htmlよりも詳細度が高い */
:root {
  color: red;
}

html {
  color: blue;
}

p {
  color: green;
}

h1とp要素の文字色を確認しました。

p:roothtml:root を検証したところ、html:root は有効であることが分かりました。
要素の指定が入っている分、:root よりも詳細度が高いです。
おそらく E:root はhtml要素以外は使えないのだと思います。

html:root の使い所があるかは分かりませんが、使っているところを見たことがないので、個人的にはこれからも使わないです。

h1とp要素の文字色は、結果
h1:pink → html:rootcolor の指定がh1要素に継承された
p:green → 直接の要素指定によりp要素に継承されたpinkがgreenに上書きされた

でした。

検証したプロパティが color なのでここに「継承」が絡んでいて少しややこしいですが、 そもそもルート要素には継承するようなプロパティを設定することがほとんどだと思います。

そこで :roothtml どちらを使うかですが、好みの問題な気がしました。

カスタムプロパティの設定は :root で、普通のプロパティの設定は html にしてみたり、どちらかで統一してみたり。
変にごちゃまぜに使わなければいいのかと思います。

かなり脱線しました。

変数の呼び出し

.class {
  property: var(--var_name);
}

var(--変数名);です。

変数の呼び出しは、親要素で定義した変数が対象なので、 :root で定義すればすべての要素から呼び出すことができます。

Sass関数内にCSSの変数を呼び出せない

sass変数をcss変数(カスタムプロパティ)に渡す方法 - Qiita

Sassでこの書き方は駄目だそうです。

body { color : darken(var(--color-primary), 10%); }

darken() はSass関数です。

ちなみによく使う calc()CSSの関数なのでCSS変数は使えます。

Sassの変数

Sassの変数についても定義と呼び出しについて調べました。

【Sass実践編】Sassを使いこなす第一歩、「変数」の書き方と使い方! : ビジネスとIT活用に役立つ情報

変数の定義

$var_name: value;

$変数名: カラーコードや単位付きの数値など;

です。

セレクタの外にグローバル変数として定義ができますし、特定のセレクタの中でローカル変数として定義もできます。

変数の値の後ろに !global を付けるとグローバル変数扱いになるそうです。

メディアクエリ内でSass変数は定義できない

メディアクエリ内の変数設定はなかったことになります。

Sass(SCSS)

$color: red;

p {
  color: $color;  // ウィンドウサイズの幅が767px以下でもred
}

@media screen and (max-width: 767px) {
 $color: blue;
  // メディアクエリの中で変数を呼び出せば適用される
  // p {
  //   color: $color;
  // }
}

コンパイルcolor の値が決まりますが、メディアクエリは動的なので、この時点では青文字になるかどうかは分かりません。
よって color: blue; にはなりません。

メディアクエリの中で変数を呼び出せば適用されますが、↑この書き方はまるでSassの恩恵を受けていないので書かないほうがいいです。

ここはmixinを使って書くべきなのでしょうね。

一連の書き方が丁寧に載っていました。

【Sass】mixinでメディアクエリを管理する方法【応用編】 - TechnoBlog(テクノブログ)

変数の呼び出し

selector {
  property: $var_name;
}

プロパティの値の一部として変数を呼び出す場合は #{$var_name} と書きます。
これをインターポレーション(補完)といいます。

background-image: url(#{bg-image-path});

sassファイルでCSS変数とSass変数両方使う理由

ここまで見てきてどちらも一長一短であることが分かりました。

CSS変数 Sass変数
変数の定義 --変数名: 値; 要素に対して(プロパティ):rootグローバル変数 $変数名: 値; セレクタの外でグローバル変数
変数の呼び出し var(--変数名) プロパティの値 $変数名 関数の引数、プロパティの値の一部として呼び出す場合は #{$変数名}
Sass関数内
メディアクエリ

ざっくりしているのでもう少し表に加えていきたいです。
(内容があっているかどうかは少し心配です)

例のHugoテーマのSASSファイルを見てみても、まずSass変数を定義して、CSS変数でも同じ変数を定義して使えるところでどちらかの変数を呼び出す、ということをしていました。

まだ釈然としませんが、Sass変数をメディアクエリなどで効率的に使う方法を学べばわかるようになるのではないかと思います。

これでSassファイルをしっかりと読んで、Hugoブログのcssカスタマイズを進めていきたいと思います。

Hugo - メニュー、各項目の表示

前回でロゴやファビコンの変更ができたので、次はメニューなどの表示面を整えていきます。

理想のブログの構成

ただ記事が探しやすく見られればよいので簡単な構成にしました。

TOP
 ┗カテゴリー
  ┗記事
 ┗タグ
  ┗記事

About(筆者紹介のページ)などヘッダーに表示されていたメニューも不要なので消します。
多言語対応もできないので日本語一択で表示させます。

Light/Darkモードの切り替えはおもしろそうなので残しておこうと思います。

その前に調べました

前回の教訓を受けてドキュメントは見よう!ということで、使用しているテーマ「Clarity」のGitHubを見ました。 ごりごり翻訳ツールを使って解読しました。

設定ファイル、params.tomlの存在

ドキュメント(README.md)を読んで大まかに分かったことは、設定の変更はconfig.tomlファイルと、その中の [param] の部分で行うということでした。
ここでconfig.tomlファイルの [param] をいじっても反映されない問題に直面しました。
サイト名の変更をするときと同じです。

では別のファイルで設定されているのかと、params.tomlというファイルの存在を思い出しました。

あれこれ試してみましたが、同じ設定項目は、params.tomlの設定値が優先されるようです。
Hugoのドキュメントにありました。

Configure Hugo | Hugo

Configuration File
Hugo uses the config.toml, config.yaml, or config.json (if found in the site root) as the default site config file.

設定ファイル
Hugoは、config.toml、config.yaml、config.json(サイトのルートにある場合)をデフォルトのサイト設定ファイルとして使用します。

config.tomlはルートに置いているのでデフォルトの設定ファイルだそうです。

Configuration Directory
In addition to using a single site config file, one can use the configDir directory (default to config/) to maintain easier organization and environment specific settings.

  • Each file represents a configuration root object, such as params.toml for [Params], menu(s).toml for [Menu], languages.toml for [Languages] etc…
  • Each file’s content must be top-level.

設定ディレクト
サイトの設定ファイルだけでなく、configDirディレクトリ(デフォルトはconfig/)を使用して、組織や環境に応じた設定を簡単に行うことができます。

  • 各ファイルは、[Params]にはparams.toml、[Menu]にはmenu(s).toml、[Languages]にはlanguages.tomlというように、設定のルートオブジェクトを表します。
  • 各ファイルの内容はトップレベルでなければなりません。

「ルートオブジェクト」というのは、最上位の設定ファイル、ということでしょうか。
「トップレベル」というのは設定項目と値が入れ子になっていない、ということかなと思いました。

config.tomlファイルでは、

# [languages]
# config/_default/languages.toml

# [menus]
# config/_default/menus/menu.xx.toml

コメントアウトになっており(ちなみに [params] の部分には同じような # config/_default/params.toml といった記述がない)、# を取って有効にすると、画面が表示されなくなりました。
コマンドラインで「bare keys cannot contain '/'」というエラーが出ました)

これは、どうやら「[languages] は config/_default/languages.toml」で設定しています」というコメント のようです。
とんだ勘違いでした。

エラー内容の確認、大事ですね!

config.tomlファイルでは [params] のように [languages] を設定できました。
[languages] の下の [markup] を見ながら書いてみるとうまくいきました。

[languages]
# config/_default/languages.toml
  [languages.ja]
    title = "サイト名"

ただlanguages.tomlファイルが優先されるので、languages.tomlファイルで設定がある限りは、config.tomlの設定値は反映されません。

少し分かった...!

これで諸々の設定もスムーズにいく、ようになるかもしれません。

複数のサイトをHugoで作るとなった場合、configディレクトリ内に環境(あるいはサイト?)ごとにディレクトリ・設定ファイルを置いて管理することができる、というところまでは分かりました(Hugoのドキュメントより)。

その先が謎なのですが、複数サイトを作らないので、今回も謎なままで themes/hugo-clarity にあるexampleSiteについてはスルーしようと思います。

テンプレートについて

そしてHugoのテンプレート機能について調べました。

静的サイトジェネレータ「Hugo」と技術文書公開向けテーマ「Docsy」でOSSサイトを作る | さくらのナレッジ

Go言語では、text/templateやhtml/templateというテンプレート機構が標準で用意されている。Hugoではこのテンプレート機構をそのままレンダリングに使用している。

Go言語のテンプレート機構について、今度はGo言語のドキュメントを見ました。

template package - text/template - pkg.go.dev

template package - html/template - pkg.go.dev

Hugoを使うにあたり、Go言語自体の学習は不要であるという記事も見かけました。
それでもテンプレート機構の書式は理解してないとカスタマイズは難しいと感じました。

「{{」および「}}」で囲まれた部分はテンプレートエンジンによって記述された処理結果に置き換えられる。

などなど。

↑引用元の「さくらのナレッジ」に文法が載っていました。
引き続き調べながらカスタマイズしていきます。

こちらもテンプレートの構成について詳しく載っています。

Hugo をしばらく触ってなかったので、わかりやすくまとめ直した - ばうあーろぐ

メニューの修正

ようやく英語やHugoに馴染んできたところで、ヘッダーのメニューを変えていきます。

ClarityのGitHubより該当箇所を見つけました。

Main Menu To add, remove, or reorganize top menu items, edit the files here. Specifically look for items with main.

If you prefer the more traditional approach, delete content\config folder and enter a main menu entry inside the config.toml file

メインメニュー トップメニューの項目を追加、削除、再編成するには、ここでファイルを編集します。特に、mainが付いている項目を探してください。

より伝統的な方法をお望みの場合は、content\configフォルダを削除し、config.tomlファイル内にメインメニューの項目を入力してください。

[[main]] があるところは言語ファイルの config/_default/menus/menu.xx.toml でした。

メニューの設定が色々できそうですが、そもそもメニューが要らない。
どうしたものかと思いましたが、テンプレート側からごっそり外してしまいました。

すなわち layouts/partials/header.html です。

メニューの設定値を弄ったほうがいいのか迷いましたが、このときはconfig周りが謎だらけだったので、テンプレートをいじりました。

ここでまた1つ問題が。
スマホになるとメニュー部分が空になります。メニューボタンもなくなってしまいました。 スマホのメニュー部分にはタグやカテゴリーの一覧が表示されていてほしいです。

## スマホハンバーガーメニューにカテゴリー・タグメニューを入れたい

メニューは要らないと言ったものの、スマホではカテゴリーやタグからすぐリンクできるようにしたいので、ハンバーガーメニューにカテゴリー・タグ一覧を入れることにしました。

新しく入れるメニュー

これはPC表示だと右カラムに出てくる部分、すなわちsidebar.htmlのタグ・カテゴリー部分をコピーして入れました。

ここで少し問題が。ハンバーガーメニュー内はスクロールしない仕様のようです。

メニューだけならそんなに高さは取らないからですね。
こちらは後で直そうと思います。

既存のメニュー

当初はテンプレートからまるまる削除していましたが後々メニューを出したくなるかもしれないので、なるべくコードを消さない方向でやってみました。

1つ目は日本語用に作ったmenu.ja.tomlの [[main]]コメントアウトすることです。

こんな感じです。

# [[main]]
  # name = "Archives"
  # url = "post/rich-content/"
  # weight = -109

「Home」は一応残しておくことにしました。

「データがない」という状態にしておかないと、テンプレート側でdiv要素が作られたりします。

同様に、「多言語対応」の扱いを受けてしまっていたようで空のdiv要素が書き出されていました。
これはmenu.ja.tomlファイルの他に残しておいたmenu.en.toml、menu.pt.tomlファイルを削除することで解決しました。

ファイルごと削除は当初抵抗があったので、全てコメントアウトで解決しないか試してみたところ、ダメでした。

このあたりのテンプレートの条件分岐のコードが読めればいいのですが、よくわからないので探り探りです。

次回へつづく

これでようやく表示したい内容が揃いました(多分)!

全体の色やハンバーガーメニューの調整をして、一覧の記事、詳細の記事の部分を触っていきます。

Sassはsassファイル、SASS記法で書かれています。
慣れないですが良い機会です。

ひとまずおしまい。

gulpのアップロードタスクの書き方について

gulpを使うようになって設定するものの1つとして「ファイルアップロード」があります。

具体的には「vinyl-ftp」というプラグインを使っています。

vinyl-ftp - npm

最新の更新も4年前(2021年10月現在)と、更新が滞っているプラグインですが、FTPクライアントを使って1つ1つファイルを上げていた身としては、今でも重宝しています。

セキュリティ上SFTPで上げた方がいいのでは、と思うのですが、SFTPでしか上げられない場合とFTPでしか上げられない場合があり、
その違いや仕組みをまだ理解していません。

むしろFTP/SFTPでデプロイする方法を改めた方がいいのでしょうが、こちらも残念ながら勉強不足です。
GitHubからnetlifyを使ってHugoブログを作るような方法が現在はメジャーなのかと考えています)

今回は何回もうまくいったりいかなかったりしている「vinyl-ftp」の設定についてメモします。

ファイルの構成は以下のようになっています。
(フォルダには「/」を入れました)

***

/www
┗/テストドメイン
 ┗/res ←imgやcss、jsフォルダに各ファイルが入る
 ┗/wp
  ┗/wp-admin
  ┗/wp-content
  ┗/wp-includes
  ┗/src ←ローカルリポジトリにのみある
   ┗/scss ←scssファイルが入る
   ┗/node_modules
   ┗package.json
   ┗deploy-config.json
   ┗gulpfile.js ←ここに書くコードの話

***

最終的にうまくいったgulpは以下のように書きました。

gulpfile.js

const fs = require('fs');
const ftp = require('vinyl-ftp');
const { src, dest } = require('gulp');
// ↑const src = gulp.src; const dest = gulp.dest;

// サイトへアップロード
const config = JSON.parse(fs.readFileSync('./deploy-config.json', 'utf8'));
const conn = ftp.create(config['production']['ftp']);
const srcList = [
  '../../res/**/*',
  '../../' + THEME_PATH + '**/*',
  '!../wp-config.php',
  '!../src/',
  '!../wp-content/uploads/',
  '!../wp-content/plugins/'
];
const uploadProduction = () => {
  return src(srcList, { base: '../../', buffer: false })
    .pipe(conn.newer(FTP_UPLOAD_PATH))
    .pipe(conn.dest(FTP_UPLOAD_PATH))
};

// タスクの宣言
exports.up_prod = uploadProduction;

つまづき1:パーミッションエラー - newerとdestの部分

conn.newer

「vinyl-ftp」のドキュメントぺージの説明

Returns a transform stream which filters the input for files which are newer than their remote counterpart.

リモート側のファイルよりも新しいファイルの入力をフィルタリングする変換ストリームを返します。

gulp.dest

公式

dest() | gulp.js

Creates a stream for writing Vinyl objects to the file system.

意味はこちらのほうがわかりやすかったです。

gulp.dest | gulp 日本語リファレンス | js STUDIO

pipeされたものを、ファイルに書き出します。 渡された全てのデータが再度選択されるため、複数のフォルダに書き出すことが可能です。 フォルダが存在しない場合、自動的に生成されます。

conn.newerconn.dest の引数は接続先のパスを書きます。 conn.newer でリモートと比較してローカルで新しく更新があったものをチェックします。
その結果を受けて conn.dest で配置しています。

FTP_UPLOAD_PATH は定数にしています。

直した所は、この定数を /home などルートディレクトリから書くということです。

接続したディレクトリから相対的に(途中から)書いてしまうとエラーが出ます。
この例では /www と間違えて

Error: /www: Permission denied

とエラーが出ました。

試しにFTPのパスワードを変更してみると別のエラー(認証エラー)が出ました。接続はできているみたいです。

パーミッションで書き込み権限がないというのは、サーバー側の問題の場合もあるし、クライアント側の問題の場合もある。
と調べたら出てきました。

サーバー側はディレクトリのパーミッションを変えてみましたが元から書き込み権限があてられていたため当然変わらず。
クライアント側は、FTPクライアントでも同じアカウント情報で接続しているのに権限とは??という状態でした。

設定のパスをいろいろ試してみて解決しました。これでとりあえずファイルをアップロードすることはできました。

つまづき2:アップロード先がおかしい - srcのオプションbase

アップロードができるようになったものの、今度は対象のアップするファイルがすべて FTP_UPLOAD_PATHディレクトリ にアップされるようになってしまいました。

cssディレクトリもなく、cssファイルがサイトトップのディレクトリに配置されてしまいました。

const srcList = [] の書き方か、glob形式の書き方が間違っているのではと調べましたが、なかなか望む内容の記事に出会えず。

こちらの記事が頼りになりました。

gulpでFTPサーバにデプロイする - なおしむ論

そこで gulp.src のbaseオプションに着目しました。

「vinyl-ftp」のドキュメントには

base: Set as file.base, default is glob beginning. This is used to determine the file names when saving in .dest().

base:file.baseとして設定され、デフォルトはglob beginningです。.dest()で保存する際のファイル名の決定に使用されます。

さっぱり分からない...。

実はこの時点まで、何を思ったのか、不要だったbaseオプションを省いていました。

../../ は、このgulpfile.jsからサイトトップのディレクトリまでの相対パスを書いています。

過去に携わったサイトのgulpfile.jsを見てみるとbaseオプションが書かれていたので、真似て入れてみました。

すると、うまくアップロードされたのです。

道理がわからないのですが、ひとまず解決です☆

gulp.src

こちらも調べてみました。

gulp.src | gulp 日本語リファレンス | js STUDIO

アップロードに指定されたglob、またはglobの配列にマッチしたファイルを選択します。 プラグインとpipe可能なVinylファイルのストリームが返されます。

配列で書けます。こちらもgulpfile.jsからの相対パスで書きます。

glob形式の指定の書き方が何となくでしか理解できないのですが、いつかまとめられた記事が見つかるといいいです。

さいごに

なんとも後味の悪い終わり方ですが、うまくいったのです。

Vinylファイル、ストリームとは??
理解していないのでまたつまづく可能性がありますが、そのときはまた唸ることにします。

おしまい。

MAMPのエラーのはなし

MySQLのアップグレードをしようとしたときに起こったエラー

MAMPを起動させると、MySQLのアップグレードを促してきたので試したところ、エラーになってしまいました。

アップグレード前のMySQLのバージョンは5.7.30です。
(アップグレードしてなるはずだったバージョンは不明)

ひとまず閉じるとMAMPウィンドウの右上の電源アイコンがオレンジ色で「stop」と表示されました。
(「cloud」の雲アイコンが赤いのは前からだったはず)

f:id:yokoyoko_115:20211012004006p:plain
MAMPの画面

これがどういう状況なのか謎のまま、MySQLのアップグレードができそうな操作
[Tools > Upgrade MySQL databases...] を試しに押してみました。

すると再び同じエラーメッセージが出ました。

f:id:yokoyoko_115:20211012004635p:plain
エラー画面

エラーメッセージ

mysql_upgrade: Got error: 2002: Can't connect to local MySQL server through socket '/Applications/MAMP/tmp/mysql/mysql.sock' (61) while connecting to the MySQL server Upgrade process encountered error and will not continue.

mysql_upgrade: Got error: 2002: MySQLサーバーへの接続中に、ソケット「/Applications/MAMP/tmp/mysql/mysql.sock」を介してローカルのMySQLサーバーに接続できません(61)。 アップグレード処理でエラーが発生し、続行できません。

以下は、MAMPがうまく使えるようになるまでにあれこれしたことを挙げていきます。

MySQLサーバーが起動しているか確認

エラーメッセージから調べるとこちらの記事を見つけました。

[2002] MySQLのソケットエラー の原因と対処法 | 株式会社ビヨンド

まずはmysqldのプロセスがあるかどうか ps(process)コマンドを使って確認します。
プロセスがあればすなわちそのアプリケーション、mysqldが実行中か確認できるようです。

プロセスもちゃんと知っておいたほうが良さそうですね。

% ps aux | grep mysqld と入れると次のように返ってきました。

% ps aux | grep mysqld
username         3327   0.0  0.0  4259000    248 s000  U+   12:05PM   0:00.00 grep mysqld
username         3325   0.0  0.0        0      0   ??  Z    12:05PM   0:00.00 (mysqld)

これは起動していないっぽいです。ステータスの「Z」はゾンビ状態だそうです。

% sudo /etc/init.d/mysqld status

これでもMySQLサーバーの起動はできず。パスワードrootを入れてもできませんでした。3回パスワードを間違えて怖くなったのでやめました。
そもそも「init.d」ディレクトリが見つかりませんでした(この頃はいろいろと間違っている...ひどい過ち)。
※後ほど再チャレンジしました(後述)。

mysql.sockの確認

次に /Applications/MAMP/tmp/mysql/ を確認しました。

% ls /Applications/MAMP/tmp/mysql/
mysql.pid        mysql.sock     mysql.sock.lock

目視(Finder)では見つからなかったのにmysql.sockがあるようです。

MyQSLサーバーを起動させる

ソケット云々がなくてもMyQSLサーバーは起動させられるのだろうか、MyQSLサーバーが起動しないとそもそも接続の段階に移らないのではないか?
など考えてMyQSLサーバーを起動させる方法を調べてやってみました。

ためしたこと1:ログを削除してみる

MAMPでMySQL Serverが突然起動しなくなった時 → ログファイル削除 | シンプルシンプルデザイン

/Applications/MAMP/db/mysql57
ここにあるib_logfile0、ib_logfile1というファイルを削除してみます。怖いのでバックアップを取ります。

しかしMAMPを起動しても何も変わりませんでした。

ためしたこと2:my.confの作成

MacのMAMPのMySQLServerが起動しない時 - Qiita

/Applications/MAMP/conf
こちらに参考記事をもとにmy.cnfファイルを作りました。
こちらもMAMPを起動しても変わりませんでした;;
作成したmy.confは削除しました。

順番はMySQLの接続→起動??

今度はMySQLサーバーへの接続がうまくいっていないために起動しないのかなと思いました。
復習、MySQLとはデータベース管理システムの1つです。データベースを使うためのソフトウェアです。

MySQLサーバーの起動」とは、MySQLを起動させてサーバーにアクセスできるようにすること...?
起動はどのタイミングで行われるのか?

いろいろ考えても答えが出ないので引き続きあれこれしました。

MySQLにログインする

調べたら出てきました。
新しい行程「ログイン」。接続・起動よりは前っぽい印象です。

MAMPを使っていたらおそらくMAMP起動時にされていたのであろうMySQLのログインをコマンドラインから行ってみます。

調べたら % mysql -u root
でログインできます。とあるのですが「command not found: mysql」と返ってきました。

ターミナルでMySQLを使う準備〜Mac用 | IT工房|AI入門とWeb開発

そうでした。
mysqlを実行するパスが通っていないので、mysqlのあるところまで移動して実行する必要があります。

cd /Applications/MAMP/Library/bin/
% ./mysql -u root -p

すると最初に出てきたあのエラー!

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/Applications/MAMP/tmp/mysql/mysql.sock' (61)

ということは、MySQLにログインする時点でつまづいていた、ソケットに問題があるということなのでしょうか。

もう一度、ソケットファイルの確認。

MySQL mysql.socketがどこにあるか探したい。 - かもメモ

こちらに書かれている mysql_config コマンドでソケットファイルの場所が確認できるそうです。 /Applications/MAMP/Library/bin/ にはいろいろ実行ファイルがあって試してみたいです。

↑このディレクトリで
% ./mysql_config --socket
すると
/Applications/MAMP/tmp/mysql/mysql.sock

こう出ました。 やはりソケットファイルはあるみたいです。

ここまでのあれこれをまとめると、推測ですが、
my.cnfを作る

mysql.sockが生成される

mysql.sockがちゃんと使われているとMySQLが起動する

ということなのではないでしょうか。

試しに、再チャレンジとして、今度はパスに気をつけてMyQSLサーバー起動のコマンド % ./mysqld start
を打ってみました。

エラーの嵐!

最終的に
[Note] ./mysqld: Shutdown complete
とシャットダウンしていました。恐ろしい。
(サーバーの状態を確認する % ./mysqld status でも同じでした)

やはりソケットファイルの問題のようです。

mysql.sockが目視できないのが引っかかる

やっぱり実は実態がないのではないか(アップグレード処理の拍子にmysql.sockが作られたと勘違いされている)と思い、コマンドラインから空のmysql.sockファイルを作ってみます。

% touch mysql.sock

しかし何も変わらず。見えません。

ところでmysql.sock.lockとは?

どうしてここまで気に留めなかったのか。mysql.sock.lockを調べてみます。

MySQL5.7が起動できない。 - Qiita

この記事を読んでmysql.sock.lockファイルを削除してみよう、

とすると、VS Codeで開いてみたタイミングだったのか、mysql.sock.lockが消えていました!あとmysql.pidファイルもです。

ここでMAMPを起動してみました。

すると、オレンジの「stop」ボタンがグレーの「start」ボタンになっていました。 めでたくMAMPWordPressのサイトを見ることができました。

後で確認すると、MAMPが起動していないときはmysql.sock.lockファイルもなく、MAMP起動後にmysql.sock.lockが作られているようです。

またまた勝手な推測ですが、おそらくmysql.sock.lockでアップグレードのうまくいかないmysql.sockの設定がロックされてしまい、接続できなかったのではないでしょうか。

根本的な解決は、

MySQLのコンフィグファイルとクライアントソフトのコンフィグファイルのソケットを同じパスに設定する

なのだと思います。
多分。仕組みが謎です。

せめてコマンドラインからmysql.sockを開いてみればよかったー(遅い)

クライアントソフトはMAMPのことなのか、それぞれのファイルがどこにあるのか、ごちゃごちゃしてきました。

結局解決方法は何だったのか

ちゃんと道筋を立てたMAMPMySQLサーバー起動の流れは分からずじまいでした。

ここまで調べて偶然元に戻った印象です。

また、MyQSLサーバーの起動きっかけだったのか、MAMP画面の「start」ボタンを押して少しすると、半ば強制的にPCが再起動しました。 お、恐ろしい。

結局MyQSLのアップグレードも叶わず。作業の途中だったので変な操作しないようにしています。
このままアップグレードできなかったらのちのち困るはずです。

どうしたものか。

後に同じ問題になった時に、少しでも以前の出来事が問題改善、知識のアップデートにつながるといいです。

ひとまず様子見ます。おしまい。

Hugo - ファビコン、ロゴ、サイト名の変更

はてなブログからHugoを使ったブログの移行が全然進んでいないので、また少しずつ進めようと思います。

Hugoのテーマによって仕様が異なると思うので自分メモです。

もとのテーマ:clarity

Clarity | Hugo Themes

☆今回の作業をやった後に見つけた記事です。同じような方がいて嬉しい♪

Hugoテーマの選定(clarity)と表示カスタマイズ | damのブログラミング雑記ブログ

ファビコンの変更

svgファイルでファビコンを作りました。
svgファイルが表示されない場合に備えてpngファイルも用意しました。

<link rel="icon" type="image/svg+xml" href="http://localhost:1313/icons/favicon.svg">
<link rel="icon alternate" type="image/png" href="http://localhost:1313/icons/favicon.png">

このコードをどこに書くのか、ファビコンのファイルをどこに置くのかを探しました。

themeディレクトリの外に置いてファイルの上書きをする

/themes/hugo-clarity の中のファイルを編集しても、scssファイル同様、テーマ元が更新されれば書き換わってしまいます。
なのでtheme内の該当ファイルをコピーして、配置しました。

/themes/hugo-clarity/layouts/partials/favicon.html このファイルを
/layouts/partials/favicon.html
ディレクトリを作ってここへ置きました。

partialsディレクトリに入っている他の各htmlファイルも同じようにコピーしてthemeの外に置いてテーマのカスタマイズができるようです。

favicon.html

{{- $iconsDir := default "icons/" .Site.Params.iconsDir }}
{{- $appleTouch := absURL (printf "%s%s" $iconsDir "apple-touch-icon.png") }}
{{- $favicon := absURL (printf "%s%s" $iconsDir "favicon.svg" ) }}
{{- $faviconAlt := absURL (printf "%s%s" $iconsDir "favicon.png" ) }}
{{- $manifest := absURL (printf "%s%s" $iconsDir "site.webmanifest" ) }}
<link rel="apple-touch-icon" sizes="180x180" href="{{ $appleTouch }}">
<link rel="icon" type="image/svg+xml" href="{{ $favicon }}">
<link rel="icon alternate" type="image/png" href="{{ $faviconAlt }}">
<link rel="manifest" href="{{ $manifest }}">

今度はファビコン画像の置く場所を確認します。

/themes/hugo-clarity/static/icons にファビコンやアイコン関係の画像がいろいろありました。
favicon.icoもあったのですが、↑のコードには書かれていない様子。どこかで使われているのでしょうか。

ひとまず変更後の apple-touch-icon.pngfavicon.svgfavicon.png
/static/icons へ置きました。

favicon.svgが表示されない

はじめChromeで表示されませんでした。コードの src のパスを叩いても表示されるのに、デベロッパーツールのNetworkをみてもリクエストされていない。
Safariでは表示されるのにChromeで表示できない...「Can I use」を見てもChromeは対応しているはずなのに謎でした。
ローカル環境だからなのかとも考えました。

結局他のロゴやサイト名変更などいじっている間に表示されるようになりました。謎の時差。

ロゴの変更

もとのテーマではヘッダーとフッターにそれぞれロゴが表示されています。

partialsディレクトリのheader.html、footer.htmlを見てみると該当の部分がありました。
さらにheader.htmlには

{{- partialCached "logo" (dict "logo" $logo "class" "nav_hide") }}

とあったので、logo.htmlが使われていることがわかりました。

フッターの方は、ロゴファイル名の変更(ロゴもpngからsvgに変更しました)、ロゴファイルの参照先はファビコンと同じ場所、ということがわかりました。

ヘッダーは少しややこしそうでした。

header.html(一部抜粋)

{{- $params := .Site.Params }}
{{ $logo := $params.logo }}

{{- partialCached "logo" (dict "logo" $logo "class" "nav_hide") }}

変数を見て、.Site.Params.logo でロゴの何かを設定しているのだろうと推測しました。

/themes/hugo-clarity/exampleSite/config/_default/params.tomlは「Params」と関係していそうなので、その中の「logo」部分を探すと、

logo = "logos/logo.png"

すごくこれっぽいです。このファイルをコピーして
/config/_default/params.toml を作って logo = "logos/logo.svg に書き換えました。

ヘッダーのlogo.svgが表示されない

またまたChromeで表示されませんでした。不思議なことにフッターのロゴは表示されましたがヘッダーが表示されませんでした。

ロゴファイルの参照先はあっていて、デベロッパーツールのNetworkを見てもリクエストされているのに、Elementsで見ると「レンダリングサイズ」が0x0になっていました。

そこで「レンダリング」で調べると、imgタグに width``height を指定しているとレイアウトシフトが起こらなくなるという記事が出てきて、レンダリングにも関係するかと思い設定してみました。
するとレンダリングもされて表示されました。

またもや謎でした。フッターと何が違うのか。こちらも時間が経てば表示されるものなのかもしれません。

サイト名の変更

title やロゴ画像の alt をサイト名に変えたいです。
サイト名はきっと同じところで管理されているのだと思います。

Site.Title が見つからない

head.htmlやheader.htmlを見る限りでは、サイト名は .site.Title で設定されているようです。

head.html(一部抜粋)

{{- $title := "" }}

<title>{{ with $title }}{{ . }} {{ $separator }} {{ end }}{{ .Site.Title }}</title>

「Params」のパターンと同じであればtitle.tomlというファイルですが、ありませんでした。
「title」や「Clarity」でファイル内検索をして /config.toml を見つけました。

title = "Clarity"

こちらを変更しました。

しかしなぜか変わらない

<title> の中身を直接入れてしまえば出るのですが、トップページ以下のページが上手く出し分けできなくなってしまうので困りました。

多言語のtitle設定

さらに「Clarity」でファイル内検索をすると、/config/_default/languages.toml が見つかりました。

languages.toml

[en]
  title = "Clarity"
  LanguageName = "English"
  weight = 1

[pt]
  title = "Claridade" # just for the sake of showing this is possible
  LanguageName = "Português"
  weight = 2

weight = 1 の英語のtitle表示が優先されていたようです。

英語もポルトガル語も対応させる予定はありません。

試しに[ja] を作って他をコメントアウトしたところ、多言語選択のボタンから「Japanese」を選択すると /jaがurlに入るようになりました。

記事がなく真っ白になりました。

ファイルごと消してしまっていいのかよく分からないので、ひとまずすべてコメントアウトして取っておくことにしました。

そうするとconfig.tomlの title の値が無事反映されました。

今後の課題・調べること

探り探りでやっとロゴとサイト名が変更できました!

このまま進めていくには限界があるので以下を調べつつカスタマイズしていきます。

  • exampleSiteディレクトリは何に使われているか
    もしかするとthemeディレクトリの中にカスタマイズ用のテーマを作って、その中にsiteディレクトリ、という構成なのかもと思い始めています。
    ルートディレクトリにあれこれ置くのも、テーマやサイトの切り替えがなければ問題ないといいのですが。
  • Hugoの文法を理解しよう
    以前もGo言語を少し調べましたがすっかり忘れました。with とかif文のような文法がわからないとどうにもならなさそうです。
  • 英語を読もう
    ファイル内のあちこちにに英語でコメントがあるので、大事なヒントを拾っていきます。
    英語を地道に読んでいくことが近道になりそうです。

同時進行で各メニューやカテゴリの表示など整えていきたいです。

SVG画像の使い方

使うことが増えてきたSVG画像。
曖昧だった部分をまとめました。

SVGとは

SVG(Scalable Vector Graphics)です。
拡大縮小できるベクター画像

SVGベクトル形式なのに対し、JPEGPNG、GIFはビットマップ形式ラスター画像です。

Illustratorに出てくる「ラスタライズ」は、パスデータや埋め込み・リンク画像をラスター画像にします。

Photoshopにに出てくる「ラスタライズ」は、テキストレイヤーやシェイプレイヤー、レイヤースタイルなどをラスター画像にします。

パスになっているものはSVGに書き出せます。
(※テキストはそのままSVGに書き出せるのですが、パス情報ではなくテキストやフォントの情報が入っています)

SVGの書き出しから使い方まで詳しい記事です。

SVGを使うときに知っておくといいことをまとめました - Qiita

こちらも色々網羅されています。

HTML SVG の基本的な使い方 / Web Design Leaves

SVGといえば!詳しいページです。

svg要素の基本的な使い方まとめ

viewPortとviewBox

svg要素の属性である width, heightviewBox
考え出すといつも大混乱します。

viewPort(ビューポート) viewBox(ビューボックス)
意味 表示領域 描画領域
書き方(例) width="400" height="300" ※cssで指定するなら省略可 viewBox="0 0 400 300"
単位 単位が付けられる。ない場合はpx 単位は付けられない。利用単位(「座標系」における単位)

widthheight 属性は、img要素の同属性とほぼ同じと考えてよさそうですが、問題は viewBox 属性。

ビューポートとビューボックスの幅と高さの数値が一致していればわかりやすいのですが、違っていると途端にわからなくなります。

そこでこんな言葉を見つけました。

【SVG 基礎 vol.1】SVGとは・viewPortとviewBox・基本の書き方・埋め込み方法 - KDE BLOG

SVGにおける1座標(利用単位)は、width、heightをviewBoxで定義したX軸、Y軸のそれぞれの値で割ったサイズということになります。

ここは割り算が発生するんですね。

そこで以下のように考えるとわかりやすいのではないかと思いました。

  1. viewBoxの中の描画を考える
  2. それをviewPortに収める

viewBoxの中の世界は単位のない世界なので、まずは座標を結んで描画し、それを単位のあるviewPortに落とし込むイメージです。

f:id:yokoyoko_115:20210627025823j:plain

preserveAspectRatio

属性の名前です。意味は「アスペクト比の保持」です。
上に書いた収めるというのは、viewPortとviewBoxの縦横比が同じであれば、拡大・縮小してきっちり収まります。
縦横比が異なる場合は、preserveAspectRatio 属性の値によって収め具合が変わってきます。
性質的にviewbox属性を持っている要素にある属性だと思っています。

SVGの描画領域 - Qiita

preserveAspectRatio="xMidYMid meet" 「ビューポートの上下左右中央から縦横の比率を守って拡大縮小する」がデフォルトだそうです。
設定値がたくさんありますね。

少し考えてみました。

f:id:yokoyoko_115:20210627231421j:plain

数値を入れて確かめてみよう

こちらでviewPortとviewBoxの値を入れて確かめることができます。

SVG viewBox 確認サンプル / Web Design Leaves

できることならviewPortとviewBoxの数値を揃えて作りたいものです。

SVG内で使われている要素

svg要素の中でしかお目にかからない要素が出てくるので、未だに馴染みが薄いです。
その中でもよく目にする要素を調べました。

大方こちらに書かれています。

SVGの記述方法 - Qiita

全部はこんなにあります!

SVG 要素リファレンス - SVG: Scalable Vector Graphics | MDN

g要素

「g = group」。
svg内の各要素をグループ化するための要素です。
イラレで作ったものをsvgで保存する際オブジェクトをグループ化していると、svgファイルのソースではそのまま <g> でオブジェクト(<path><polygon>など)が囲まれています。

単純な1つのオブジェクト(円)だけならSVGの書き出しで <g> は付かないだろうと思いきや、

<g id="レイヤー_2" data-name="レイヤー 2">
  <g id="レイヤー_1-2" data-name="レイヤー 1">
    <circle class="cls-1" cx="46" cy="46" r="45.5"/>
  </g>
</g>

2重に付いていました。

1つのレイヤーに円を作っただけなのですが、謎です。

まとめて色をつけたい、変形させたいときなどは <g> で囲って設定します。
不要なg要素は削除していいと思います。

後述してますがg要素でもuse要素で参照(呼び出し)することができます。

path要素

パスで結んだ図形の要素です。
名前のついている図形(円や多角形など)はそれぞれ要素(<circle><polygon> など)が用意されています。
<path> では円などを描画できますが、主に複雑な図形を表します。

d属性

d - SVG: Scalable Vector Graphics | MDN

dは何の略なのだろう、というのが気になりました。
おそらく

d属性は描かれるパスを定義します。

とあるので、「Definition(定義)」のdなのではと推測しました。

値は直線や曲線などを示すコマンドを使って座標を打っていくのですが、手動でここをいじることはまずないと思うので、ざっくりMやZなどを知っておこうと思います。

polygon要素、circle要素

それぞれ多角形、円の要素です。楕円はellipse要素です。

points属性

polygon(多角形)、polyline(折れ線)要素で使います。x、y座標を入れます。
d属性と似ていますが、コマンドがなく、座標を直線で結びます。
pointsの値は見てなんとなく理解できそうです。

symbol要素

「symbol(記号、符号、マーク)」
use要素でオブジェクト(図形)を表示させるために使う要素で、オブジェクトを定義します。
symbol要素で囲ったオブジェクトは表示されません。

symbol要素はviewbox属性とpreserveAspectRatio属性を持っているので、symbol要素を使うときは、その親のsvg要素でviewbox属性を指定するよりはsymbol要素で指定した方が使い勝手がよいです。
両方でviewbox属性指定するとややこしいことになります。

こちらで検証されています。

SVGのsymbol、use要素とviewBox属性の挙動 - Kekeの日記

svg要素の中にはsymbol要素でオブジェクトを複数定義することができます。

そしてsymbol要素の中でのg要素の使い所は、symbol要素の中でまとめてスタイルを掛けたい(動き、色など)ときに意識して囲っていけばいいのかなと思います。

use要素でオブジェクトを参照するため、symbolまたはg要素に id を付けます。

defs要素

「Definitions(定義)」。これも「定義」なのではないかと思います。

symbol要素同様、defs要素で囲ったオブジェクトは表示されません。

defs - SVG: Scalable Vector Graphics | MDN

参照される要素は、可能なかぎりdefs要素内で定義されることが推奨されています。

一口に「参照」といっても、下に書いたuse属性以外にも参照の形があります。

上記のNDMのページを見てみると、

fill="url(#Gradient01)

参照元linearGradient というグラデーションを定義する要素です。

オブジェクトの「定義」もsymbol要素以外にlinearGradient要素など色々あるので、とりあえずdefs要素で囲っておきましょう。

<defs><linearGradient></linearGradient></defs>
<defs><symbol></symbol></defs> でいいはずです。

defs または symbol で囲われていないものも見かけるので、今後もいろいろコードを見て確認します。

また、定義部分が表示されないように一工夫必要です。

インラインSVGのdefsとgかsymbolとuseによる呼び出しのマークアップ

インラインSVGの呼び出しのマークアップは画像を定義する呼び出し元を表示したくない場合はデザインのCSSで親要素のsvgタグなどに非表示の「display:none」をかける。

defsタグやsymbolタグを使うとその場で描画しなくなるけれどもマークアップした分の高さが残ってしまうから余計な隙間を縮めるためにもCSSの「display:none」で要素として取り除くか、せめて「height:0」で高さをなくす必要が出て来る。

svg要素に display="none" をかけて非表示にする方法も見かけました。display属性があるのですね。

別記事で検証しました。

use要素

symbol要素で定義したオブジェクトを参照します。

オブジェクトの定義、参照は、それぞれ別のsvg要素で囲って使うことがほとんどです。

使い方(例)

<svg><use href="#idname" /></svg>

use要素のhref属性で定義元を参照します。定義が別ファイルで書かれていれば、リンク先のパスも書きます。

この href は、symbol要素で囲っていないid属性が付いているg要素のオブジェクトも参照できます。
このときsymbol要素とは違い、参照元になっているg要素のオブジェクトも表示されます。

SVG1.1 グループ化について(g, symbol, defs) - はしくれエンジニアもどきのメモ

SVG内で使われている属性

今度は属性に着目します。

こちらにいろいろ載っていました。

ASCII.jp:SVGの見た目は「属性」で書く――HTMLとの違い

SVGの属性の多くは、塗り色、線の色、線の太さといった図形の「見た目」を定義します。文書マークアップ言語であるHTMLでは、文脈に関係のない見た目はCSSで定義することが推奨されています。一方でSVGは、グラフィックのマークアップ言語なので、属性で見た目を定義することは一般的です。

このように書かれているので、SVGのコードの中で塗りつぶしの色や線の色、図形の変形なども属性で指定して良さそうです。
気兼ねなく使えます。

そして、図形の色や見た目に関わる属性はプレゼンテーション属性といい、CSSのプロパティでも同じく指定できる属性を指します。

data-name属性

SVGというよりもHTML5の知識です。

いまさら聞けない、HTML5カスタムデータ属性の基本と使いどころ – WPJ

これはカスタムデータ属性です。 スタイルではなくデータを格納するための属性です。

書き出し時に善意で付けてくれている場合がありますが、必要なければ削除しましょう。

transform属性

path要素やg要素にありました。

CSSのtransformプロパティと同じく、座標や角度を指定して移動・拡大縮小・回転を設定する属性です。

transform はプレゼンテーション属性です。
書き出すとたまに transform が指定されているときがありますが(特にtranslate)、
こちらも不必要に入っているときは図形の座標など値を修正してtransform を削除していきたいところです。

アウトライン(線)に関する属性

イラレで指定できるかのように、アウトラインに関する属性は結構あります。

SVGでアウトラインをカスタマイズしてみよう | Webクリエイターボックス

属性 内容
stroke アウトラインの色
stroke-width アウトラインの幅。単位なしでは座標に従う
stroke-opacity アウトラインの透明度
stroke-linecap アウトラインの端の形。 butt(デフォルト、端で切れる)、round(丸く)、square(四角く)
stroke-linejoin アウトラインの 角の形。miter(デフォルト、尖っている)、round(丸く)、bevel(尖りが切られている)
stroke-dasharray アウトラインの破線のパターン。線分、隙間、線分、隙間...と繰り返しのパターンを指定します(値が複数の場合があるので「array」なのですね)
stroke-dashoffset アウトラインの破線の開始位置

stroke-dasharrayの指定、フォトショやイラレでも未だ理屈がよく分かっていなかったのですが、分かった気がしました。

そしてstroke-dasharray と一緒に使うことが多い stroke-dashoffset を使って一筆書きのアニメーションも作れるのですね!(憧れ)

styleの優先度

ある図形に色を付ける場合、以下の方法があります。

  • インラインの属性:<path fill="red" />
  • インラインのstyle属性:<path style="fill: yellow" />
  • style要素:<style>.style-element { fill: blue; }</style>
  • 別ファイルのCSS.css-file { fill: green; }

優先度の順番を調べてみました。

HTML

<svg width="200" height="200" viewBox="0, 0, 200, 200" xmlns="http://www.w3.org/2000/svg">
  <style>
    .style-element {fill: blue;}  <!-- blue②番目 -->
  </style>
  <circle class="style-element css-file" style="fill: yellow" fill="red" cx="100" cy="100" r="90" />  <!-- yellow①番目、red④番目 -->
</svg>

CSS

.css-file {
  fill: green;  // green③番目
}

よって、

  1. インラインのstyle属性:<path style="fill: yellow" />
  2. style要素:<style>.style-element { fill: blue; }</style>
  3. 別ファイルのCSS.css-file { fill: green; }
  4. インラインの属性:<path fill="red" />

このような順番で適用されていました。

1〜3はCSSの優先度そのままです。
プレゼンテーション属性(覚えたて)を使ったスタイルは優先度が低いことを覚えておこうと思います。

その他 - 波のアニメーション

波が動くアニメーションを実装する際にこちらを参考にしました。

SVGとanimationで滑らかな動きの波っぽいやつ | かちびと.net

波の1サイクルの部分の図形を左から右へ、繰り返し動かしていると波に見えます。

作りたかったのは小さな波だったので、background-repeatで波のSVG画像を表示させてアニメーションさせました。
(これならpng画像でやっても同じですね)

複数の波を重ねる場合は、 use を使うといいですね。
SVG側では波の位置・色、cssでは use に動きの指定をしています。

このあたりの指定の仕方は個人の好みやアニメーションの仕様にもよるのかと思います。

また波のアニメーションの中でこちらも見ました。

[CSS]テキストリンクのホバー時に、波線をうにょうにょとアニメーションさせるスタイルシートのテクニック | コリス

テキストhover時に波アニメーション付きのSVG画像を背景画像にしています。

波の動かし方は1つ目の参考ページと同じなのですが、
驚く点は、CSSファイルのインラインでSVG画像を指定しているところです。
background-image: url("data:image/svg+xml;... と入っています。

他の拡張子の画像ではできない、SVGならではの力技に見えました。

さいごに

アイコンとして使うことが増えてきたSVGですが、アイコンやイラストなど、いろいろなもののアニメーションを探していきたいです。

さっそく別の記事で練習してみました。

記事を通して基本的な部分は分かってきました。よかった...!

おしまい。

SVGで図形を書いてみよう

練習にイチからSVGを書いてみようと思いました。

use を使った物を作りたいので、同じ図形が複数出てくるもの...五輪!

今にぴったりなので作ってみることにしました。

著作権の問題でNGでした

もしやと思って調べたところ、

オリンピック・パラリンピックに関する知的財産等の無断使用および不正使用ないし流用は法的にも罰せられます。

オリンピック・パラリンピックに関するエンブレム、ロゴ、用語、名称をはじめとする知的財産は、日本国内では「商標法」、「不正競争防止法」、「著作権法」等により保護されています。

フリー素材でロゴ画像が手に入るぐらいなので大丈夫そうですが、こわいのでやめておきます。
(すでに登録していた五輪画像が記事のサムネイルになってしまっている。どうしたらいいのでしょう)

他の画像を探さなくては。

目標の画像

オリンピック開催中に作ろうという期限もなくなったので、せっかくならいろいろな要素を盛り込んだものを作りたいと思い、下記イラレで作った画像を目指します。

f:id:yokoyoko_115:20210807012044j:plain

The 子供の絵!
子供の絵にはお日様が右上か左上によく描かれていますよね。太陽の存在感はすごい。

ポイントは

  • 虹:グラデーションをかける
  • 雲:useを使う、拡大縮小
  • 太陽:光の線でuseを使う

です。

あわよくば太陽の光の線が回転して、雲が右から左に動かせたらいいなと思います。

作ってみました

動かす前と動かしたあとでコードが若干変わりました。以下は動かしています。

See the Pen GORIN by yokoyoko (@yokoyoko_code) on CodePen.

コードについて

今回は虹・雲・太陽をそれぞれのSVGの図形として、それらをまとめて1つのSVG画像としています。

1つずつコードを整えていきます。

イラレでうまく作れず、ドーナツの半分の形にしたところ、グラデーションが崩れてしまいました。
パスだけ使って、グラデーションはsvgファイルを直接触ろうと思います。

イラレから書き出した虹SVG

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 380.46 190">
  <defs>
    <style>.cls-1{fill:url(#名称未設定グラデーション_350);}</style>
    <radialGradient id="名称未設定グラデーション_350" cx="190.23" cy="95" r="150.35" gradientUnits="userSpaceOnUse">
      <stop offset="0.7" stop-color="#c7659a"/>
      <stop offset="0.76" stop-color="#51527d"/>
      <stop offset="0.82" stop-color="#4facd2"/>
      <stop offset="0.88" stop-color="#369263"/>
      <stop offset="0.94" stop-color="#f4eb5f"/>
      <stop offset="1" stop-color="#d94f4f"/>
    </radialGradient>
  </defs>
  <g id="レイヤー_2" data-name="レイヤー 2">
    <g id="レイヤー_1-2" data-name="レイヤー 1">
    <path class="cls-1" d="M55.69,190a134.54,134.54,0,0,1,269.08,0h55.69C380.33,85,295.21,0,190.23,0S.13,85,0,190Z"/>
    </g>
  </g>
</svg>

path が虹の輪郭で、style で塗りつぶし(fill)をしています。
fill プロパティでurlを radialGradient に紐づけています。
そして radialGradientdefs で囲まれて定義され、見えなくなっています。

fill属性を使う

fill はプロパティと属性、どちらもあります。

この場合 style を使って path にfillプロパティをあてていますが、直接 path にfill属性を入れてもできます。

<path fill="url(#rainbow)" d="M55.69,190a134.54,134.54,0,0,1,269.08,0h55.69C380.33,85,295.21,0,190.23,0S.13,85,0,190Z"/>

これで style は削除できます。

radialGradient要素の設定

radialGradient は放射状のグラデーションを定義します。 cx、cy属性は、viewBoxのx、y座標を示し、r属性は半径を示します。

半円ドーナツ図形の中心がグラデーションの起点になってしまっていたので、xの座標は中央のままで、yの座標を図形の下辺にし、半径も半円ドーナツ図形をぴったり囲むように大きくします。

f:id:yokoyoko_115:20210808003235j:plain

ちなみに gradientUnits="userSpaceOnUse" についても調べましたが、分かりませんでした。

グラデーションとパターン – SVG 1.1 (第2版)

gradientUnits="userSpaceOnUse" の場合、 cx, cy, r, fx, fy は,グラデーション要素が参照された所における現在の利用座標系(すなわち fill あるいは stroke プロパティによりグラデーション要素を参照している要素の利用座標系)に対し, gradientTransform 属性で指定される変換を適用した結果の座標系における値を表す。

これを書かない、または gradientUnits="objectBoundingBox" (初期値)だと、虹が真っ赤になってしまったので今回の場合は必要です。

gradientTransform属性を知る時が来れば分かるようになるかもしれません。

イラレから書き出した雲SVG

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 121 80.67">
  <defs>
    <style>.cls-1{fill:#fff;}</style>
  </defs>
  <g id="レイヤー_2" data-name="レイヤー 2">
    <g id="レイヤー_1-2" data-name="レイヤー 1">
      <path class="cls-1" d="M96.51,15.85a24.73,24.73,0,0,0-4.33.4A24.45,24.45,0,0,0,51.61,7.41a24.4,24.4,0,0,0-38.27,24.1A24.48,24.48,0,1,0,44.5,67.38a24.47,24.47,0,0,0,45-3.58,24.48,24.48,0,1,0,7-47.95Z"/>
    </g>
  </g>
</svg>

雲は2つ出てくるのでuseを使ってみます。

viewBox,width,heightの値で雲の大きさ、x,yの値で雲の表示位置を設定する

1つのSVG画像として表示するにあたり、虹や雲など個別で書き出したSVG図形をどう配置するか混乱しましたが、MDNのページを見てこう考えました。

<symbol> - SVG: Scalable Vector Graphics | MDN

  1. まず定義側のviewBoxを考える。すでに書き出されたpathの座標(d)があるのでここはむやみに書き換えられない。viewBoxは図形いっぱいの枠と考える
  2. 定義側のwidth、heightを考える。これらの値に図形が拡大縮小する。そしてこのwidth、heightの値が呼び出し側の単位と合う
  3. 呼び出し側の use にwidth、height属性で大きさ、x、y属性で座標を指定して配置する

図にするとこういうイメージです。

f:id:yokoyoko_115:20210809014420j:plain

もし symbol にviewBox属性を指定していないと、たとえ symbol にwidth、height属性を指定していても use でwidth、height属性を指定して拡大・縮小できませんでした。もとの大きさのままです。
symbol にviewBox属性は必須

もしsymbol にwidth、height属性を指定していない場合、use でwidth、height属性を指定していれば拡大・縮小できます。
このとき use でwidth、height属性を指定していなければ呼び出し側のviewBoxの大きさいっぱいになります。
symbol または use でwidth、height属性を指定する

<use> - SVG: Scalable Vector Graphics | MDN

use 要素にあるx, y, width (en-US), height (en-US), href の各属性のみが参照される要素に設定されているものを上書きします。

複数の図形を拡大・縮小させて配置するなら use でwidth、height属性を指定することになりそうです。

ちなみに、widthかheight片方のみを指定した場合(もう片方が未指定)、widthかheightの初期値は呼び出し側のviewBoxの大きさになりますが、おそらくpreserveAspectRatioの値がデフォルトであることが原因で、呼び出した図形はviewBoxの範囲内で上下左右中央に配置されます。

x、y属性でうまく配置するには、width、height属性で図形ぴったりの比率で指定する必要がありそうです。

先ほどの虹も結局 use を使うことになりました。

太陽

最後は太陽です。

イラレから書き出した太陽SVG

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 115.39 115.39">
  <defs>
    <style>.cls-1{fill:#f08218;}.cls-2{fill:none;stroke:#f08218;stroke-miterlimit:10;stroke-width:4px;}</style>
  </defs>
  <g id="レイヤー_2" data-name="レイヤー 2">
    <g id="レイヤー_1-2" data-name="レイヤー 1">
      <circle class="cls-1" cx="57.7" cy="57.7" r="30.28"/>
      <line class="cls-2" x1="15.94" y1="57.7" y2="57.7"/>
      <circle class="cls-1" cx="57.7" cy="57.7" r="30.28"/>
      <line class="cls-2" x1="28.17" y1="87.22" x2="16.9" y2="98.49"/>
      <circle class="cls-1" cx="57.7" cy="57.7" r="30.28"/>
      <line class="cls-2" x1="57.7" y1="99.45" x2="57.7" y2="115.39"/>
      <circle class="cls-1" cx="57.7" cy="57.7" r="30.28"/>
      <line class="cls-2" x1="87.22" y1="87.22" x2="98.49" y2="98.49"/>
      <circle class="cls-1" cx="57.7" cy="57.7" r="30.28"/>
      <line class="cls-2" x1="99.45" y1="57.7" x2="115.39" y2="57.7"/>
      <circle class="cls-1" cx="57.7" cy="57.7" r="30.28"/>
      <line class="cls-2" x1="87.22" y1="28.17" x2="98.49" y2="16.9"/>
      <circle class="cls-1" cx="57.7" cy="57.7" r="30.28"/>
      <line class="cls-2" x1="57.7" y1="15.94" x2="57.7"/>
      <circle class="cls-1" cx="57.7" cy="57.7" r="30.28"/>
      <line class="cls-2" x1="28.17" y1="28.17" x2="16.9" y2="16.9"/>
    </g>
  </g>
</svg>

line は複数あるのは分かりますが何故か circle も同じ数だけあります!
各属性値も同じなので、8つの円が重なっているようです。7つは消します。

この line のx1、y1とx2、y2属性、1が始点で2が終点のx、y座標をあらわしています。
指定されていない場合、初期値は0です。

8つある line のうち1つを使って、太陽の縁を中心に45度ずつ回転させて表示させてみよう!

...と当初は思ったのですが、use で太陽の光の線を8回呼び出すのはどうなのか、それだと後々の「光の線を回転させる」が難しいのではと思い、まとめて呼び出すことにしました。

symbolを呼び出すのかgを呼び出すのか

太陽の図形は「円」と「光の線」で分けて出したいので、ざっくりと下記のようにしました。

定義側の太陽

    <symbol>
      <g>
        <circle>
      </g>
      <g>
        <line>
           :  (✕ 7)
      </g>
    </symbol> 

symbolg はid属性を振れば use で呼び出せます。
この場合は太陽まるごと呼び出すときは symbol 、「円」と「光の線」で2回呼び出すときは g を使ってできます。

2つは呼び出したときのhtmlへの出力が異なります。

symbol の場合は、symbolsvg に置き換わった形で出力されます。 中で g に「円」と「光の線」がそれぞれ囲まれています。 一方 g の場合はそのまま出力されます。

なので「円」と「光の線」で分けられていながら、1つの図形としてまとまっている symbol で呼び出すことにしました。

2つのg要素をさらにg要素で囲い、それを呼び出す方法もありますが、g要素は呼び出し側でwidthとheightの指定ができません。

symbolg の使い分けは、いろいろ作っていくうちに慣れてきそうな気がしました。
迷ったら symbol で囲います。

classとスタイルのあて方

pathguse など様々な要素でスタイルをあてることができるので、何がベストなのか大混乱です。

大事なのは定義側で指定したスタイルを呼び出し側で上書きできないということです。

雲のところで大きさの上書き指定は可能だがその他はできない、というのはこの事だったのですね!

定義側の pathfill: red とした場合、呼び出し側の usefill: blue と指定しても青にはなりません。

そう考えると、呼び出し側に統一してスタイルをあてた方が良いのでしょうか。
先の「symbolを呼び出すのかgを呼び出すのか」問題とも関係してくるので、状況によりけりなのかなと思いました。

ちなみにデベロッパーツールでスタイルを確認したときにつまづきました。定義側と呼び出し側、それぞれを確認しないといけないです。

定義側のsvgのスペースを削除する

SVG画像の前に、定義したsvg要素に謎の300x150pxの隙間ができました。

svgdisplay="none" を書いたり、csssvg { display: none; } を指定するとうまくいくと思ったのですが、
なんと虹が消えてしまいました。

虹で使っている radialGradientdisplay: none で無効になってしまったのだと思います。

そこで色々と試行錯誤したところ、

/* 定義側のsvg */
.svg-defs {
  display: block;
  width: 0;
  height: 0;
}

としたところ、隙間が消えてくれました。

他のケースでも使えるといいなと思います。

できあがったSVG(動かない)

これで虹・雲・太陽がそろいました!
大きさや位置が目分量ですが真似できました◎

HTML

<!-- 定義側 -->
<svg class="svg-defs">
  <defs>
    <!-- 虹 -->
    <symbol id="rainbow" viewBox="0 0 380.46 190">
      <radialGradient id="rainbow-gra" cx="190.23" cy="190" r="190.23" gradientUnits="userSpaceOnUse">
        <stop offset="0.7" stop-color="#c7659a"/>
        <stop offset="0.76" stop-color="#51527d"/>
        <stop offset="0.82" stop-color="#4facd2"/>
        <stop offset="0.88" stop-color="#369263"/>
        <stop offset="0.94" stop-color="#f4eb5f"/>
        <stop offset="1" stop-color="#d94f4f"/>
      </radialGradient>
      <path fill="url(#rainbow-gra)" d="M55.69,190a134.54,134.54,0,0,1,269.08,0h55.69C380.33,85,295.21,0,190.23,0S.13,85,0,190Z"/>
    </symbol>
    <!-- 雲 -->
    <symbol id="cloud" viewBox="0 0 121 80.67">
      <path d="M96.51,15.85a24.73,24.73,0,0,0-4.33.4A24.45,24.45,0,0,0,51.61,7.41a24.4,24.4,0,0,0-38.27,24.1A24.48,24.48,0,1,0,44.5,67.38a24.47,24.47,0,0,0,45-3.58,24.48,24.48,0,1,0,7-47.95Z"/>
    </symbol>
    <!-- 太陽 -->
    <symbol id="sun" viewBox="0 0 115.39 115.39">
      <g id="sun-circle">
        <circle class="sun-circle" cx="57.7" cy="57.7" r="30.28"/>
      </g>
      <g id="sun-line" class="sun-line">
        <line x1="15.94" y1="57.7" y2="57.7"/>
        <line x1="28.17" y1="87.22" x2="16.9" y2="98.49"/>
        <line x1="57.7" y1="99.45" x2="57.7" y2="115.39"/>
        <line x1="87.22" y1="87.22" x2="98.49" y2="98.49"/>
        <line x1="99.45" y1="57.7" x2="115.39" y2="57.7"/>
        <line x1="87.22" y1="28.17" x2="98.49" y2="16.9"/>
        <line x1="57.7" y1="15.94" x2="57.7"/>
        <line x1="28.17" y1="28.17" x2="16.9" y2="16.9"/>
      </g>
    </symbol>    
  </defs>
</svg>

<!-- 呼び出し側 -->
<svg  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="oekaki-area" width="500" height="300" viewBox="0 0 500 300" role="img" aria-labelledby="oekaki">
<!-- 雲 -->
  <use href="#cloud" class="cloud" x="250" y="10" width="140" height="93.3"></use>
  <use href="#cloud" class="cloud" x="350" y="160" width="120" height="80"></use>
<!-- 虹 -->
  <use href="#rainbow" x="75" y="125"  width="350" height="175"></use>
<!-- 太陽 -->
  <use href="#sun" x="20" y="20" width="130" height="130"></use> 
<!-- 太陽のパーツを個別に出す場合 -->
<!--   <use class="sun-circle" href="#sun-circle" x="20" y="20" width="130" height="130"></use> 
  <use class="sun-line" href="#sun-line" x="20" y="20" width="130" height="130"></use>
</svg> -->

CSS

.svg-defs {
  display: block;
  width: 0;
  height: 0;
}

#oekaki-area {
  border: 1px solid #000;
  background-color: #def0f3;
}
.cloud {
  fill: #fff;
}
.sun-circle {
  fill: #f08218;
}
.sun-line {
  stroke: #f08218;
  stroke-miterlimit: 10;
  stroke-width: 4px;
}

雲を動かす

さらにSVGの図形に動きを加えてみます。

雲は静止画の位置から左へ移動して、また右から現れるようにします。

こちらを見ながら書いたらばっちりだと思います。

SVG アニメーション(SMIL を使ったアニメーション) / Web Design Leaves

せっかくならSVGの中で動かしたい...!

SMIL(Synchronized Multimedia Integration Language)というマークアップ言語を使う方法がそれにあたるようです。

SVG animation with SMIL - SVG: Scalable Vector Graphics | MDN

SMIL では以下のようなことができます: ・要素の数値属性 (x, y など) のアニメーション ・トランスフォーム属性 (translation または rotation) のアニメーション ・色属性のアニメーション ・モーションパスに従う

具体的には animateMotionanimate など、アニメーションに関係するSVG要素を書きます。
cssを書かずにアニメーションできます。

animateMotion、animate要素が使えるかどうか

はじめは animateMotion を使うのが適当と思いましたが、pathに沿った動きをさせたい場合に使うようです。

ふわふわ上下しながら流れていって欲しいのが正直なところですが、ここは右から左に水平に動かします。

続いて animate を考えましたが、こちらは要素の属性のアニメーションに使うものなので、雲の path には位置に関わる属性(x,y)がなくて動かせない。
雲を変形させることはできます。

cx,cy属性のある circle であれば animate を使って移動のアニメーションができます。

呼び出し側で使えない。定義側も問題あり

animate などのアニメーション要素は use と絡めて書くことはできないのではと思いました。

アニメーション要素は、 path など形を表す要素の子要素になるように書きます。
<path><animate></path> です。
path で囲む書き方もできるのですね。

use を使うと svg で書き出されるので、animate を差し込む余地がありません。

また、定義側でアニメーションの定義をすればよいのではと考えましたが、雲の定義の仕方では無理そうです。 定義側では雲の形状だけを設定していて、viewBoxが雲いっぱいのサイズのため雲が移動するスペースがありません。

呼び出し側と同じviewBoxで、定義側で雲が配置されていれば可能なのかもしれませんが、まだ今の知識では書けません。

となると、雲の場合はcssに頼るしかないのかもしれません。

cssのanimationプロパティで動かす

静止画の位置から右から左に移動させる、というところで少しつまずきました。

0%→100%で右端(見えない)→左端(見えない)とすると、最初の表示のタイミングでは雲が見えません。

その場合は animation-delay プロパティにマイナスの値を設定するとうまくいきます。
遅らすのではなく早めることができるので、0%→100%の途中からスタートできます。

10秒かけた移動だから...とこれも目分量で設定しました。

雲は動きました!

太陽の光を動かす

こちらはSMIL で動かすことができました。

animateTransform要素で回転させる

参考ページなどを見てすんなりとできました。

g で囲った中で animateTransform を書いて g 内の要素の動きを設定できます。

ちなみにデフォルトでは回転は時計回り(右回り)ですが、to="-360, ..." とマイナスの値だと左回りになりました。

さいごに

当初の五輪だったらここまで長くならなかったですね。
SVGはまだまだ奥深いことがわかりました。

※検証段階の内容が多分に含まれているので、分かり次第修正していきます。

順番が前後してしまいますが、この後SVGについてまとめた記事を書きます。

実践が大事!おしまい!