[Javascript] ホームページで雪や葉っぱを降らせるKaedeライブラリを作った話

2025/07/02

Javascript Tool

t f B! P L
eyecatch 仕事でホームページを作っていると、どのサイトも「他のサイトに無い新しい事」というのを要求されます。 SEOに強いとか、読み込みの早い、コンバージョン率の高いホームページという、マーケティング要素はさておき、 奇抜なデザインや、これまでにない使いやすさ、などを要求されると、 誰もがそれを実現するライブラリをネットで検索すると思いますが、 個人的には、できるだけ他の人が作っていないような機能の提案をして、 そのライブラリを自分で作るというのを、趣味的にやってしまうクセがあり、 今回も、葉っぱがゆらゆらと画面の背景に舞っているイメージを要求されたので、 使い回しのきくライブラリを作るチャンスと、ここぞとばかり思って、週末を使って一気に作り上げました。

ライブラリを作るきかっけ

とある大学さんの特設ホームページでの要望だったのですが、 学校での指定樹木というのがあり、その学校は、イチョウがそれにあたります。(どこの学校さんなのかわかる人にはわかりますよね) と言う事で、イチョウの葉がヒラヒラと舞い落ちるライブラリを作ったのですが、 葉っぱを画像にしているので、カエデだろうが、紙切れだろうが、同じアニメーションでよければ、対応できるようにしてみました。 ライブラリ名は「Kaede」としたんですが、何故「イチョウ」にしなかったのかというと、 英字で書くとIcho、カタカナでもイチョウ、なんかあんまりパッとしないという思考が頭をよぎり、見た目も聴き心地もいい感じの「カエデ」にしただけなので、 実を言うと単なる感覚だけです。

デモ

https://yugeta.github.io/kaede/demo/

コードのポイント解説

できるだけCSSのみで構築しようと思ったんですが、どうしてもランダム的な配置やサイズや角度などの処理があるため、Javascriptを使わざるを得ませんでした。 でも、JavascriptとCSSでそれぞれ役割を分けてセットしたので比較的シンプルな構造が作れたと思いますよ。

CSSのポイント

#kaede .item{ --y : 0; /* left */ --x : 0; /* top */ --w : 30px; /* width */ --h : 30px; /* height */ --r : 0deg; /* rotate */ --d : 0.0s; /* deray */ --m : 300px; /* move */ --s : 5.0s; /* speed */ position:absolute; top : var(--y); left : var(--x); width : var(--w); height: var(--h); opacity:0.0; animation : falling var(--s) linear var(--d) forwards; } この箇所、少しプログラムちっくにしていますが、デフォルトセットしたプロパティ変数を、 エレメントのstyle属性記述の方が優先になるというCSS特性を利用して、 javascriptで上書きする仕様にしています。 @keyframes falling{ 0%{ transform:translate(0,0) rotate(0deg); opacity:0.0; } 10%{ opacity:1.0; } 70%{ opacity:1.0; } 100%{ transform:translate(0, var(--m)) rotate(180deg); opacity:0.0; } } 葉っぱがひらひらと落ちる様子は、このアニメーションで一括管理しています。 フェイドインしてフェイドアウトする動きと、落ちながら180度回転する動きをさせることで、 なんとなく葉っぱが落ちる印象を与えてくれます。 左右にひらひら動かそうとも思ったんですが、全ての葉っぱが同じ動きをすると、違和感が出ていたので、シンプルにこれだけにしておきました。

Javascript

src/main.js が呼ばれたら、同じ階層にある style.css を自動でセットして、あとは画像を指定個数分複製するようにしています。 実際の処理はほぼ、item.js に書いているので、シンプルに書かれたコードを読んで理解しやすいと思います。(たぶん・・・) 基本的に画像を、divタグで囲って、2階層の構造にしています。 div > img 何故2階層にしているのかというと、葉っぱの落ちるCSSアニメーションをtransformで行なっているので、 1つのエレメントに対して、transformは1つしか書けないというCSSルールに準拠するために、内部のランダム角度などをtransformで保持するためにこうしています。 #set_img(img){ const div = document.createElement("div") div.classList.add("item") this.#root.appendChild(div) const pos = this.#random_position() const size = this.#random_size() const rotate = this.#random_rotate() const delay = this.#random_delay() const speed = this.#get_speed(size.w) div.style.setProperty("--y", `${pos.y}px`) div.style.setProperty("--x", `${pos.x}px`) div.style.setProperty("--w", `${size.w}px`) div.style.setProperty("--h", `${size.h}px`) div.style.setProperty("--r", `${rotate}deg`) div.style.setProperty("--s", `${speed}ms`) div.style.setProperty("--d", `${delay}ms`) div.appendChild(img) div.addEventListener("animationend", e =>{ e.target.parentNode.removeChild(e.target) this.#create_img(e.target) }) } 肝心の画像のランダム値のセットはここで処理していて、 位置、サイズ、角度、落下スピードなどをここでコントロールしています。 そして、前述したように、styke属性のプロパティに書き込みも同時に行なっています。 ランダム性をよりリアルに表現するために、delayという、アニメーションの遅延処理もランダムでセットしています。 これにより、全部の葉っぱが同じ動きをするけど、開始時間が少しズレるだけで、ランダムに動いている印象になります。 最後の箇所のイベントセットしているのは、imgタグの親要素のdivタグが実際のcssアニメーションを行う際に、 animationendイベントとして、cssアニメーションが100%になったタイミングを取得して、その葉っぱの寿命にして、 エレメントを削除して、また新たな画像を作り出すような仕様にしているので、 これにより無限に葉っぱが落ち続けるアニメができるようになっています。

Github

https://github.com/yugeta/kaede

あとがき

このページにも落ち葉アニメをセットしてみたので、ホームページで使用した感じがわかってもらえたかと思います。 落ち葉アニメをスクロール固定にしているのは、ページ全体に適用しようとすると、ページのスクロールが長いページの場合、 どんでもない数を処理しないといけなくなるので、初期のバージョンでは固定でやるようにしています。 今後改良はしていくと思いますが、これだけでもかなり印象的な効果が出るのがわかってもらえると思います。 いや〜改めて、こういう開発作業、おもろいですね〜。ずっと作っていたい気分です。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ