3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

初めてのSvelte開発!AIイラスト補助アプリを作ってみた感想

Last updated at Posted at 2023-04-10

目的

この記事では、AIイラストレーターのためのプロンプト生成補助アプリ、
Cyber Prompterを制作しましたのでご紹介します。
image.png
本記事の目的は大きく2つです。

  • Cyber Prompterの概要と目的
  • (Reactをよく使う人目線での)Svelteを使ってみての感想

アプリデモ動画

まず、どんなアプリか知っていただきたく、デモ動画を御覧ください!

使い方

  1. 検索ワードの入力

    • ユーザーは自分の検索したいワードを入力します。
  2. プロンプトの選択

    • 右から左へ流れていくプロンプト候補から、複数のプロンプトを選択して自分の好きなプロンプトを作成します。
  3. プロンプトの作成

    • 選択したプロンプトをコピーボタンを押してクリップボードにコピーします。
  4. AI画像の生成

    • 各々が好きなAIのプロンプトから生成するAI画像アプリにコピーしたプロンプトを入れて、画像を好きに生成します。

アプリのUIについて

アプリのUIについて、以下の特徴を説明します。

b0079458-11fb-612b-37e5-4abad170ae06.gif
  • プロンプトの候補が右から左に流れるデザイン
  • オリジナリティの高くインタラクティブな操作
  • 直感かつ簡潔で使いやすいUI(パッと見で使える)

使用された技術について

Cyber Prompterで使用された技術について、package.jsonの内容を紹介し、各技術の役割や特徴について説明します。

  • @smui
    • Svelteでマテリアルデザインに則った仕組みを提供してくれるライブラリ
  • @sveltejs/kit
    • ブラウザーで最小限の作業を行う息をのむほど簡潔なコンポーネントを、HTML、CSS、JavaScript などの既におなじみの言語を使用して記述できます
  • unplugin-icons
    • 非常に多くのアイコンにコンポーネントとしてアクセスできるライブラリ
  • prettier, prettier-plugin-svelte, eslint, eslint-config-prettier, eslint-plugin-svelte3
    • コードをキレイにしたり、静的解析してくれるツールたち
package.jsonを展開
package.json
"devDependencies": {
		"@smui/button": "^7.0.0-beta.0",
		"@smui/circular-progress": "^7.0.0-beta.0",
		"@smui/common": "^7.0.0-beta.0",
		"@smui/fab": "^7.0.0-beta.0",
		"@smui/icon-button": "^7.0.0-beta.0",
		"@smui/paper": "^7.0.0-beta.0",
		"@smui/textfield": "^7.0.0-beta.0",
		"@sveltejs/adapter-auto": "^2.0.0",
		"@sveltejs/kit": "^1.10.0",
		"@types/cookie": "^0.5.1",
		"@typescript-eslint/eslint-plugin": "^5.54.0",
		"@typescript-eslint/parser": "^5.54.0",
		"eslint": "^8.35.0",
		"eslint-config-prettier": "^8.6.0",
		"eslint-plugin-svelte3": "^4.0.0",
		"normalize.css": "^8.0.1",
		"prettier": "^2.8.4",
		"prettier-plugin-svelte": "^2.9.0",
		"smui-theme": "^7.0.0-beta.0",
		"svelte": "^3.55.1",
		"svelte-preprocess": "^5.0.1",
		"tslib": "^2.5.0",
		"typescript": "^4.9.5",
		"unplugin-icons": "^0.15.3",
		"vite": "^4.1.4"
	}

viteの設定はこんな感じでsveltekitやアイコンの設定を噛ませています。

vite.config.ts
import { defineConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite';
import Icons from 'unplugin-icons/vite';

export default defineConfig({
	plugins: [
		sveltekit(),
		Icons({
			compiler: 'svelte'
		})
	]
});

表現技法

単語が右から左に流れる部分はオリジナリティにこだわりました。
いい感じではないでしょうか?

b0079458-11fb-612b-37e5-4abad170ae06.gif

その結果動作はかなり重くなったのですが、そんなの知りません。
スペックの高い端末を使って下さい!(丸投げ)

動きにはCSSの仕組みを使いました。

横に流れる
@keyframes right-to-left {
  from {
    right: -300px;
  }
  to {
    right: 100vw;
  }
}
上下にゆらゆら
@keyframes swing {
  0%,
  100% {
    transform: translateY(0px);
  }
  50% {
    transform: translateY(12px);
  }
}

背景とイイ感じに馴染む部分にはmix-blend-modeを使いました。
この機能、スゴイです。
背景と効果をつけて馴染ませることができます。
設定値はcolor-dodgeにしましたが、他にもmultiply,hard-light,differenceなどたくさんあります。
参考:mix-blend-mode - CSS: カスケーディングスタイルシート | MDN

背景とイイ感じに馴染む
* :global(.prompt-text-image) {
  color: rgba(0, 0, 0, 0);
  background-size: cover;
  background-position: center center;
  mix-blend-mode: color-dodge;  // color-dodgeを指定!
  filter: blur(2px);
  &:hover {
    mix-blend-mode: normal;  // 選択時はblendをしないことで選択状況が分かりやすい
    filter: blur(2px);
  }
}

ただ、mix-blend-modeを使うとテキストまで背景と馴染んでしまい、文字が見づらくなってしまいました。
そこで、テキストとその背景画像を分けることにしました。(ちなみにテキストの背景は桃です。どんぶらこです。)
全く同じ場所にテキストを配置し別要素とすることでmix-blend-modeの適用を受けません。
要素数が倍になりパフォーマンスが悪化してしまったので、他にいいやり方があったら良かったのですが、
どうしても他の方法が思いつかなかったのでこうしました。(良いやり方があったら教えてください...!)
下記が流れる部分のhtmlでbutton背景要素divテキスト要素です。

流れる部分のhtml(svelte記法あり)
{#each fallingItems as prompt (prompt.id)}
  <button
    class="button-reset prompt-text prompt-text-image {outerWidth > WIDE_PX ? 'wide' : ''}"
    style="top: {(0.1 + prompt.random * 0.7) *
      outerHeight}px;background-image: url('textbox/1.png');"
    on:click={handleSelectPrompt(prompt.id)}
  >
    <!-- / expand side  -->
    /{prompt.prompt}/</button
  >
  <div
    class="prompt-text prompt-text-content {outerWidth > WIDE_PX ? 'wide' : ''}"
    style="top: {(0.1 + prompt.random * 0.7) * outerHeight}px"
  >
    <!-- for not blurring text -->
    <span class="prompt-text-not-display">/</span>{prompt.prompt}<span
      class="prompt-text-not-display">/</span
    >
  </div>
{/each}

CSS フレームワーク

今回は CSS フレームワークにMaterial UIを使いました。
Reactの方はいつも使っているので慣れているのですが、Svelteのは元のフレームは同じでも使い方がかなり違ったので大変でした。
特にアイコンに関しては書き方がかなり違っていました。
Svelteの場合は以下のように書きます。

<script lang="ts">
  import { Icon } from '@smui/button';
</script>
<section>
  <Icon class="material-icons">content_copy</Icon>
<section>

Reactの場合はコンポーネントを呼び出せばOKです。

import React from 'react'
import ContentCopyIcon from '@mui/icons-material/ContentCopy';

export const Test = () => {
  return (
    <ContentCopyIcon />
  )
}

Svelteの場合、Introducing Material Symbolsの方法が採用されていてクラス名テキストコンテンツ部に文字を入力して使います。
テキストコンテンツにテキストを入力しているのにも関わらず表示内容がアイコンになることが非常に違和感でした。
ここはReactと比べても表記の方法が異なり慣れない点でした。

あとはイベントアタッチの記述方法がかなり違うのでそこは戸惑いました。

<form on:submit={handleGenerate}>  // on:event_nameで表記する

ただ、ここら辺は慣れの問題かなと思います。

あとは CSS の書き方素の CSS というか SCSSで書けるのが新鮮でした。
素の CSS の書きが新鮮になる日が来るとは思っていませんでした。

例えば、Svelte(普通のCSS)だとjustify-contents: "center";とケバブケースで書きますが、ReactだとjustifyContents: "center",とキャメルケースで書きます。
Reactだとjavascriptって感じなのですが、Svelteだとcssとjs(とhtml)が共存していて違和感がすごかったです。
(vueとかもSvelte側だと思うので、React固有の感じ方かもです。)

Chromeの開発者ツールでスタイルを調整して直接コピペできるのは割と嬉しかったです。
Reactでは変換しないとできないので。

ストア機能

ストアの機能もよくできていて 作成が簡単でした。
こんな感じでwritableを使って変数をエクスポートします。
シンプル。

import { writable } from 'svelte/store';

export type Prompt = {
	id: string;
	prompt: string;
	createdAt: Date;
	selectedAt: Date | null;
	random: number;  // 出現位置用
};

export const prompts = writable<Prompt[]>([]);

その後、update()関数などが提供されるので、それを用いて更新します。

prompts.update((prev) => {
	return [
		...prev,
		{
			id: crypto.randomUUID(),
			prompt: word,
			createdAt: new Date(),
			selectedAt: null,
			random: Math.random()
		}
	];
});

ここは、スゴイ便利だなーと感じました。
余計な手続きが非常に少なくていいですよね。

React vs Svelte 所感

ここまで振り返ってきて、やはり慣れの問題は重要だと思いました。
頭を切り替えるのが結構たいへんでした。
そういう意味ではSvelteが流行ってどんどん使うようになれば、
慣れてきて記述も少ないので素早く実装できるのかもしれないと思いました。
新しい言語に触れるという意味では良い経験になりました。

エコシステムに不安がある点も採用するのが難しいところです。
Reactはサードパーティー製の良いライブラリが充実しており採用時にも安心できます。
Svelteはベータ版が多いのでかなり短い頻度でいきなり記述方法や設定の仕方が変わったりして困りごとが増えそうな印象です。
今回の開発時も前上がっていた設定が上手くいかずvercerl上のビルドが失敗しまくってハマりました。
内部のバージョンが意図せず上がってしまったことが原因で、固定したら上手く行きましたが、割とそういうことが起きそう。
プロジェクトで使うとすると、ここら辺は運用保守コストにそのまま乗っかってくる部分なのでシビアにならざるを得ないと思いました。

仕事や業務また使える技術者の数を考えると、Reactがまだまだ人気の高く、需要の高いフレームワークであるのは間違いないです。
私としては一旦Svelte使わずReactに戻ろうと思っています。

さいごに

ご覧いただき、ありがとうございました。
この記事では、AIイラストレーターのための新しいアプリ、Cyber Prompterについて紹介しました。
本記事の目的は以下の2つでした。

  • Cyber Prompterの概要と目的
  • (Reactをよく使う人目線での)Svelteを使ってみての感想

この記事を読んでいただき、ありがとうございます。
Svelteでの開発やCyber Prompterに興味を持っていただけたら嬉しいです。
ぜひ、開発したアプリを使ってみて、感想やフィードバックをいただけると幸いです。
今後も新しい技術に挑戦し、記事を通じて情報発信していきたいと思います。
フォローもぜひよろしくお願いします!

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?