MasatoMakino/pixijs-basic-scrollbar

View on GitHub
demoSrc/demo_scrollbar.js

Summary

Maintainability
A
3 hrs
Test Coverage
import { Application, Container, Graphics, Rectangle, Ticker } from "pixi.js";
import { ScrollBarView, ScrollBarContents } from "../esm/index.js";
import TWEEN from "@tweenjs/tween.js";

const onDomContentsLoaded = async () => {
  const app = new Application();
  await app.init({ width: 800, height: 800 });

  document.body.appendChild(app.canvas);

  Ticker.shared.add((e) => {
    TWEEN.update(performance.now());
  });

  const scrollbar = initScrollBar(app.stage, app.canvas);

  const addButton = (label) => {
    const btnPlus = document.createElement("button");
    btnPlus.innerText = label;
    document.body.appendChild(btnPlus);
    return btnPlus;
  };
  const btnPlus = addButton("Contents Size +");
  const btnMinus = addButton("Contents Size -");
  const changeSize = (dif) => {
    const scrollPosition = scrollbar.rate;
    overrideContents(scrollbar.contents.target, dif);
    scrollbar.updateSlider();
    scrollbar.changeRate(scrollPosition);
  };
  const onPlus = () => {
    changeSize(64);
  };
  const onMinus = () => {
    changeSize(-64);
  };
  btnPlus.addEventListener("click", onPlus);
  btnMinus.addEventListener("click", onMinus);
};

/**
 * スクロールバーの実装サンプル
 * @param stage
 */
const initScrollBar = (stage, view) => {
  const SCROLLBAR_W = 16;
  const SCROLLBAR_H = 360;
  const SCROLLBAR_Y = 120;
  const CONTENTS_W = 240;

  const container = new Container();
  stage.addChild(container);
  container.x = 32;
  container.y = SCROLLBAR_Y;

  const contents = getScrollBarOption(CONTENTS_W, SCROLLBAR_H, container);
  const scrollbar = new ScrollBarView(
    {
      base: getScrollBarBase(SCROLLBAR_W, SCROLLBAR_H, 0x0000ff),
      button: getScrollBarButton(SCROLLBAR_W, 0xffff00),
      minPosition: 0,
      maxPosition: SCROLLBAR_H,
      rate: 35.0,
      isHorizontal: false,
      canvas: view,
    },
    contents,
  );

  stage.addChild(scrollbar);
  scrollbar.x = container.x + CONTENTS_W;
  scrollbar.y = SCROLLBAR_Y;

  scrollbar.sliderEventEmitter.on("slider_change", (e) => {
    // console.log(e);
  });

  /**
   * スクロール動作を確認するために、故意にマスクを外しています。
   */
  // contents.target.mask = null;
  return scrollbar;
};

const getScrollBarBase = (w, h, color) => {
  const g = new Graphics();
  g.rect(0, 0, w, h).fill(color);
  g.hitArea = new Rectangle(0, 0, w, h);
  return g;
};

const getScrollBarButton = (width, color) => {
  const ratio = 0.5;
  const g = new Graphics();
  g.rect(-width / 2, -width * ratio, width, width).fill(color);

  g.hitArea = new Rectangle(-width / 2, -width * ratio, width, width);
  g.x = width / 2;
  return g;
};

const getScrollBarContents = (w, h, container, fillStyle) => {
  const g = new Graphics();
  g.rect(0, 0, w, h).fill(fillStyle);

  g.boundsArea = new Rectangle(0, 0, w, h);
  container.addChild(g);
  return g;
};

/**
 *
 * @param {Graphics} g
 * @param {number} difHeight
 */
const overrideContents = (g, difHeight) => {
  const fill = g.fillStyle;
  console.log(g);
  console.log(fill);

  const area = g.boundsArea.clone();
  area.height += difHeight;

  g.clear();
  g.rect(area.x, area.y, area.width, area.height).fill({
    color: fill.color,
    alpha: fill.alpha,
  });
  g.boundsArea = new Rectangle(area.x, area.y, area.width, area.height);
};

const getScrollBarOption = (contentsW, scrollBarH, container) => {
  const targetContents = getScrollBarContents(
    contentsW,
    scrollBarH * 2,
    container,
    { color: 0xff00ff },
  );
  const contentsMask = getScrollBarContents(contentsW, scrollBarH, container, {
    color: 0x0000ff,
    alpha: 0.3,
  });
  return new ScrollBarContents(targetContents, contentsMask, container);
};

/**
 * DOMContentLoaded以降に初期化処理を実行する
 */
if (document.readyState !== "loading") {
  onDomContentsLoaded();
} else {
  document.addEventListener("DOMContentLoaded", onDomContentsLoaded);
}