a-farme-球に文字test-gpt-v2
<!DOCTYPE html>
<html>
<head>
<title>A-Frame - 回転する立方体 (移動修正+VR対応)</title>
<script src="https://aframe.io/releases/1.7.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-troika-text@0.11.0/dist/aframe-troika-text.min.js"></script>
<script>
// --- ★★★ カメラ移動コンポーネント (VR対応版) ★★★ ---
AFRAME.registerComponent('camera-relative-controls', {
schema: {
targetSpeed: { type: 'number', default: 5 },
acceleration: { type: 'number', default: 10 },
damping: { type: 'number', default: 8 },
brakingDeceleration: { type: 'number', default: 20 },
enabled: { type: 'boolean', default: true }
},
init: function () {
this.keys = {};
this.currentVelocity = new THREE.Vector3();
this.ZERO_VECTOR = new THREE.Vector3();
this.direction = new THREE.Vector3();
this.rightDirection = new THREE.Vector3();
this.moveDirection = new THREE.Vector3();
this.desiredVelocity = new THREE.Vector3();
this.cameraWorldQuaternion = new THREE.Quaternion();
this.cameraEl = this.el.querySelector('[camera]');
this.isReady = false;
if (!this.cameraEl) {
console.error('camera-relative-controls requires a child entity with the [camera] component.');
}
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
window.addEventListener('keydown', this.onKeyDown);
window.addEventListener('keyup', this.onKeyUp);
},
remove: function () {
window.removeEventListener('keydown', this.onKeyDown);
window.removeEventListener('keyup', this.onKeyUp);
},
tick: function (time, timeDelta) {
if (!this.data.enabled) return;
if (!this.isReady) {
if (this.cameraEl && this.cameraEl.object3D && this.cameraEl.object3D.matrixWorld) {
this.isReady = true;
} else {
if (!this.cameraEl) {
this.cameraEl = this.el.querySelector('[camera]');
}
return;
}
}
if (!this.cameraEl || !this.cameraEl.object3D) return;
const el = this.el;
const data = this.data;
const position = el.object3D.position;
const dt = timeDelta / 1000;
this.cameraEl.object3D.getWorldQuaternion(this.cameraWorldQuaternion);
this.direction.set(0, 0, -1).applyQuaternion(this.cameraWorldQuaternion);
if (this.direction.lengthSq() > 0.0001) this.direction.normalize();
this.rightDirection.set(1, 0, 0).applyQuaternion(this.cameraWorldQuaternion);
this.rightDirection.y = 0;
if (this.rightDirection.lengthSq() > 0.0001) this.rightDirection.normalize();
this.moveDirection.set(0, 0, 0);
if (this.keys['KeyW'] || this.keys['ArrowUp']) this.moveDirection.add(this.direction);
if (this.keys['KeyS'] || this.keys['ArrowDown']) this.moveDirection.sub(this.direction);
if (this.keys['KeyA'] || this.keys['ArrowLeft']) this.moveDirection.sub(this.rightDirection);
if (this.keys['KeyD'] || this.keys['ArrowRight']) this.moveDirection.add(this.rightDirection);
// --- VRコントローラー対応の移動処理追加 ---
const leftController = this.el.sceneEl.querySelector('[hand-controls="hand: left"]');
if (leftController && leftController.components['tracked-controls']) {
const gamepad = leftController.components['tracked-controls'].controller;
if (gamepad && gamepad.axes.length >= 2) {
const x = gamepad.axes[0];
const y = gamepad.axes[1];
if (Math.abs(x) > 0.1 || Math.abs(y) > 0.1) {
const moveX = this.rightDirection.clone().multiplyScalar(x);
const moveZ = this.direction.clone().multiplyScalar(-y);
this.moveDirection.add(moveX).add(moveZ);
}
}
}
// --- ここまでVR対応追加 ---
const isKeyPressed = this.moveDirection.lengthSq() > 0.0001;
if (isKeyPressed) this.moveDirection.normalize();
let lerpFactor = data.damping;
const isMoving = this.currentVelocity.lengthSq() > 0.01;
if (isKeyPressed) {
let isOpposing = false;
if (isMoving) {
const dot = this.currentVelocity.dot(this.moveDirection);
if (dot < -0.1) isOpposing = true;
}
if (isOpposing) {
this.desiredVelocity.copy(this.ZERO_VECTOR);
lerpFactor = data.brakingDeceleration;
} else {
this.desiredVelocity.copy(this.moveDirection).multiplyScalar(data.targetSpeed);
lerpFactor = data.acceleration;
}
} else {
this.desiredVelocity.copy(this.ZERO_VECTOR);
lerpFactor = data.damping;
}
const effectiveLerpFactor = 1.0 - Math.exp(-lerpFactor * dt);
this.currentVelocity.lerp(this.desiredVelocity, effectiveLerpFactor);
if (this.currentVelocity.lengthSq() < 0.0001) this.currentVelocity.copy(this.ZERO_VECTOR);
if (this.currentVelocity.lengthSq() > 0) {
const deltaPosition = this.currentVelocity.clone().multiplyScalar(dt);
position.add(deltaPosition);
}
},
onKeyDown: function (event) {
if (!this.data.enabled) return;
if (['KeyW', 'ArrowUp', 'KeyS', 'ArrowDown', 'KeyA', 'ArrowLeft', 'KeyD', 'ArrowRight'].includes(event.code)) {
this.keys[event.code] = true;
}
},
onKeyUp: function (event) {
if (this.keys[event.code] !== undefined) {
delete this.keys[event.code];
}
}
});
// --- ここまでカメラ移動コンポーネント ---
AFRAME.registerComponent('random-rotate', {
schema: { maxSpeed: { type: 'number', default: 5 } },
init: function () {
this.axis = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize();
const speed = ((Math.random() * 0.8) + 0.2) * this.data.maxSpeed * (Math.random() < 0.5 ? 1 : -1);
this.speedRad = THREE.MathUtils.degToRad(speed);
this.deltaRotationQuaternion = new THREE.Quaternion();
},
tick: function (time, timeDelta) {
const dt = timeDelta / 1000;
const angle = this.speedRad * dt;
this.deltaRotationQuaternion.setFromAxisAngle(this.axis, angle);
this.el.object3D.quaternion.multiplyQuaternions(this.deltaRotationQuaternion, this.el.object3D.quaternion);
}
});
</script>
</head>
<body>
<a-scene id="myScene" background="color: #000000">
<a-entity id="rig" position="0 0 5"
camera-relative-controls="targetSpeed: 250; acceleration: 3; damping: 5; brakingDeceleration: 1;">
<a-entity id="camera" camera="far: 20000;" look-controls position="0 1.6 0">
<a-entity cursor="rayOrigin: mouse; fuse: false;" raycaster="objects: .clickableObject, .clickableButton; far: 3000;"
position="0 0 -1"
geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03;"
material="color: black; shader: flat; opacity: 0.7">
<a-animation begin="click" easing="ease-in" attribute="scale" fill="backwards" from="0.1 0.1 0.1"
to="1 1 1" dur="150"></a-animation>
</a-entity>
</a-entity>
</a-entity>
<!-- VRハンドコントローラーの追加 -->
<a-entity hand-controls="hand: left"></a-entity>
<a-entity hand-controls="hand: right"></a-entity>
<a-entity light="type: ambient; color: #444"></a-entity>
<a-entity light="type: directional; color: #FFF; intensity: 0.8" position="-1 1.5 1"></a-entity>
<a-entity light="type: directional; color: #AAA; intensity: 0.4" position="1 1 -1"></a-entity>
<a-entity id="infoPanel" visible="false" position="0 -1000 0" look-at="[camera]">
<a-plane id="panelBackground" width="50" height="24" color="#333" opacity="0.9" side="double"></a-plane>
<a-entity id="panelText"
troika-text="value: Placeholder; color: white; fontSize: 0.72; maxWidth: 46; align: center; anchorX: center; anchorY: middle; baseline: middle;"
position="0 0 0.05">
</a-entity>
<a-triangle id="prevButton" class="clickableButton"
position="-20.0 0 0.01" rotation="0 0 90" scale="2.0 2.0 2.0"
material="color: #CCC; shader: flat; opacity: 0.8;">
</a-triangle>
<a-triangle id="nextButton" class="clickableButton"
position="20.0 0 0.01" rotation="0 0 -90" scale="2.0 2.0 2.0"
material="color: #CCC; shader: flat; opacity: 0.8;">
</a-triangle>
<a-circle id="closeButton" class="clickableButton"
position="24 11 0.05" radius="1.5" color="red" shader="flat">
</a-circle>
</a-entity>
<script>
const sceneEl = document.getElementById('myScene');
const infoPanelEl = document.getElementById('infoPanel');
const panelTextEl = document.getElementById('panelText');
const cameraEl = document.getElementById('camera');
const prevButtonEl = document.getElementById('prevButton');
const nextButtonEl = document.getElementById('nextButton');
const closeButtonEl = document.getElementById('closeButton');
const numSpheres = 60;
const spread = 2000;
const targetWorldPosition = new THREE.Vector3();
const cameraWorldPosition = new THREE.Vector3();
const direction = new THREE.Vector3();
const panelPosition = new THREE.Vector3();
const PAGES = ['index', 'color', 'size'];
const TOTAL_PAGES = PAGES.length;
for (let i = 0; i < numSpheres; i++) {
const cubeEl = document.createElement('a-box');
const side = Math.random() * 10.0 + 0.5;
const x = (Math.random() - 0.5) * spread;
const y = Math.random() * (spread / 2) + side / 2;
const z = (Math.random() - 0.5) * spread;
const color = `hsl(${Math.random() * 360}, 50%, 75%)`;
const sphereIndex = i + 1;
cubeEl.setAttribute('width', side);
cubeEl.setAttribute('height', side);
cubeEl.setAttribute('depth', side);
cubeEl.setAttribute('color', color);
cubeEl.setAttribute('position', { x: x, y: y, z: z });
cubeEl.classList.add('clickableObject');
cubeEl.dataset.sphereIndex = sphereIndex;
cubeEl.dataset.color = color;
cubeEl.dataset.size = side.toFixed(2);
cubeEl.setAttribute('random-rotate', { maxSpeed: 5 });
cubeEl.addEventListener('click', handleSphereClick);
sceneEl.appendChild(cubeEl);
}
function updatePanelDisplay() {
if (!infoPanelEl.dataset.sphereIndex) return;
const index = parseInt(infoPanelEl.dataset.sphereIndex || '0', 10);
const color = infoPanelEl.dataset.color || 'N/A';
const size = infoPanelEl.dataset.size || 'N/A';
const pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10);
let displayText = '';
const pageType = PAGES[pageIndex];
if (pageType === 'index') displayText = `立方体: ${index}`;
else if (pageType === 'color') displayText = `色: ${color}`;
else if (pageType === 'size') displayText = `サイズ: ${size}`;
const pageIndicator = `(${pageIndex + 1}/${TOTAL_PAGES})`;
const finalDisplayText = `${pageIndicator}\n${displayText}`;
panelTextEl.setAttribute('troika-text', 'value', finalDisplayText);
}
function handleSphereClick(event) {
event.stopPropagation();
const clickedCube = event.target;
if (!clickedCube.dataset.sphereIndex || !clickedCube.dataset.color || !clickedCube.dataset.size) return;
infoPanelEl.dataset.sphereIndex = clickedCube.dataset.sphereIndex;
infoPanelEl.dataset.color = clickedCube.dataset.color;
infoPanelEl.dataset.size = clickedCube.dataset.size;
infoPanelEl.dataset.currentPageIndex = '0';
updatePanelDisplay();
clickedCube.object3D.getWorldPosition(targetWorldPosition);
cameraEl.object3D.getWorldPosition(cameraWorldPosition);
const cubeSide = parseFloat(clickedCube.dataset.size || 0);
const offsetDistance = cubeSide / 2 + 0.5;
direction.subVectors(cameraWorldPosition, targetWorldPosition).normalize();
panelPosition.copy(targetWorldPosition).addScaledVector(direction, offsetDistance);
infoPanelEl.object3D.position.copy(panelPosition);
infoPanelEl.setAttribute('visible', true);
}
prevButtonEl.addEventListener('click', function (event) {
event.stopPropagation();
if (!infoPanelEl.getAttribute('visible')) return;
let pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10);
pageIndex = (pageIndex - 1 + TOTAL_PAGES) % TOTAL_PAGES;
infoPanelEl.dataset.currentPageIndex = pageIndex.toString();
updatePanelDisplay();
});
nextButtonEl.addEventListener('click', function (event) {
event.stopPropagation();
if (!infoPanelEl.getAttribute('visible')) return;
let pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10);
pageIndex = (pageIndex + 1) % TOTAL_PAGES;
infoPanelEl.dataset.currentPageIndex = pageIndex.toString();
updatePanelDisplay();
});
closeButtonEl.addEventListener('click', function (event) {
event.stopPropagation();
infoPanelEl.setAttribute('visible', false);
delete infoPanelEl.dataset.sphereIndex;
delete infoPanelEl.dataset.color;
delete infoPanelEl.dataset.size;
delete infoPanelEl.dataset.currentPageIndex;
});
</script>
</a-scene>
</body>
</html>
使用変数
<!DOCTYPE html>
<html>
<head>
<title>A-Frame - 回転する立方体 (移動修正+VR対応)</title>
<script src="https://aframe.io/releases/1.7.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-look-at-component@0.8.0/dist/aframe-look-at-component.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/aframe-troika-text@0.11.0/dist/aframe-troika-text.min.js"></script>
<script>
// --- ★★★ カメラ移動コンポーネント (VR対応版) ★★★ ---
AFRAME.registerComponent('camera-relative-controls', {
schema: {
targetSpeed: { type: 'number', default: 5 },
acceleration: { type: 'number', default: 10 },
damping: { type: 'number', default: 8 },
brakingDeceleration: { type: 'number', default: 20 },
enabled: { type: 'boolean', default: true }
},
init: function () {
this.keys = {};
this.currentVelocity = new THREE.Vector3();
this.ZERO_VECTOR = new THREE.Vector3();
this.direction = new THREE.Vector3();
this.rightDirection = new THREE.Vector3();
this.moveDirection = new THREE.Vector3();
this.desiredVelocity = new THREE.Vector3();
this.cameraWorldQuaternion = new THREE.Quaternion();
this.cameraEl = this.el.querySelector('[camera]');
this.isReady = false;
if (!this.cameraEl) {
console.error('camera-relative-controls requires a child entity with the [camera] component.');
}
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
window.addEventListener('keydown', this.onKeyDown);
window.addEventListener('keyup', this.onKeyUp);
},
remove: function () {
window.removeEventListener('keydown', this.onKeyDown);
window.removeEventListener('keyup', this.onKeyUp);
},
tick: function (time, timeDelta) {
if (!this.data.enabled) return;
if (!this.isReady) {
if (this.cameraEl && this.cameraEl.object3D && this.cameraEl.object3D.matrixWorld) {
this.isReady = true;
} else {
if (!this.cameraEl) {
this.cameraEl = this.el.querySelector('[camera]');
}
return;
}
}
if (!this.cameraEl || !this.cameraEl.object3D) return;
const el = this.el;
const data = this.data;
const position = el.object3D.position;
const dt = timeDelta / 1000;
this.cameraEl.object3D.getWorldQuaternion(this.cameraWorldQuaternion);
this.direction.set(0, 0, -1).applyQuaternion(this.cameraWorldQuaternion);
if (this.direction.lengthSq() > 0.0001) this.direction.normalize();
this.rightDirection.set(1, 0, 0).applyQuaternion(this.cameraWorldQuaternion);
this.rightDirection.y = 0;
if (this.rightDirection.lengthSq() > 0.0001) this.rightDirection.normalize();
this.moveDirection.set(0, 0, 0);
if (this.keys['KeyW'] || this.keys['ArrowUp']) this.moveDirection.add(this.direction);
if (this.keys['KeyS'] || this.keys['ArrowDown']) this.moveDirection.sub(this.direction);
if (this.keys['KeyA'] || this.keys['ArrowLeft']) this.moveDirection.sub(this.rightDirection);
if (this.keys['KeyD'] || this.keys['ArrowRight']) this.moveDirection.add(this.rightDirection);
// --- VRコントローラー対応の移動処理追加 ---
const leftController = this.el.sceneEl.querySelector('[hand-controls="hand: left"]');
if (leftController && leftController.components['tracked-controls']) {
const gamepad = leftController.components['tracked-controls'].controller;
if (gamepad && gamepad.axes.length >= 2) {
const x = gamepad.axes[0];
const y = gamepad.axes[1];
if (Math.abs(x) > 0.1 || Math.abs(y) > 0.1) {
const moveX = this.rightDirection.clone().multiplyScalar(x);
const moveZ = this.direction.clone().multiplyScalar(-y);
this.moveDirection.add(moveX).add(moveZ);
}
}
}
// --- ここまでVR対応追加 ---
const isKeyPressed = this.moveDirection.lengthSq() > 0.0001;
if (isKeyPressed) this.moveDirection.normalize();
let lerpFactor = data.damping;
const isMoving = this.currentVelocity.lengthSq() > 0.01;
if (isKeyPressed) {
let isOpposing = false;
if (isMoving) {
const dot = this.currentVelocity.dot(this.moveDirection);
if (dot < -0.1) isOpposing = true;
}
if (isOpposing) {
this.desiredVelocity.copy(this.ZERO_VECTOR);
lerpFactor = data.brakingDeceleration;
} else {
this.desiredVelocity.copy(this.moveDirection).multiplyScalar(data.targetSpeed);
lerpFactor = data.acceleration;
}
} else {
this.desiredVelocity.copy(this.ZERO_VECTOR);
lerpFactor = data.damping;
}
const effectiveLerpFactor = 1.0 - Math.exp(-lerpFactor * dt);
this.currentVelocity.lerp(this.desiredVelocity, effectiveLerpFactor);
if (this.currentVelocity.lengthSq() < 0.0001) this.currentVelocity.copy(this.ZERO_VECTOR);
if (this.currentVelocity.lengthSq() > 0) {
const deltaPosition = this.currentVelocity.clone().multiplyScalar(dt);
position.add(deltaPosition);
}
},
onKeyDown: function (event) {
if (!this.data.enabled) return;
if (['KeyW', 'ArrowUp', 'KeyS', 'ArrowDown', 'KeyA', 'ArrowLeft', 'KeyD', 'ArrowRight'].includes(event.code)) {
this.keys[event.code] = true;
}
},
onKeyUp: function (event) {
if (this.keys[event.code] !== undefined) {
delete this.keys[event.code];
}
}
});
// --- ここまでカメラ移動コンポーネント ---
AFRAME.registerComponent('random-rotate', {
schema: { maxSpeed: { type: 'number', default: 5 } },
init: function () {
this.axis = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).normalize();
const speed = ((Math.random() * 0.8) + 0.2) * this.data.maxSpeed * (Math.random() < 0.5 ? 1 : -1);
this.speedRad = THREE.MathUtils.degToRad(speed);
this.deltaRotationQuaternion = new THREE.Quaternion();
},
tick: function (time, timeDelta) {
const dt = timeDelta / 1000;
const angle = this.speedRad * dt;
this.deltaRotationQuaternion.setFromAxisAngle(this.axis, angle);
this.el.object3D.quaternion.multiplyQuaternions(this.deltaRotationQuaternion, this.el.object3D.quaternion);
}
});
</script>
</head>
<body>
<a-scene id="myScene" background="color: #000000">
<a-entity id="rig" position="0 0 5"
camera-relative-controls="targetSpeed: 250; acceleration: 3; damping: 5; brakingDeceleration: 1;">
<a-entity id="camera" camera="far: 20000;" look-controls position="0 1.6 0">
<a-entity cursor="rayOrigin: mouse; fuse: false;" raycaster="objects: .clickableObject, .clickableButton; far: 3000;"
position="0 0 -1"
geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03;"
material="color: black; shader: flat; opacity: 0.7">
<a-animation begin="click" easing="ease-in" attribute="scale" fill="backwards" from="0.1 0.1 0.1"
to="1 1 1" dur="150"></a-animation>
</a-entity>
</a-entity>
</a-entity>
<!-- VRハンドコントローラーの追加 -->
<a-entity hand-controls="hand: left"></a-entity>
<a-entity hand-controls="hand: right"></a-entity>
<a-entity light="type: ambient; color: #444"></a-entity>
<a-entity light="type: directional; color: #FFF; intensity: 0.8" position="-1 1.5 1"></a-entity>
<a-entity light="type: directional; color: #AAA; intensity: 0.4" position="1 1 -1"></a-entity>
<a-entity id="infoPanel" visible="false" position="0 -1000 0" look-at="[camera]">
<a-plane id="panelBackground" width="50" height="24" color="#333" opacity="0.9" side="double"></a-plane>
<a-entity id="panelText"
troika-text="value: Placeholder; color: white; fontSize: 0.72; maxWidth: 46; align: center; anchorX: center; anchorY: middle; baseline: middle;"
position="0 0 0.05">
</a-entity>
<a-triangle id="prevButton" class="clickableButton"
position="-20.0 0 0.01" rotation="0 0 90" scale="2.0 2.0 2.0"
material="color: #CCC; shader: flat; opacity: 0.8;">
</a-triangle>
<a-triangle id="nextButton" class="clickableButton"
position="20.0 0 0.01" rotation="0 0 -90" scale="2.0 2.0 2.0"
material="color: #CCC; shader: flat; opacity: 0.8;">
</a-triangle>
<a-circle id="closeButton" class="clickableButton"
position="24 11 0.05" radius="1.5" color="red" shader="flat">
</a-circle>
</a-entity>
<script>
const sceneEl = document.getElementById('myScene');
const infoPanelEl = document.getElementById('infoPanel');
const panelTextEl = document.getElementById('panelText');
const cameraEl = document.getElementById('camera');
const prevButtonEl = document.getElementById('prevButton');
const nextButtonEl = document.getElementById('nextButton');
const closeButtonEl = document.getElementById('closeButton');
const numSpheres = 60;
const spread = 2000;
const targetWorldPosition = new THREE.Vector3();
const cameraWorldPosition = new THREE.Vector3();
const direction = new THREE.Vector3();
const panelPosition = new THREE.Vector3();
const PAGES = ['index', 'color', 'size'];
const TOTAL_PAGES = PAGES.length;
for (let i = 0; i < numSpheres; i++) {
const cubeEl = document.createElement('a-box');
const side = Math.random() * 10.0 + 0.5;
const x = (Math.random() - 0.5) * spread;
const y = Math.random() * (spread / 2) + side / 2;
const z = (Math.random() - 0.5) * spread;
const color = `hsl(${Math.random() * 360}, 50%, 75%)`;
const sphereIndex = i + 1;
cubeEl.setAttribute('width', side);
cubeEl.setAttribute('height', side);
cubeEl.setAttribute('depth', side);
cubeEl.setAttribute('color', color);
cubeEl.setAttribute('position', { x: x, y: y, z: z });
cubeEl.classList.add('clickableObject');
cubeEl.dataset.sphereIndex = sphereIndex;
cubeEl.dataset.color = color;
cubeEl.dataset.size = side.toFixed(2);
cubeEl.setAttribute('random-rotate', { maxSpeed: 5 });
cubeEl.addEventListener('click', handleSphereClick);
sceneEl.appendChild(cubeEl);
}
function updatePanelDisplay() {
if (!infoPanelEl.dataset.sphereIndex) return;
const index = parseInt(infoPanelEl.dataset.sphereIndex || '0', 10);
const color = infoPanelEl.dataset.color || 'N/A';
const size = infoPanelEl.dataset.size || 'N/A';
const pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10);
let displayText = '';
const pageType = PAGES[pageIndex];
if (pageType === 'index') displayText = `立方体: ${index}`;
else if (pageType === 'color') displayText = `色: ${color}`;
else if (pageType === 'size') displayText = `サイズ: ${size}`;
const pageIndicator = `(${pageIndex + 1}/${TOTAL_PAGES})`;
const finalDisplayText = `${pageIndicator}\n${displayText}`;
panelTextEl.setAttribute('troika-text', 'value', finalDisplayText);
}
function handleSphereClick(event) {
event.stopPropagation();
const clickedCube = event.target;
if (!clickedCube.dataset.sphereIndex || !clickedCube.dataset.color || !clickedCube.dataset.size) return;
infoPanelEl.dataset.sphereIndex = clickedCube.dataset.sphereIndex;
infoPanelEl.dataset.color = clickedCube.dataset.color;
infoPanelEl.dataset.size = clickedCube.dataset.size;
infoPanelEl.dataset.currentPageIndex = '0';
updatePanelDisplay();
clickedCube.object3D.getWorldPosition(targetWorldPosition);
cameraEl.object3D.getWorldPosition(cameraWorldPosition);
const cubeSide = parseFloat(clickedCube.dataset.size || 0);
const offsetDistance = cubeSide / 2 + 0.5;
direction.subVectors(cameraWorldPosition, targetWorldPosition).normalize();
panelPosition.copy(targetWorldPosition).addScaledVector(direction, offsetDistance);
infoPanelEl.object3D.position.copy(panelPosition);
infoPanelEl.setAttribute('visible', true);
}
prevButtonEl.addEventListener('click', function (event) {
event.stopPropagation();
if (!infoPanelEl.getAttribute('visible')) return;
let pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10);
pageIndex = (pageIndex - 1 + TOTAL_PAGES) % TOTAL_PAGES;
infoPanelEl.dataset.currentPageIndex = pageIndex.toString();
updatePanelDisplay();
});
nextButtonEl.addEventListener('click', function (event) {
event.stopPropagation();
if (!infoPanelEl.getAttribute('visible')) return;
let pageIndex = parseInt(infoPanelEl.dataset.currentPageIndex || '0', 10);
pageIndex = (pageIndex + 1) % TOTAL_PAGES;
infoPanelEl.dataset.currentPageIndex = pageIndex.toString();
updatePanelDisplay();
});
closeButtonEl.addEventListener('click', function (event) {
event.stopPropagation();
infoPanelEl.setAttribute('visible', false);
delete infoPanelEl.dataset.sphereIndex;
delete infoPanelEl.dataset.color;
delete infoPanelEl.dataset.size;
delete infoPanelEl.dataset.currentPageIndex;
});
</script>
</a-scene>
</body>
</html>
使用変数
-------( Function ) | |
ameraWorldPosition | |
angle | |
argetWorldPosition | |
at | |
attribute | |
axis | |
background | |
begin | |
camera | |
cameraEl | |
class | |
clickedCube | |
closeButtonEl | |
color | |
controls | |
cubeEl | |
cubeSide | |
currentPageIndex | |
currentVelocity | |
cursor | |
data | |
deltaPosition | |
desiredVelocity | |
direction | |
displayText | |
dot | |
dt | |
dur | |
easing | |
el | |
eraWorldQuaternion | |
ffectiveLerpFactor | |
fill | |
finalDisplayText | |
from | |
gamepad | |
geometry | |
handleSphereClick -------( Function ) | |
height | |
i | |
id | |
index | |
infoPanelEl | |
isKeyPressed | |
isMoving | |
isOpposing | |
isReady | |
keys | |
leftController | |
lerpFactor | |
light | |
material | |
moveDirection | |
moveX | |
moveZ | |
nextButtonEl | |
numSpheres | |
offsetDistance | |
onKeyDown | |
onKeyUp | |
opacity | |
pageIndex | |
pageIndicator | |
PAGES | |
pageType | |
panelPosition | |
panelTextEl | |
position | |
prevButtonEl | |
radius | |
raycaster | |
rightDirection | |
rotation | |
RotationQuaternion | |
scale | |
sceneEl | |
shader | |
side | |
size | |
speed | |
speedRad | |
sphereIndex | |
spread | |
src | |
text | |
to | |
TOTAL_PAGES | |
updatePanelDisplay -------( Function ) | |
visible | |
width | |
x | |
y | |
z | |
ZERO_VECTOR |