悪名高きスワイプ広告を解析する

# この記事の概要
ユーザーから嫌われている広告の1つに「スワイプ広告」というものがある。
誤タップをしやすいことが理由だが、あまりにもこの広告だけ誤タップするため調べたところ
実は誤タップしたように見せかけて意図的に広告先に遷移させる広告であるということがわかった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426263/9649f328-dc98-7530-3dc0-1e51de7cc36c.png)
_スワイプ広告とは、左右にスワイプすると画像がついてくるタイプの広告である。_

# スワイプ広告とは
スワイプ広告とは、主にアフィリエイトサイトで見られる広告形式の一つである。
ユーザーは指で画面上の広告を左右にスワイプすることで、広告画像を切り替えることができる。
スワイプによるインタラクティブ性を活かし、複数のメッセージやメディアを使い、魅力的な広告体験を提供することが特徴である。

# なぜ悪名高いのか
しかし、スワイプ広告はユーザーから嫌われている。その理由は、誤タップを誘発しやすいからである。
誤って広告をタップして画面が遷移してしまう確率が非常に高い。
もちろんこの時ユーザーの感覚としては、誤って指が触れてしまったか、あるいはスワイプする指が早く画面から離れてしまって広告に遷移したように感じる。
しかし今回調べたところ、スワイプ広告はユーザーが誤タップをしていないにも関わらず、誤タップをしたような挙動をする仕様であることがわかった。
下の動画がその時の挙動である

![AdsReport.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426263/285ba7b3-6340-2981-3fdf-7f659dd5f6db.gif)
_スムージーの広告だけ、上下にスワイプしようとすると広告に遷移していることがわかる。_
_明らかに漫画の広告とは挙動が違う。_
_また、左右にスワイプしたときには遷移しないのも気になる。_

# 結論
コードを追った結果、スワイプ広告は以下の条件がそろうと広告をタップしたとみなすことがわかった。

- タップ開始位置が広告エリア内
- タップ開始から終了までが250ms以内
- タップ開始と終了位置のX座標差が50pix以内
 
つまり
 
__広告位置から始まる縦スワイプは、強制的に広告をタップしたとみなされる。__

今まであなたが感じていた、「あ、やべ」は
決してあなたのミスではなく、広告の仕組みなのである

# 該当のコード
以下、デベロッパーツールを使用して解析した内容をつらつらと書き並べる

`swipegallery_layout.js` というファイルがタップ位置等を管理している

## タップが開始された(touchstart)時
1051行目～1061行目
重要なポイントは以下
- b.time = Date.now(); … タップ開始時刻を代入
- b.p = 0 … タップ開始座標の初期値0を代入
が実施されている

該当のソースコード部分を乗せるが、基本的に難読化されている記述のため
あくまで参考程度に見てほしい
```js
    Y.prototype.Ba = function(a) {
        this.V || a.preventDefault();
        if (!this.W && 0 != this.b.length) {
            var b = this.T;
            b.U = !0;
            this.V ? (b.pageX = a.e.touches[0].pageX, b.G = a.e.touches[0].pageX) : (b.pageX = a.e.pageX, b.G = a.e.pageX);
            b.time = Date.now();
            b.A = 0;
            b.p = 0
        }
    };
```

## タップ中に指が動いた(touchmoveイベント)時

1061行目～1089行目
重要なポイントは以下
- a.preventDefault(); … この記述により、縦スクロール時の画面移動が無効になる。
- b.p = a.e.touches[0].pageX - b.pageX; … 最初のタップ開始X座標と今のタップ位置のX軸方向の差を代入
```js
    Y.prototype.Aa = function(a) {
        if (!this.W && 0 != this.b.length) {
            var b = this.T;
            if (this.V) {
                a.preventDefault();
                if (1 < a.e.touches.length || a.e.scale && 1 !== a.e.scale) return;
                b.p = a.e.touches[0].pageX - b.pageX;
                b.A = a.e.touches[0].pageX - b.G;
                b.G = a.e.touches[0].pageX
            } else {
                if (!b.U) return;
                b.p = a.e.pageX - b.pageX;
                b.A = a.e.pageX - b.G;
                b.G = a.e.pageX
            }
            b.ea = !0;
            var c = this.b[this.d].h();
            this.Ma = 0 > b.p ? 0 : 1;
            a = Math.abs(b.p / this.P);
            var d = !1;
            0 == this.Ma ? ($(c, this.H * a, this.I * a, this.J * a, this.K * a, 0), this.d != this.b.length - 1 ? (c = this.b[this.d + 1].h(),
                $(c, -this.H * (1 - a), -this.I * (1 - a), this.J * (1 - a), this.K * (1 - a), 0)) : d = !0) : ($(c, this.H * -a, this.I * -a, this.J * a, this.K * a, 0), 0 != this.d ? (c = this.b[this.d - 1].h(), $(c, this.H * (1 - a), this.I * (1 - a), this.J * (1 - a), this.K * (1 - a), 0)) : d = !0);
            d && (b.A /= Math.abs(b.A) / this.P + this.La);
            A(this.f.style, Eb(0));
            this.B += b.A;
            A(this.f.style, Fb(this.B, 0, 0))
        }
    };
```

## タップが終了した(touchendイベント)時
1090行目～1097行目

重要なポイントは以下
```js
 ((50 > Math.abs(b.p))  //タップ開始位置とタップ終了位置のX座標が50pix以内
   && 250 > a - b.time)  //かつ タップ開始とタップ終了の時間が250ms以内であれば
    && (this.dispatchEvent("tap"), this.dispatchEvent("tap-" + this.d)), //広告をタップしたというイベントを発生させる
 
```
 
一応該当の記述も貼っておく。
4行目の`b.U && ...`から始まる1行が上記の動作を実現している
```js
    Y.prototype.la = function(a) {
        if (!this.W && 0 != this.b.length) {
            var b = this.T;
            b.U && "mouseout" != a.type && (a = Date.now(), 500 < a - this.ua && (50 > Math.abs(b.p) && 250 > a - b.time) && (this.dispatchEvent("tap"), this.dispatchEvent("tap-" + this.d)), this.ua = a);
            b.U = !1;
            b.ea && (a = this.d, Math.abs(b.p) > this.Na && (0 > b.p ? a = this.d == this.b.length - 1 ? this.d : this.d + 1 : 0 < b.p && (a = 0 == this.d ? this.d : this.d - 1)), this.Q(a), b.ea = !1)
        }
    };
```

## つまりどういう動作か
ユーザーがあたかも「画面をスワイプしていたら、広告に指が当たってしまった」と感じるいわゆる誤タップは、
実はきちんとスクリプト化された"仕様"であることがわかる。
素早い縦スワイプを検出すると広告先に遷移し、さらにこの時画面は動いていないことからユーザーは"タップしてしまった"と勘違いする。
ゆっくり動かすと縦スワイプも横スワイプも広告には飛ばず
また、指に合わせて画像がついてくるので
スワイプが無効になっている違和感も感じづらくなる。

# 誤タップ広告はユーザビリティを損なうだけでなく、広告主のブランド価値も下げる
スワイプ広告を見て一番感じるのは、誤タップを誘発する広告にユーザーメリットは1つもないことである。
掲載サイトのユーザビリティを損なうのはもちろん、誤タップで飛んでいく先の広告主に対してもいい印象は生まれない。
 
あくまで推測だが、おそらくこの誤タップ広告システムでサービスを提供している会社が存在し、広告主に見せる広告表示数のカウントを水増しできるところにメリットがあると考えられる。
今回の広告主の商品は私も実際に利用しているが、これからも利用したいと感じるブランドと感じていたためこのような形で広告が出てしまっているのはとても残念である。
広告主には、このようなブランド価値を下げる広告掲示がされている事実を把握していただき、こういった形式の広告にならないようなアクションを取っていただけることを願うばかりだ。
 
また根本的な話では、スワイプ広告の広告遷移は決して誤タップやバグではなく、明確に仕様化された作為的な遷移である。
会社の方針なのか1人のエンジニアの悪知恵なのかはわからないが
ユーザーより自分の利益を優先させる姿勢は、同じエンジニアとして非常に残念に感じる。

<details>
<summary> 更新履歴 </summary>

# リリースノート
#### 2023/08/7
- 誤記の修正
- 概要の追加

#### 2023/08/08
- シンタックスハイライトの追加
    - @getty104 様ありがとうございました

#### 2023/08/10
- リリースノートの追加
- 広告主の名前の削除
    - スワイプ広告は掲載サイト側の設定に起因しており、広告主はこういった事実を把握しづらいという意見を頂きました。ブランドに配慮し広告主様の名前は出さないようにいたしました。
</details>