changed 5 years ago
Linked with GitHub

前言

聽到 收費廣告版位 最常想到的就是 Google Adsense,開發者可以在自己的網站上找位置串接 API,之後選擇廣告曝光來獲取收益,但這篇不是想討論如何串接,而是背後是如何觸發計價收費的,以及如果要自制站內廣告賣給廠商該如何實作。

常需要判定曝光定價的兩大邏輯

  • 曝光多少百分比
  • 曝光秒算

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

本篇的小調整

這篇是說明 IntersectionObserver 應用的尾篇,為了更接近工作方式,所以改採用現成的 Component 套件來嘗試實作,並試著使用 Code Sandbox 來 Demo 案例。接下來分享搜集資料過程中看到兩個套件的實作方式。

react-intersection-observer

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

latest 版本 => 2019/11 Published
Weekly Downloads:42,528
Unpacked Size:114 kB
Issue:這禮拜作者還有回覆

發現此 Component README 寫得超級詳細的,甚至連 Intersection Observer 技術都有詳細圖解,看到心裡總是想著那我幹嘛還寫那麼多這世上很溫暖呢

安裝

npm i @researchgate/react-intersection-observer

引入

import Observer from '@researchgate/react-intersection-observer';

可用參數(Options)
root: HTMLElement|string | default window viewport
rootMargin: string | default 0px 0px 0px 0px
threshold: number|Array<number> | default: 0
disabled:boolean | default: false
onChange (required):接觸發 callback 取得 IntersectionObserverEntry
children: React.Element<*>|null

解說案例:可見度百分比 threshold 偵測

參數設定

  • root:未指定,使用預設裝置 viewport
  • rootMargin:未指定,使用當前 viewport 邊界
  • threshold:設定 [0, 0.25, 0.5, 0.75, 1] 五個 step,顯示達到這些數值則觸發事件
  • disabled:未指定,預設不封鎖
  • onChnage:變更 Nav 顏色及取得 intersectionRatio 顯示比例
  • children:填入會被 Observe 的元件(畫面黑色區塊)
import React, { Component } from "react";
import Observer from "@researchgate/react-intersection-observer";
import "./styles.css";

export default class Default extends Component {
  constructor(props) {
    super(props);
    this.state = {
      visibility: "hidden",
      visibleRate: 0
    };
  }
  handleChange = ({ isIntersecting, intersectionRatio }) => {
    this.setState({
      visibility: isIntersecting ? "visible" : "invisible",
      visibleRate: intersectionRatio * 100
    });
  };

  render() {
    return (
      <div>
        <div className={`nav ${this.state.visibility}`}>
          {this.state.visibleRate}%
        </div>
        <div className="body">
          <section className="hint">往下滑動</section>
          <Observer
            onChange={this.handleChange}
            threshold={[0, 0.25, 0.5, 0.75, 1]}
          >
            <section className="target">
              <div /><div /><div /><div />
            </section>
          </Observer>
          <section className="hint">往上滑動</section>
        </div>
      </div>
    );
  }
}

特性一:threshold 與 intersectionRatio

這個不是套件的特定,是 IntersectionOberser 原生的實作就會有的問題,原因是滾動 scrollbar 一個單位是不會無限切割的,多顯示或少顯示的元件比例不一定會剛好等於 threshold 的設定值,而是當 intersectionRatio 越過這個值時,去觸發事件。

特性二:Observer元件不會出現 DOM 物件

<div className="body">
  <section className="hint">往下滑動</section>
  <Observer
    onChange={this.handleChange}
    threshold={[0, 0.25, 0.5, 0.75, 1]}
  >
    <section className="target">
        <div /><div /><div /><div />
    </section>
  </Observer>
<section className="hint">往上滑動</section>

收費廣告版位應用

常需要判定曝光定價的兩大邏輯

  • 曝光多少百分比
  • 曝光秒算

步驟1:規劃 Component 介面

Props

  • src:圖片連結
  • keepSecond:計價停留秒數條件
  • threshold:計價顯示比例條件

State

  • track:是否計價
import React from 'react';
import Observer from '@researchgate/react-intersection-observer';

class AdImage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tracked: false
    };
  }

  handleChange = ({ isIntersecting, intersectionRatio }) => {
    const { keepSeconds } = this.props;
    if (isIntersecting && intersectionRatio >= 0.5) {
      this.recordedTimeout = setTimeout(() => {
        this.setState({ tracked: true });
      }, keepSeconds * 1000);
      return;
    }
    clearTimeout(this.recordedTimeout);
  };

  render() {
    const { src, threshold } = this.props;
    const adTrackedSrc = "https://fakeimg.pl/300x100?text=Tracked";
    let imageSrc = src;
    if (this.state.tracked === true) {
      imageSrc = adTrackedSrc;
    }
    return (
      <Observer onChange={this.handleChange} threshold={threshold}>
        <img className="adImage" src={imageSrc} />
      </Observer>
    );
  }
}

串接成畫面測試看看

import React from 'react';

export default class Default extends Component {
  render() {
    return (
      <div>
        <section className="hint">往下滑動</section>
        <section className="target">
          <AdImage
            src="https://fakeimg.pl/300x100/?text=keep1s"
            keepSeconds={1}
            threshold={0.5}
          />
          <AdImage
            src="https://fakeimg.pl/300x100/?text=keep2s"
            keepSeconds={2}
            threshold={0.5}
          />
          <AdImage
            src="https://fakeimg.pl/300x100/?text=showAll"
            keepSeconds={0}
            threshold={1}
          />
        </section>
        <section className="hint">往上滑動</section>
      </div>
    );
  }
}

Hook

小結

參考資料

* react-intersection-observer
*

Select a repo