🧬

Dioxus で QR code を生成する Web サイトを作ってみた

2024/05/07に公開

概要

Dioxus で QR code を生成する Web サイト ZEQRA を作ってみたので、雑な内容ですが記事にしました。

環境

関連記事

実装

結構雑に書いてるので GitHub のコードも併せて参照して下さい。

https://github.com/st-little/zeqra/blob/develop/README.md

  1. Dioxus のプロジェクトを作成します。

    次のコマンドでプロジェクトの作成を開始します。

    dx new
    

    プロジェクトの設定は次の通りです。

    ✔ 🤷   Which sub-template should be expanded? · Web
    🤷   Project Name: zeqra
    ✔ 🤷   How do you want to create CSS? · Vanilla
    ✔ 🤷   Should the application use the Dioxus router? · false
    
  2. Cargo.toml マニフェストファイルに依存関係を追加します。

    cargo add qrcode base64 anyhow serde
    cargo add dioxus-free-icons --features=ionicons
    
  3. bulma の CSS ファイルを zeqra/assets/bulma.min.css に保存します。

  4. bulma の CSS ファイルを読み込みます。

    zeqra/Dioxus.toml
    style = ["./bulma.min.css"]
    
  5. 依存関係を追加します。

    zeqra/src/main.rs
    use dioxus_free_icons::{icons::io_icons::{IoCloudDownload, IoLogoGithub, IoLogoTwitter, IoOpen}, Icon};
    use serde::{Deserialize, Serialize};
    use qrcode::render::svg;
    use qrcode::QrCode;
    use base64::{engine::general_purpose, Engine as _};
    use anyhow::Result;
    
  6. State を実装します。

    zeqra/src/main.rs
    #[derive(Debug, Serialize, Deserialize)]
    pub struct AppState {
        isError: bool,
        dataUrl: Option<String>,
        topNavbarBurgerActive: bool,
        topNavbarBurgerClass: String,
        topNavbarMenuClass: String,
        termsOfUseModalActive: bool,
        termsOfUseModalClass: String,
        privacyPolicyModalActive: bool,
        privacyPolicyModalClass: String,
    }
    
    impl AppState {
        fn new() -> Self {
            Self {
                isError: false,
                dataUrl: None,
                topNavbarBurgerActive: false,
                topNavbarBurgerClass: "navbar-burger".to_string(),
                topNavbarMenuClass: "navbar-menu".to_string(),
                termsOfUseModalActive: false,
                termsOfUseModalClass: "modal".to_string(),
                privacyPolicyModalActive: false,
                privacyPolicyModalClass: "modal".to_string(),
            }
        }
    }
    
  7. QR code を作成する関数を実装します。

    zeqra/src/main.rs
    #[derive(PartialEq, Props, Clone)]
    struct MakeQrCodeProps {
        text: String,
        width: u32,
        height: u32,
        dark_color: String,
        light_color: String,
    }
    
    impl Default for MakeQrCodeProps {
        fn default() -> Self {
            Self {
                text: "".to_string(),
                width: 200,
                height: 200,
                dark_color: "#000000".to_string(),
                light_color: "#ffffff".to_string(),
            }
        }
    }
    
    fn makeQrCode(props: MakeQrCodeProps) -> Result<String> {
        let code: QrCode = QrCode::new(props.text.as_bytes())?;
        let image = code.render()
            .min_dimensions(props.width, props.height)
            .dark_color(svg::Color(&props.dark_color))
            .light_color(svg::Color(&props.light_color))
            .build();
        let data_url = format!("data:image/svg+xml;base64,{}", general_purpose::STANDARD.encode(&image));
    
        Ok(data_url)
    }
    
  8. QR code を表示する QrCode コンポーネントを実装します。AppState.dataUrl の値を img タグの src 属性 に設定しています。

    zeqra/src/main.rs
    #[component]
    fn QrCode() -> Element {
        let app_state = consume_context::<Signal<AppState>>();
        let data_url = app_state.read().dataUrl.clone();
    
        match data_url {
            Some(data_url) => {
                rsx! {
                    div {
                        class: "container pt-5 has-text-centered",
                        div {
                            img { src: "{data_url}", width: "150", height: "150" }
                        }
                        div {
                            class: "pt-3",
                            a {
                                class: "button is-medium is-success",
                                href: "{data_url}",
                                download: "QR-code.svg",
                                span {
                                    class: "icon is-small mr-2",
                                    Icon { width: 24, height: 24, icon: IoCloudDownload }
                                },
                                "Download"
                            }
                        }
                    }
                }
            }
            None => None
        }
    }
    
  9. QR コードを生成 ボタンを押下すると makeQrCode 関数を実行して戻り値を AppState.dataUrl に設定するように App コンポーネントを実装します。また、生成した QR code を表示するために QrCode コンポーネントも追加します。

    zeqra/src/main.rs
    #[component]
    fn App() -> Element {
        use_context_provider(|| Signal::new(AppState::new()));
        let mut app_state = consume_context::<Signal<AppState>>();
    
        rsx! {
            link { rel: "stylesheet", href: "main.css" }
            div {
                class: "container px-3 py-3",
                form {
                    class: "box",
                    onsubmit: move |ev| {
                        let qrCode = makeQrCode(MakeQrCodeProps { text: ev.values().get("text").unwrap().as_value(), ..Default::default() });
                        match qrCode {
                            Ok(data_url) => {
                                app_state.write().isError = false;
                                app_state.write().dataUrl = Some(data_url);
                            }
                            Err(_) => {
                                app_state.write().isError = true;
                                app_state.write().dataUrl = None;
                            }
                        }
                    },
                    div {
                        class: "field",
                        div {
                            class: "control",
                            input {
                                id: "text",
                                name: "text",
                                class: "input",
                                r#type: "text",
                                required: true,
                                placeholder: "テキストを入力してください",
                            }
                        }
                    }
                    div {
                        class: "field is-grouped is-justify-content-center",
                        div {
                            class: "control",
                            button {
                                class: "button is-medium is-fullwidth is-link",
                                r#type: "submit",
                                "QR コードを生成"
                            }
                        }
                    }
                }
            }
            QrCode {}
        }
    }
    

Discussion