エフェクト
slider-frag

Slider-frag


デモ

ソースコード

import { Group } from "three";
import { INode, world, config } from "negl";
import { SingleMeshSlider } from "#/parts/helper/slider/SingleMeshSlider";
import vertexShader from "./vertex.glsl";
import fsBefore from "./before.glsl";
import fsAfter from "./after.glsl";

import square from "./square.glsl";
import gate from "./gate.glsl";
import diagonal from "./diagonal.glsl";
import flip from "./flip.glsl";
import book from "./book.glsl";
import collapse from "./collapse.glsl";
import swap from "./swap.glsl";

// パターンを追加する際はこちらに記述
const fragType = {
  square,
  gate,
  diagonal,
  flip,
  book,
  collapse,
  swap,
};

// book
const MIN_AMOUNT = -0.16;
const MAX_AMOUNT = 1.5;
export default class extends SingleMeshSlider {
  beforeCreateMesh() {
    super.beforeCreateMesh();
    const type = INode.getDS(this.$.el, "frag") ?? "gate";
    this.fragType = type;
    this.angleRadian = -Math.PI / 4;
  }

  setupUniforms() {
    const uniforms = super.setupUniforms();
    uniforms.progress = uniforms.uProgress;

    // book用の処理
    if (this.fragType === "book") {
      uniforms.amount = {
        value: uniforms.progress.value * (MAX_AMOUNT - MIN_AMOUNT) + MIN_AMOUNT,
      };
      uniforms.cylinderCenter = { value: uniforms.amount.value };
      // 360 degrees * uniforms.amount
      uniforms.cylinderAngle = { value: 2.0 * Math.PI * uniforms.amount.value };
      uniforms.cylinderRadius = { value: 1.0 / Math.PI / 2.0 };

      uniforms.progress._value = uniforms.progress.value;
      Object.defineProperty(uniforms.progress, "value", {
        set: function (newValue) {
          this._value = newValue;
          uniforms.amount.value =
            uniforms.progress.value * (MAX_AMOUNT - MIN_AMOUNT) + MIN_AMOUNT;
          uniforms.cylinderCenter.value = uniforms.amount.value;
          // 360 degrees * uniforms.amount
          uniforms.cylinderAngle.value = 2.0 * Math.PI * uniforms.amount.value;
          uniforms.cylinderRadius.value = 1.0 / Math.PI / 2.0;
        },
        get: function () {
          return this._value;
        },
      });
    }

    return uniforms;
  }

  setupVertex() {
    return vertexShader;
  }

  setupFragment() {
    const type = INode.getDS(this.$.el, "frag") ?? "gate";
    const frag = fragType[type];

    this.fragType = type;

    const fs = fsBefore + "\n" + frag + "\n" + fsAfter;
    return fs;
  }

  setupMesh() {
    const group = new Group();
    this.plane = super.setupMesh();
    group.add(this.plane);
    this.plane.rotation.y = this.angleRadian;
    this.plane.position.x += Math.cos(this.angleRadian);
    return group;
  }

  render(tick) {
    super.render(tick);

    if (this.fragType === "book") {
      this.uniforms.amount.value =
        this.uniforms.progress.value * (MAX_AMOUNT - MIN_AMOUNT) + MIN_AMOUNT;
    }
  }

  debug(folder) {
    super.debug(folder);

    folder
      .add(this, "angleRadian", -Math.PI, Math.PI, 0.01)
      .name("angleRadian")
      .listen()
      .onChange(() => {
        this.plane.rotation.y = this.angleRadian;
      });

    folder
      .add(this, "fragType", [
        "square",
        "gate",
        "diagonal",
        "flip",
        "book",
        "collapse",
        "swap",
      ])
      .name("frag")
      .onChange(async () => {
        const slider = world.getObByEl('[data-webgl="slider-frag"]');
        const el = slider.$.el;
        el.dataset.frag = this.fragType;
        this.fragmentShader = this.setupFragment();
        const material = this.setupMaterial();
        slider.mesh.children[0].material = material;
      });
  }
}

利用方法

⚠️

ダウンロードしたコードをプロジェクトに配置し、以下のコードを記述してください。

index.html

<img
  class="slider"
  data-webgl="slider-frag"
  data-frag="book"
  data-tex-1="/sample1.jpg"
  data-tex-2="/sample2.jpg"
  data-tex-3="/sample3.jpg"
  data-tex-4="/sample4.jpg"
  data-tex-5="/sample5.jpg"
/>

補足

data-frag には下記のいずれかの値を指定してください。デフォルトは"gate"です。

"square", "gate", "diagonal", "flip", "book", "collapse", "swap"

使用画像・動画

  • こちらを参考にダウンロードした各ファイルを配置してください。