【jsPsych】ブラウザのズーム機能には気をつけろ


# はじめに
- 文字や画像のサイズ、あるいは位置などを決める際にはピクセル数を指定することがよくあります。ただし、参加者が所持するデバイスごとにそのディスプレイサイズや解像度は多様で、それに伴って実際に表示されるサイズも異なります。

- **ピクセル(px)によるサイズ指定はあくまでディスプレイサイズや解像度によって決まる相対的なものです**

- 「自分のPCでは問題ないけど、参加者のPCではうまく表示されない」といった問題が生ずることは容易に想像できます。極端に大きい、小さいサイズなどにしなければ大体大丈夫ですし、ある程度気をつけていれば多くの問題は回避できます。

- ディスプレイの解像度は**window.screen.width**や**window.screen.availWidth**などを使えば簡単に取得できます。人によってはこれらで取得した解像度をもとに**position: absolute**を用いて位置を指定しているのではないでしょうか。
    - BrowserCheckのプライグインでも取得可能です。


- 色々位置の指定などはできますが、気を付けていないと思いもよらないズレが生ずる可能性があります。
    - <font color= "red">その一つがブラウザのズーム機能です。</font>

# ブラウザのズーム機能
![スクリーンショット 2023-05-18 10.35.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/875221d3-706e-7e12-24da-352b06bfed58.png)



- Google Chromeでは画面右上のツールバーから拡大率を調整することができます。
- <font color="red">**実験をスタートする前にこの拡大率を変更していた場合に、どんな見栄えになるか確認しておいた方が良いです**</font>

# 実際に提示してみると
- では実際に提示するとどうなるのでしょうか。

## ピクセルで指定しない場合
- ピクセルで指定せず、位置をずらす方法の一つとして % で指定します。
    - 今回は左から25%の位置に提示してみます
    - また、指定した位置は起点を示すものになるので、transformで調整します。
- コードは以下になります

```javascript
    var trial2 = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: '<p style="font-size:30px; position: absolute; left: 25%; transform: translate(-50%, -50%);">スペースキーを押してください</p>',
        choices: [' ']
    };
```
- まずは拡大率を変えないときは以下のようになります
<figure>
<figcaption>「%」で指定し、拡大率1の画面</figcaption>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/e26ebe0f-5414-e32d-f8c2-6b6d32b6002b.png">
</figure>


- では今度は拡大率を少し下げ、75%のときの見栄えは以下になります。

<figure>
<figcaption>「%」で指定し、拡大率0.75の画面</figcaption>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/2a4b518d-1c83-d592-f3d9-a3114f87ac7f.png">
</figure>

- 拡大率を下げているので、文字が小さくなっていますが、位置に変化はみられません。

## ピクセル数で指定した場合

- 次はピクセル数で指定したときの場合をみてみましょう。**window.screen.width**で解像度を取得し、その1/4の位置に文字を提示します。
- コードは以下です。


```javascript
    var Width = window.screen.width;
    var trial3 = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: `<p style="font-size:30px; position: absolute; left: ${Width / 4}px; transform: translate(-50%, -50%);">スペースキーを押してください</p>`,
        choices: [' ']
    };
```

<figure>
<figcaption>「ピクセル」で指定し、拡大率1の画面</figcaption>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/a2395f98-59b9-28b8-91b2-2c3048a5261c.png">
</figure>

- 表示される画面は位置を25%で指定したときと全く同じです。

- ではこのとき、ブラウザの拡大率をいじるとどうなるのでしょうか

<figure>
<figcaption>「ピクセル」で指定し、拡大率0.75の画面</figcaption>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/eb46e72a-35a6-e1c3-f523-a08f9a29a1e7.png">
</figure>

- 文字が小さくなっているのは先ほども確認しましたが、左側に寄っているのがわかります

## ピクセルで中央に提示してみる
- 今のだとわかりにくいかもしれないので、今度は中央に十字が提示されるようにしてみます。
- 拡大率0.75でどうなるのでしょうか
- コードは以下です。

```javascript
    var Width = window.screen.width;
    var trial4 = {
        type: jsPsychHtmlKeyboardResponse,
        stimulus: `<p style="font-size:40px; position: absolute; left: ${Width / 2}px; transform: translate(-50%, -50%);">+</p>`,
        choices: [' ']
    };
```


<figure>
<figcaption>「ピクセル」で指定し、拡大率0.75の画面</figcaption>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2251263/d27b88b9-6786-47cc-e168-a469838add5b.png">
</figure>

- やっぱり左側によっていますね。
    - ちなみに、拡大率を1よりも大きくすると右側に寄ります。
- これは由々しき事態です。

# なぜこんなことに...
- **window.screen.width**や**window.screen.availWidth**ではモニタの使用できるピクセル数を取得します。
- ブラウザ上の拡大or縮小を用いると、実際に表示される幅や高さのピクセル数が変化してしまうためです。

## 拡大率を変えるのってどんなとき
- 近年では、ディスプレイの性能も上がり、フルHDではなく、2Kや4Kディスプレイも一般的に使用されています
- このような高解像度でhtmlを開くとかなり小さく表示されてしまうことがあります。
    - 同じインチ数のディスプレイサイズで、解像度が1920ピクセルで20ピクセルの文字を表示するときと3840ピクセルのディスプレイで20ピクセルの文字を表示する場合では後者の方が見た目はかなり小さくなります
- 非常に高解像度のディスプレイでネットサーフィンをしていると全体的に表示されるものが小さくなるので、ブラウザのズーム機能を用いてみやすくしたりします。
- また、windows、Macにかかわらず、最近ではディスプレイが高解像度でも最大限利用するのではなく、デフォルトではやや解像度を下げて設定されています。
    - これはディスプレイ設定で簡単に変更できます。
- ガジェット好きなら最大限利用できるように設定し直していても不思議ではありません。

- また、実験を行う際も適切な文字サイズで表示しないと参加者がみやすくするために拡大or縮小することは多いにあると思います。

# 解決策
- まず、前提として<font color="red">**ブラウザの拡大率をこちら側が直接調整することはできません**</font>
- 実験実施の際に最も簡単にこれらを回避する方法は
    - 実験実施上の注意点として参加者に直しておいてもらうことです。
    - あるいは、**position: absolute**を使わずに、relativeにしたり、px単位での指定をやめれば問題ないです。
    - px単位ではなく別の単位で指定する
        - 適用できる単位は以下が参考になります
        - https://developer.mozilla.org/ja/docs/Learn/CSS/Building_blocks/Values_and_units

- 単位を変えるのは論文で報告する際に少し面倒な気はします。

## 拡大率を計算する
- さて、拡大率を直接操作することはできなくとも、どれくらいにしているのかは計算できます。
- こちらの記事を参考にしました
    - https://ameblo.jp/personwritep/entry-12576056093.html

- **window.outerWidth**と**window.innerWidth**を利用します。
    - 何を取得しているのかは以下が参考になります。
        - https://developer.mozilla.org/ja/docs/Web/API/Window/outerWidth
        - https://developer.mozilla.org/ja/docs/Web/API/Window/innerWidth

- これらを用いて拡大率を計算すると以下のように計算できます
```javascript
const zoom_value = window.outerWidth / window.innerWidth;
```

- この値が1になっていればブラウザ上で拡大or縮小をしていないことを意味します。
    - **デベロッパーツールなどを開いていると1にはなりません**
- 特定のブラウザ以外はじくときと同様にこの値が1以外の場合ははじくとしても良いと思います。
    - https://zenn.dev/snishiyama/articles/c75f419817c4f9d1d494

## 計算された拡大率を基に実験画面を拡大or縮小する
- 拡大率がわかるなら、それに基づいて画面を拡大or縮小してしまいます。
- javascriptから得られた値をcssに動的に適用するにはデフォルトでもできますが、今回は記法が簡単なjQueryを用います。
    - (簡単に書けるので私が普段使っているだけなんですけどね。)

- まずはjQueryを読み込みます。プラグインを読み込むときと同様に`<script>`タグを用います。
    - 最新バージョンを使う場合は公式から確認してください。
    - とりあえず今回は以下を読み込みます。
```javascript
<script src="https://code.jquery.com/jquery-3.6.1.min.js"
        integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
```

- 表示される全体(body)のcssを変更するには以下のようにします。

```javascript
    const Width = window.screen.width;
    const Height = window.screen.height;
    $("body").css({ "transform-origin": "0 0" });
    $("body").css({ "transform": `scale(${1 / zoom_value})` });
    $("body").css({ "width": `${Width}` });
    $("body").css({ "height": `${Height}` });
```

- 全画面表示にしたときにこれらを適用させたいので、jsPsych-call-functionプラグインを利用します。
    - fullscreenプラグインとcall-functionプラグインは事前に読み込んでおいてください。
```javascript
<script src="https://unpkg.com/@jspsych/plugin-fullscreen@1.1.2"></script>
<script src="https://unpkg.com/@jspsych/plugin-call-function@1.1.2"></script>
```

- 適用するには以下のようにします

```javascript
    const fullscreen = {
        type: jsPsychFullscreen,
        fullscreen_mode: true
    };

    const pix = {
        type: jsPsychCallFunction,
        func: function () {
            Width = window.screen.availWidth;
            Height = window.screen.availHeight;
            let zoom_value = (window.outerWidth) / window.innerWidth;
            if (zoom_value != 1) {
                $("body").css({ "transform-origin": "0 0" });
                $("body").css({ "transform": `scale(${1 / zoom_value})` });
                $("body").css({ "width": `${Width}` });
                $("body").css({ "height": `${Height}` });
            }
        }
    };
timeline.push(fullscreen);
timeline.push(pix);
```

- 計算した拡大率が1ではなかった場合に変更するように書いてあります。
- 他にわかりやすい方法があったら是非教えてください。

# 終わりに

- オンラインですと思いもよらぬアクシデントが起きるので、その一例になります。
- コピペで動かすことは可能ですが、正しく挙動しないとかあったらすみません。。
    - <font color="red">**個人の判断でご利用ください。**</font>

:::note warn
私も勉強中の身なので、本記事のコードを利用しても責任は負いかねます。。
:::

- また、ブラウザによってinnerWidthの挙動が変わることがあるらしいので気をつけてください。
- ここではchrome用で書いています。
- **実験をやる前のデバッグが大切です。**