@croquet/microverse-library
Version:
An npm package version of Microverse
237 lines (198 loc) • 8.12 kB
JavaScript
// the following import statement is solely for the type checking and
// autocompletion features in IDE. A Behavior cannot inherit from
// another behavior or a base class but can use the methods and
// properties of the card to which it is installed.
// The prototype classes ActorBehavior and PawnBehavior provide
// the features defined at the card object.
import {ActorBehavior, PawnBehavior} from "../PrototypeBehavior";
class ScrollAreaActor extends ActorBehavior {
setup() {
this.noSave = true;
console.log("setup.scrollTop", this.scrollTop);
this.subscribe(this.id, "scrollTop", "setScrollTop");
}
setTarget(card) {
if (!this.scroller) {
this.scroller = this.createCard({
name: "scroll bar",
type: "2d",
parent: this,
behaviorModules: ["ScrollBar"],
noSave: true,
fullBright: true,
height: this._cardData.height,
width: 0.1,
depth: 0.01,
backgroundColor: 0x606060,
color: 0x808080,
});
}
this.target = card;
this.say("targetSet");
if (this.scrollTop === undefined) this.scrollTop = 0; // [0..1]
this.future(1).setScrollTop(this.scrollTop, 1, 2, 3);
}
setScrollTop(scrollTop) {
// scrollTop = 0 -> (this._cardData.height - this.target._cardData.height) / 2
// scrollTop = 1 -> this.target._cardData.height - this._cardData.height / 2
this.scrollTop = scrollTop;
let diff = this._cardData.height - this.target._cardData.height;
let offset = ((-diff) * scrollTop + diff * (1 - scrollTop)) / 1.8;
let t = this.target.translation;
if (diff > 0) {
this.target.set({translation: [t[0], diff / 2 - 0.1, t[2]]});
} else {
this.target.set({translation: [t[0], offset, t[2]]});
}
let width = this._cardData.width;
if (!this.scroller) {return;}
this.scroller.set({
translation: [width / 2 + 0.05, 0, 0.01]
});
this.scroller.setCardData({
width: 0.1,
height: this._cardData.height,
});
this.say("updateDisplay");
}
}
class ScrollAreaPawn extends PawnBehavior {
setup() {
this.listen("targetSet", "targetSet");
this.listen("updateDisplay", "updateDisplay");
if (this.actor.target) {
this.targetSet();
}
this.future(1000).updateDisplay();
}
targetSet() {
//console.log("target set");
this.initializeClipping();
[...this.shape.children].forEach((c) => this.shape.remove(c));
let geometry = this.roundedCornerGeometry(
this.actor._cardData.width,
this.actor._cardData.height,
this.actor._cardData.depth,
this.actor._cardData.cornerRadius || 0.01
);
let material = this.makePlaneMaterial(this.actor._cardData.depth, this.actor._cardData.color, this.actor._cardData.frameColor || 0xaaaaaa, true);
let mesh = new Microverse.THREE.Mesh(geometry, material);
this.shape.add(mesh);
}
initializeClipping() {
let THREE = Microverse.THREE;
this.clippingPlanes = [
new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
new THREE.Plane(new THREE.Vector3(0, -1, 0), 0),
new THREE.Plane(new THREE.Vector3(-1, 0, 0), 0),
new THREE.Plane(new THREE.Vector3(1, 0, 0), 0)
];
}
updateDisplay() {
// console.log("updateDisplay");
let w = this.actor._cardData.width;
let h = this.actor._cardData.height;
let left = w / 2;
let right = w / 2;
let bottom = h / 2; // * (1 - this.actor.scrollTop) - 0.5
let top = h / 2;
this.material[0].clippingPlanes = this.computeClippingPlanes([top, bottom, left, right]);
let topLeft = [-left, top, 0];
let bottomRight = [right, -bottom, 0];
let globalTopLeft = Microverse.v3_transform(topLeft, this.global);
let globalBottomRight = Microverse.v3_transform(bottomRight, this.global);
let pawn = Microverse.GetPawn(this.actor.target.id);
// let pawnInv = Microverse.m4_invert(pawn._global);
// let pawnTopLeft = Microverse.v3_transform(globalTopLeft, pawnInv);
// let pawnBottomRight = Microverse.v3_transform(globalBottomRight, pawnInv);
let menu = [...pawn.children][0];
let planes = menu.call("Menu$MenuPawn", "menuComputeClippingPlanes", [top, bottom, left, right]);
menu.material.clippingPlanes = planes;
if (!pawn) {return;}
pawn.children.forEach((c) => {
c.children.forEach((d) => {
if (d.setTextRenderingBounds) {
let th = d.actor._cardData.height;
let textInv = Microverse.m4_invert(d.global);
let textTopLeft = Microverse.v3_transform(globalTopLeft, textInv);
let textBottomRight = Microverse.v3_transform(globalBottomRight, textInv);
let bTop = -(textTopLeft[1] - (th / 2)) / d.textScale();
let bBottom = -(textBottomRight[1] + (th / 2)) / d.textScale();
d.setTextRenderingBounds({left: 0, right: 1000, top: bTop, bottom: bBottom});
}
});
});
}
computeClippingPlanes(ary) {
//let [top, bottom, right, left] = ary; this is the order
let planes = [];
if (Number.isNaN(this.shape.matrixWorld.elements[0])) return [];
for (let i = 0; i < 4; i++) {
planes[i] = new Microverse.THREE.Plane();
planes[i].copy(this.clippingPlanes[i]);
planes[i].constant = ary[i];
planes[i].applyMatrix4(this.shape.matrixWorld);
}
return planes;
}
}
class ScrollBarActor extends ActorBehavior {
setup() {}
}
class ScrollBarPawn extends PawnBehavior {
setup() {
this.addEventListener("pointerDown", "pointerDown");
this.addEventListener("pointerUp", "pointerUp");
this.addEventListener("pointerMove", "pointerMove");
}
pointerDown(evt) {
if (!evt.xyz) {return;}
if (!this._parent) {return;}
let startScrollTop = this._parent.actor.scrollTop;
let vec = new Microverse.THREE.Vector3(...evt.xyz);
let inv = this.renderObject.matrixWorld.clone().invert();
vec = vec.applyMatrix4(inv);
let downInfo = {child: this, startScrollTop, downPosition: vec};
this.downInfo = downInfo
let avatar = this.service("PawnManager").get(evt.avatarId);
if (avatar) {
avatar.addFirstResponder("pointerMove", {}, this);
}
}
pointerUp(evt) {
if (this.downInfo) {
delete this.downInfo;
let avatar = this.service("PawnManager").get(evt.avatarId);
if (avatar) {
avatar.removeFirstResponder("pointerMove", {}, this);
}
}
}
pointerMove(evt) {
if (!evt.xyz) {return;}
if (!this.downInfo) {return;}
let vec = new Microverse.THREE.Vector3(...evt.xyz);
let pInv = this.renderObject.matrixWorld.clone().invert();
vec = vec.applyMatrix4(pInv);
let origDownPoint = this.downInfo.downPosition;
let origScrollTop = this.downInfo.startScrollTop;
let deltaY = origDownPoint.y - vec.y;
let scrollTop = Math.min(Math.max(deltaY / this.actor._cardData.height + origScrollTop, 0), 1);
this.publish(this.actor._parent.id, "scrollTop", scrollTop);
}
}
export default {
modules: [
{
name: "ScrollArea",
actorBehaviors: [ScrollAreaActor],
pawnBehaviors: [ScrollAreaPawn]
},
{
name: "ScrollBar",
actorBehaviors: [ScrollBarActor],
pawnBehaviors: [ScrollBarPawn]
}
]
}
/* globals Microverse */