Open26

Nuxt3で少し特殊なアンケートフォームを作る

Y - AY - A

やりたいこと

アンケートフォームを作る

調査したい内容

色の組み合わせが、見分けやすさに与える影響について

Y - AY - A

もっとくわしく

なぜNuxt?

つかったことがあるから

なんのため?

学校の課題

どこが特殊?

  • 普通のアンケート
    • 「○○」について賛成ですか、反対ですか
    • あなたは、「○○」という単語を聞いたことがありますか
    • 「○○」という政策は必要だと思いますか?
  • 今回の調査
    • この色の組み合わせは、見やすいですか?

色の見分けやすさを調べる。誰がやってもそんなに結果わからないはず。自分だけを対象とした調査としてもよいが、時間がかかりすぎる。みんなに分担してもらいたい

Y - AY - A

結局

本当に分析に必要なデータは、数人の協力者に頼むことにする。途中で回答をやめてしまうかもしれない

アンケートの実施目的は、『「数人の協力者」と「学校に通う多数の生徒」の、色の感じ方に有意な差が存在しないことを示すため』

Y - AY - A

作りたいもの

ユーザー視点

  1. トップページひらく
  2. 初めて開いた人は、調査について説明するページに転送
  3. 「同意」をタップすると、調査の一覧ページが開く
  4. 調査の一覧から、回答したいものを選択
  5. 画面に、二種類の色の組み合わせが表示される
  6. 「見やすい」~「見にくい」の4段階から選択
  7. 次の色の組み合わせが表示される
  8. 5~7をくりかえす
  9. すべての色の組み合わせについて回答し終わると、調査の一覧ページに戻る

ページ別

  • トップページ
    • 調査の一覧が並ぶ
  • 調査について説明し、同意を求めるページ
  • 実際に調査を行うページ

コンポーネント別

  • ヘッダー
    • 「?」アイコンで、調査について説明するモーダルが開く
    • データは、同意を求めるためのページと同じところから取ってきたい
  • 調査一覧ページにならぶボタン
    • 調査について少しだけ説明する
    • 調査一覧ページで v-forで並べる
  • 調査時に色を表示
  • 調査時に文字を表示
    • 「背景」と「その上に表示された文字」の色を変化させ、文字が読みやすいか調査
  • 調査時に、見分けやすさを選択するボタン
    • スマホでも簡単に押せるようにしたい
  • いろんなところでつかう、モーダルウィンドウ
    • 「調査への協力ありがとう」
    • 「この調査では、こんなことをします。この条件に同意する必要があります」
Y - AY - A

データの保存

GASにPOSTを送信する。

参考

https://zenn.dev/ukkz/articles/cca653be27108c

https://zenn.dev/kinjyo/articles/b379422ab922fb

https://rooter.jp/programming/edit-spreadsheet-with-gas/

本当にできるのか?

GASでLINE Botを作ったことがあるからいけるはず

Y - AY - A

実装したコード

main.gs
function doGet(e) {
  return HtmlService.createHtmlOutput("論文アンケート")
}

function doPost(e) {
  const dataArray = e.postData.contents.split(",");
 ContentService.createTextOutput(JSON.stringify(e));
  SaveData(dataArray);
  return ContentService.createTextOutput(JSON.stringify(dataArray));
}


function SaveData(data) {
  // dataの0番目には、シートの名前が入っている
  try {
    let sheet = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(data[0]);

    let dataSplice = data.splice(0, 1, Utilities.getUuid(),Utilities.formatDate(new Date(), 'JST', 'yyyy-MM-dd HH:mm:ss'));

    // 一番下にデータを追加
    sheet.appendRow(data);
  } catch {
    throw new Error("関数SaveDataで、エラーが発生しました");
  }
}

Y - AY - A

データの構造をどうしよう

調査するための色のデータ

何パターンかに対応する必要がある

調査で質問する時に使うデータ

  • 色の id、コードの組み合わせ
    • 質問項目では、色のコードを名前で指定したい。設定ミスを防ぐため
      \rightarrow これは、すべての調査で共通化すればよいのでは?
  • 調査の種類
    • 色を二色見比べるもの、背景色と文字の色の組み合わせを評価するもの
  • 何問あるのか
Y - AY - A

調査終了時、ブラウザからGASに送るデータ

  • 調査のID
  • その人が何回目に調査に回答したか
  • 回答データ
    • 色の組み合わせに名前を付けたもの(名前は数値にしようかな)
Y - AY - A

とりあえず書いてみる

{
  "research-id": "01020303", //
  "data": {
    "colors": [
      //色の組み合わせを、表を使って表現する(対戦表みたいな感じ)。その場所を、数値を使って指定する感じ
      [1, 1, 1],
      [1, 1, 2],
      [1, 1, 3],
      [1, 1, 4],
      [1, 1, 5],
      [1, 1, 6],
      [1, 2, 1]
      // 以下たくさん続く
    ]
  }
}

Y - AY - A

data.colorsを、ランダムにソートして、似たような色が順番に出ないようにしたい

Y - AY - A

色の組み合わせのリストは、Excelか何かで作ってコピーすれば使いまわせそう

Y - AY - A

現時点での構成

  • アクセスしたら,Cookieの有無をしらべ,なかったら/aboutへ転送
  • /aboutで,アンケート調査について説明する
  • 「同意」ボタンをクリックしたら,Cookieをセットしトップページへ転送
Y - AY - A

実装したコード

about.vue
<!-- アンケート調査について説明するページ -->
<template>
  <p class="text-h4 mb-8 text-center">この調査について</p>

  <!-- いろいろせつめいする -->

  <div class="my-7">
    <v-btn class="bg-primary mx-auto" to="/" @click="click">
      アンケートに解答する
    </v-btn>
  </div>
</template>

<script setup>
const cookie = useCookie("visited", { maxAge: 60 * 60 * 24 * 30 * 2 });
function click() {
  cookie.value = true;
}
</script>
index.vue
<!-- トップページ -->

<template>
  <!-- アンケート調査のリンク -->  
</template>

<script setup>
definePageMeta({
  middleware: [
    function (to, from) {
      if (!useCookie("visited").value) {
        return navigateTo("/about");
      }
    },
  ],
});
</script>

困ったところ

  • useCookie()の有効期限の指定方法

https://nuxt.com/docs/api/composables/use-cookie#maxage-expires

ちゃんと,

maxAge Specifies the number (in seconds) to be the value for the Max-Age Set-Cookie attribute.

って書いてあった。秒数で指定する。

  • Cookieを取得する方法
    useCookie("visited").value.valueが抜けていた。
Y - AY - A
  • トップページに来たら,調査のリンクが二つ。
    1. 色の見分けやすさ
    2. 色の組み合わせの感じ方
  • クリックすると,/research/[slug]へ転送
Y - AY - A

途中まで

components/DisplayColor.vue
<template>
  <div :style="colors">
    <div class="w-100 color1 h-50 pa-auto">aaa</div>
    <div class="w-100 color2 h-50 pa-auto">aaa</div>
  </div>
</template>

<style>
.color1 {
  background-color: var(--color1);
}
.color2 {
  background-color: var(--color2);
}
</style>

<script setup>
const colors = {
  "--color1": "#222222",
  "--color2": "#777777",
};
</script>

引っかかったところ

  • :style="colors"の指定(colorsは,変数名と値を指定した配列)
    これを指定すると,style="--color1: #222222; --color2: #777777;"と展開される。
    その:style=""を指定した要素の配下でのみ使用可能

なぜだかわからないが,下のサイトのやり方だとうまくいかなかった

https://zenn.dev/tentel/articles/736bfeaaa60ab3

  • vuetifyのpadding
    ちゃんとドキュメントを読もう!

https://next.vuetifyjs.com/en/styles/spacing/

Y - AY - A

結局

いろいろ試したら、上記参考サイトのやり方で実装できた

components/DisplayColor.vue
<template>
  <div>
    {{ type }}
    <div class="w-100 color1 pa-auto h-half"></div>
    <div class="w-100 color2 h-half pa-auto"></div>
  </div>
</template>

<style scoped>
.color1 {
  background-color: v-bind(color1);
}
.color2 {
  background-color: v-bind(color2);
}
.h-half {
  height: 30dvh;
}
</style>

<script setup lang="ts">
interface Props {
  type: string | string[];
  id: number;
}

interface colorDatas {
  type: number;
  data: {
    color1: string;
    color2: string;
  };
}

import colorData from "@/assets/json/1.json";

const id = withDefaults(defineProps<Props>(), {
  id: 1,
});

const color1 = ref("#222222");

watch(id, (next, prev) => {
  color1.value = colorData.data[id.id].color1;
});

const color2 = computed(() => {
  return colorData.data[id.id].color2;
});
</script>
Y - AY - A
DisplayColor.vue
<script setup lang="ts">
interface Props {
  type: string | string[];
  id: number;
  group_id: number;
}

interface colorDatas {
  type: number;
  data: {
    colorA: string;
    colorB: string;
  };
}

import colorData2 from "@/assets/json/combination_list.json";

const Props = withDefaults(defineProps<Props>(), {
  id: 0,
  group_id: 0,
});

const colorA = computed(() => {
  const l = colorData2[Props.group_id][Props.id][0][1];
  const c = colorData2[Props.group_id][Props.id][0][2];
  const h = colorData2[Props.group_id][Props.id][0][0];
  return [l + "% " + (c * 134) / 100 + " " + h];
});
const colorB = computed(() => {
  const l = colorData2[Props.group_id][Props.id][1][1];
  const c = colorData2[Props.group_id][Props.id][1][2];
  const h = colorData2[Props.group_id][Props.id][1][0];
  return [l + "% " + (c * 134) / 100 + " " + h];
});
</script>

表色系を変更したので,L・c・hそれぞれの値を定義しています。

Y - AY - A
combination_list.jsonの一部
[
  [
    [[225,66.6,33.3],[315,33.3,50]],
    [[45,66.6,66.6],[135,33.3,50]],
    [[135,33.3,66.6],[45,33.3,66.6]],
    [[135,50,66.6],[45,50,50]],
    [[45,33.3,33.3],[45,50,33.3]],[[45,50,33.3],[315,33.3,33.3]],[[45,33.3,66.6],[45,33.3,33.3]],[[135,33.3,66.6],[135,50,66.6]],[[45,33.3,50],[135,50,50]],[[45,33.3,50],[45,50,66.6]],[[135,33.3,33.3],[135,50,66.6]],[[135,33.3,50],[315,50,66.6]],[[45,50,50],[45,66.6,66.6]],[[135,33.3,66.6],[315,33.3,66.6]],[[135,33.3,66.6],[225,33.3,66.6]],[[135,33.3,50],[45,66.6,66.6]],[[135,33.3,33.3],[45,50,66.6]],[[135,33.3,50],[315,33.3,66.6]],[[45,33.3,50],[45,66.6,50]],[[45,33.3,33.3],[45,50,50]],[[135,33.3,50],[45,33.3,66.6]],[[45,50,50],[45,66.6,33.3]],[[45,33.3,33.3],[225,50,33.3]],[[45,50,33.3],[315,50,50]],[[135,50,33.3],[45,50,33.3]],[[45,50,66.6],[225,33.3,50]],[[135,50,33.3],[135,50,50]],[[45,66.6,33.3],[45,66.6,66.6]],[[45,50,50],[225,66.6,66.6]],[[45,50,50],[225,50,33.3]],[[135,33.3,66.6],[225,33.3,50]],[[135,33.3,66.6],[225,66.6,66.6]],[[135,33.3,50],[225,50,66.6]],[[45,66.6,50],[135,50,33.3]],[[135,50,66.6],[135,66.6,33.3]],[[45,33.3,66.6],[45,66.6,33.3]],[[315,50,50],[135,50,66.6]],[[45,66.6,50],[135,66.6,66.6]],[[315,66.6,33.3],[315,33.3,66.6]],[[45,66.6,50],[45,50,50]],[[225,66.6,66.6],[135,33.3,50]],[[45,50,50],[135,50,33.3]],[[315,33.3,50],[225,66.6,33.3]],[[315,33.3,33.3],[225,66.6,50]],[[135,50,33.3],[45,33.3,66.6]],[[225,66.6,50],[315,50,66.6]],[[225,66.6,50],[45,50,66.6]],[[315,33.3,66.6],[225,33.3,50]],[[225,66.6,50],[135,66.6,50]],[[225,66.6,66.6],[135,33.3,66.6]],[[135,33.3,50],[315,33.3,50]],[[225,66.6,66.6],[45,50,33.3]],[[315,33.3,50],[225,66.6,50]],[[225,66.6,66.6],[315,66.6,50]],[[315,33.3,50],[135,66.6,66.6]],[[135,33.3,33.3],[225,66.6,33.3]],[[45,50,33.3],[315,33.3,66.6]],[[225,66.6,66.6],[225,33.3,50]],[[315,33.3,33.3],[135,66.6,66.6]],[[135,66.6,33.3],[45,50,66.6]],[[315,33.3,33.3],[225,33.3,66.6]],[[315,33.3,66.6],[225,33.3,66.6]],[[45,66.6,50],[45,50,33.3]]

  ],
  ['省略'],
  ['省略'],
]

これで63組の数値のペアがあります。
これが20組あります。

Y - AY - A

結局

  • 36*35 =1260パターンの色の組み合わせを作った。
  • 1260÷20=63だから,一人63件ずつ回答してもらうことにした。少なくとも20人にアンケートする。
  • だいたい330人くらいの人から,659×63件の回答をもらった。(二つのテーマを合わせて。)