目的
この記事では、AIイラストレーターのためのプロンプト生成補助アプリ、
Cyber Prompterを制作しましたのでご紹介します。
本記事の目的は大きく2つです。
- Cyber Prompterの概要と目的
- (Reactをよく使う人目線での)Svelteを使ってみての感想
アプリデモ動画
まず、どんなアプリか知っていただきたく、デモ動画を御覧ください!
Cyber PrompterというイラストAIの呪文(プロンプト)生成を補助するアプリをチームで作りましたー!
— toromo (@toromo12) April 9, 2023
動画制作は @mskz2021 さんです!
使ってみてねhttps://t.co/pfVZPeLEnl pic.twitter.com/r5DHPop4qO
使い方
-
検索ワードの入力
- ユーザーは自分の検索したいワードを入力します。
-
プロンプトの選択
- 右から左へ流れていくプロンプト候補から、複数のプロンプトを選択して自分の好きなプロンプトを作成します。
-
プロンプトの作成
- 選択したプロンプトをコピーボタンを押してクリップボードにコピーします。
-
AI画像の生成
- 各々が好きなAIのプロンプトから生成するAI画像アプリにコピーしたプロンプトを入れて、画像を好きに生成します。
アプリのUIについて
アプリのUIについて、以下の特徴を説明します。
- プロンプトの候補が右から左に流れるデザイン
- オリジナリティの高くインタラクティブな操作
- 直感かつ簡潔で使いやすい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を展開
"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やアイコンの設定を噛ませています。
import { defineConfig } from 'vite';
import { sveltekit } from '@sveltejs/kit/vite';
import Icons from 'unplugin-icons/vite';
export default defineConfig({
plugins: [
sveltekit(),
Icons({
compiler: 'svelte'
})
]
});
表現技法
単語が右から左に流れる部分はオリジナリティにこだわりました。
いい感じではないでしょうか?
その結果動作はかなり重くなったのですが、そんなの知りません。
スペックの高い端末を使って下さい!(丸投げ)
動きには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
がテキスト要素です。
{#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に興味を持っていただけたら嬉しいです。
ぜひ、開発したアプリを使ってみて、感想やフィードバックをいただけると幸いです。
今後も新しい技術に挑戦し、記事を通じて情報発信していきたいと思います。
フォローもぜひよろしくお願いします!
Cyber PrompterというイラストAIの呪文(プロンプト)生成を補助するアプリをチームで作りましたー!
— toromo (@toromo12) April 9, 2023
動画制作は @mskz2021 さんです!
使ってみてねhttps://t.co/pfVZPeLEnl pic.twitter.com/r5DHPop4qO