Slider-reflect
デモ
ソースコード
import { DoubleSide, Group, PlaneGeometry, VideoTexture } from "three";
import vertexShader from "./vertex.glsl";
import fragmentShader from "./fragment.glsl";
import { utils } from "negl";
import { SingleMeshSlider } from "#/parts/helper/slider/SingleMeshSlider";
export default class extends SingleMeshSlider {
beforeCreateMesh() {
this.activeSlideIdx = 0;
}
setupGeometry() {
return new PlaneGeometry(
this.rect.width * this.texes.size,
this.rect.height,
1,
1
);
}
setupTexes(uniforms) {
this.texes?.forEach((tex, key) => {
const lastChar = key.charAt(key.length - 1);
uniforms["tex" + lastChar] = { value: tex };
});
return uniforms;
}
setupUniforms() {
const uniforms = super.setupUniforms();
uniforms.uIsReflect = { value: 0 };
uniforms.uSlideIdx = { value: 0 };
uniforms.uSlideTotal = { value: this.texes.size };
uniforms.uActiveSlideIdx = { value: this.activeSlideIdx };
return uniforms;
}
setupMesh() {
const mesh = super.setupMesh();
const reflect = mesh.clone();
reflect.material = reflect.material.clone();
reflect.material.alphaTest = 0;
reflect.material.uniforms.uIsReflect.value = 1;
reflect.material.uniforms.uTick = this.uniforms.uTick;
reflect.material.uniforms.uActiveSlideIdx = this.uniforms.uActiveSlideIdx;
reflect.material.side = DoubleSide;
reflect.rotation.x = Math.PI;
const gap = 10;
reflect.position.y -= this.rect.height + gap;
const group = new Group();
group.rotation.y = 0.4;
group.add(mesh, reflect);
return group;
}
setupVertex() {
return vertexShader;
}
setupFragment() {
return fragmentShader;
}
goTo(idx) {
this.activeSlideIdx = idx;
this.playVideo(idx);
}
render(tick) {
super.render(tick);
const uActiveSlideIdx = this.uniforms.uActiveSlideIdx.value;
const idx = utils.lerp(uActiveSlideIdx, this.activeSlideIdx, 0.1);
this.uniforms.uActiveSlideIdx.value = idx;
}
playVideo(idx) {
const offset = 2;
const i = ((idx + offset) % this.texes.size) + 1;
const texValue = this.uniforms["tex" + i].value;
this.playingVideo?.pause();
if (texValue instanceof VideoTexture) {
this.playInterval = setInterval(() => {
if (this.uniforms.uActiveSlideIdx.value === idx) {
this.playingVideo = texValue.source.data;
this.playingVideo.play?.();
clearInterval(this.playInterval);
}
}, 200);
}
}
afterInit() {
setTimeout(() => {
this.texes.forEach((tex) => {
tex.source.data.pause?.();
});
this.goTo(this.activeSlideIdx);
}, 50);
}
debug(folder) {
folder
.add(this.mesh.rotation, "y", -Math.PI, Math.PI, 0.01)
.name("rotation.y");
const sliderIdx = { value: 0 };
folder
.add(sliderIdx, "value", 0, 12, 1)
.name("goTo")
.listen()
.onChange(() => {
this.goTo(sliderIdx.value);
});
}
}
利用方法
⚠️
ダウンロードしたコードをプロジェクトに配置し、以下のコードを記述してください。
index.html
<img
class="slider slider-w-45"
data-webgl="slider-reflect"
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"
/>
使用画像・動画
- こちらを参考にダウンロードした各ファイルを配置してください。