ã¦ã§ããªã¼ãã£ãª API ã«ã¯ãè±å¯ãªãµã¦ã³ãå¦çï¼ä»ã«ãï¼ãªãã·ã§ã³ãããã¾ãããä¾ãã°ã 3D ã²ã¼ã å ã§é³æºã®å¨ããç§»åããéã®ãã³ãã³ã°ã®ããã«ããªã¹ãã¼ã鳿ºã®å¨ããç§»åããéã®é³ã®éããã¨ãã¥ã¬ã¼ãããæ©è½ãå«ã¾ãã¦ãã¾ãã ãããå ¬å¼ã«ã¯ç©ºéåã¨å¼ã³ããã®è¨äºã§ã¯ãã®ãããªã·ã¹ãã ã®å®è£ æ¹æ³ã®åºæ¬ã«å¿ãã説æããã¾ãã
空éåã®åºæ¬ã¦ã§ããªã¼ãã£ãªã§ã¯ãè¤é㪠3D 空éå㯠PannerNode
ã使ã£ã¦ä½æããã¾ããå¹³ããè¨ãã°ãé³å£°ã 3D 空éã«ç¾ããããã«ããããã®ãããããã®ã¯ã¼ã«ãªæ°å¦ã§ãã é³ã¯ããªãã®ä¸ãé£ãã ããããªãã®å¾ãã«å¿ã³å¯ã£ãããããªãã®åãç§»åããããã¾ãã ãããããã¨ã§ãã
WebXR ãã²ã¼ã ã«ã¯å®ã«æçã§ãã 3D 空éã§ã¯ããªã¢ã«ãªé³å£°ãå®ç¾ããå¯ä¸ã®æ¹æ³ã§ãã three.js ã A-frame ã®ãããªã©ã¤ãã©ãªã¼ã¯ãé³ãæ±ãã¨ãã«ãã®å¯è½æ§ãå©ç¨ãã¾ãã å®å ¨ãª 3D 空éå ã§é³ãç§»åãããå¿ è¦ã¯ãªãã 2D å¹³é¢ã ãã«éå®ãããã¨ãã§ããã®ã§ããã 2D ã²ã¼ã ãè¨ç»ãã¦ããã¨ãã¦ãããã®ãã¼ãã¯æ¢ãã¦ãããã¼ãã«ãªãã§ãããã
ã¡ã¢: åç´ãªå·¦å³ã®ã¹ãã¬ãªãã³ãã³ã°å¹æã使ãããä¸è¬çãªç¨éã«å¯¾å¦ããããã«è¨è¨ããã StereoPannerNode
ãããã¾ãã ããã¯ä½¿ç¨ããã®ãã¯ããã«ç°¡åã§ãããæããã«æ±ç¨æ§ã«ã¯é ãåã³ã¾ããã åç´ãªã¹ãã¬ãªãã³ãã³ã°å¹æã欲ããã ããªãã StereoPannerNode ã®ä¾ ï¼ã½ã¼ã¹ã³ã¼ããåç
§ï¼ã§å¿
è¦ãªãã®ã¯ãã¹ã¦å¾ãããã§ãããã
3D 空éåã®ãã¢ãè¡ãããã«ãåºæ¬çãªã¦ã§ããªã¼ãã£ãª API ã®ä½¿ç¨ã¬ã¤ãã§ä½æããã©ã¸ã«ã»ãã¢ã夿´ãããã®ã使ãã¾ããã 3D 空éåãã¢ã©ã¤ãçãã覧ãã ããï¼ã½ã¼ã¹ã³ã¼ããã覧ãã ããï¼ã
ãã®ãã¢ã§ã¯ãæå®ãããã³ã³ããã¼ã©ã¼ã§ã©ã¸ã«ã»ãç§»åãããå転ãããããããã¨ãã§ãã¾ãã ã©ã¸ã«ã»ãç§»åãããã¨ãããã«å¿ãã¦é³ãå¤åããé¨å±ã®å·¦å³ã«ç§»ãããã¨ãã³ããããã¦ã¼ã¶ã¼ããé ãããã¨å°ãããªã£ãããã¹ãã¼ã«ã¼ãã¦ã¼ã¶ã¼ããé¢ããããã«å転ããããã¾ãã ãã㯠PannerNode
ãªãã¸ã§ã¯ãã¤ã³ã¹ã¿ã³ã¹ã®æ§ã
ãªããããã£ããã®åãã«é¢é£ãã¦è¨å®ãããã¨ã§ã空éåãã¨ãã¥ã¬ã¼ããã¦ãã¾ãã
ã¡ã¢: ããããã©ã³ã使ç¨ããããã³ã³ãã¥ã¼ã¿ã¼ã«æ¥ç¶ãããµã©ã¦ã³ããµã¦ã³ãã·ã¹ãã ãããã°ãã¯ããã«è¯ãçµé¨ãã§ãã¾ãã
é³å£°ãªã¹ãã¼ã®ä½æããã§ã¯å§ãã¾ãããï¼ BaseAudioContext
ï¼AudioContext
ãæ¡å¼µãããã¤ã³ã¿ã¼ãã§ã¤ã¹ï¼ã¯ listener
ããããã£ãæã£ã¦ãããããã AudioListener
ãªãã¸ã§ã¯ããè¿ãã¾ãã ããã¯ã·ã¼ã³ã®ãªã¹ãã¼ï¼é常ã¯ã¦ã¼ã¶ã¼ï¼ã表ãã¾ãã 空éã®ã©ãã«ãã¦ãã©ã®æ¹åãåãã¦ããããå®ç¾©ãã¾ãã ãããã¯éçãªã¾ã¾ã§ãã pannerNode
ã¯ãªã¹ãã¼ã®ä½ç½®ã¨ã®ç¸å¯¾çãªé³ã®ä½ç½®ãè¨ç®ãããã¨ãã§ãã¾ãã
ã³ã³ããã¹ãã¨ãªã¹ãã¼ã使ãããªã¹ãã¼ã®ä½ç½®ããé¨å±ãè¦ãè¾¼ãã§ããäººãæ³å®ãã¦è¨å®ãã¾ãããã
const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioCtx = new AudioContext();
const listener = audioCtx.listener;
const posX = window.innerWidth / 2;
const posY = window.innerHeight / 2;
const posZ = 300;
listener.positionX.value = posX;
listener.positionY.value = posY;
listener.positionZ.value = posZ - 5;
positionX
ã使ç¨ãã¦ãªã¹ãã¼ãå·¦å³ã«ã positionY
ã使ç¨ãã¦ä¸ä¸ã«ã positionZ
ã使ç¨ãã¦é¨å±ã®å
å¤ã«ç§»åããããã¨ãã§ãã¾ããããã§ã¯ããã¥ã¼ãã¼ãã®ä¸å¤®ãã©ã¸ã«ã»ã®å°ãåã«ãªã¹ãã¼ãè¨å®ãã¦ãã¾ããã¾ãããªã¹ãã¼ãåãã¦ããæ¹åãè¨å®ãããã¨ãã§ãã¾ãããããæ¢å®å¤ã§ããããã¾ãåä½ãã¾ãã
listener.forwardX.value = 0;
listener.forwardY.value = 0;
listener.forwardZ.value = -1;
listener.upX.value = 0;
listener.upY.value = 1;
listener.upZ.value = 0;
forward ããããã£ã¯ããªã¹ãã¼ã®åæ¹ï¼ä¾ãã°ãå½¼ããåãã¦ããæ¹åï¼ã® 3D 座æ¨ä½ç½®ã表ãã up ããããã£ã¯ããªã¹ãã¼ã®é ã®ã¦ã£ãºãã® 3D 座æ¨ä½ç½®ã表ãã¾ãã ãã® 2 ã¤ãçµã¿åããããã¨ã§ããã¾ãæ¹åãè¨å®ãããã¨ãã§ãã¾ãã
ããã¼ãã¼ãã®ä½æPannerNode
ã使ãã¾ããããããã«ã¯ããããã®ããããã£ãããã¾ãããããããè¦ã¦ããã¾ãããã
å§ãã«ã panningModel
ãè¨å®ãã¾ãã ãã㯠3D 空éã§é³å£°ãä½ç½®æå®ããããã«ä½¿ç¨ãã空éæå®ã¢ã«ã´ãªãºã ã§ãããããè¨å®ããã«ã¯æ¬¡ã®ããã«ãã¾ãã
equalpower
â æ¢å®ã§ä¸è¬çãªãã³ãã³ã°ã®æ¹æ³
HRTF
â ãã㯠'Head-related transfer function' ã®ç¥ã§ãé³ã®ä½ç½®ãææ¡ããéã«äººéã®é ãèæ
®ãã¦ããããã§ãã
ãªããªãè³¢ãã HRTF
ã¢ãã«ã使ç¨ãã¦ã¿ã¾ãããã
const panningModel = "HRTF";
coneInnerAngle
㨠coneOuterAngle
ããããã£ãæå®ãã¾ãã æ¢å®ã§ã¯ã©ã¡ãã 360 度ã§ãã ç§ãã¡ã®ã©ã¸ã«ã»ã®ã¹ãã¼ã«ã¼ã¯ãããå°ããªåéãæã¤ãã¨ã«ãªãããããå®ç¾©ãããã¨ãã§ãã¾ãã å
å´ã®ã³ã¼ã³ã¯ã²ã¤ã³ï¼é³éï¼ãå¸¸ã«æå¤§ã§ã¨ãã¥ã¬ã¼ããããå ´æã§ãå¤å´ã®ã³ã¼ã³ã¯ã²ã¤ã³ï¼é³éï¼ãä¸ããå§ããå ´æã§ãã ã²ã¤ã³ã¯ coneOuterGain
ã®å¤ã§ç¸®å°ãã¾ãã å¾ã§ãããã®å¼æ°ã«ä½¿ç¨ããå¤ãæ ¼ç´ãã宿°ã使ãã¾ãããã
const innerCone = 60;
const outerCone = 90;
const outerGain = 0.3;
次ã®å¼æ°ã¯ distanceModel
- ãã㯠linear
ãinverse
ãexponential
ã®ããããã«ã®ã¿è¨å®ãããã¨ãã§ãã¾ãããããã¯ç°ãªãã¢ã«ã´ãªãºã ã§ãé³å£°ã½ã¼ã¹ããªã¹ãã¼ããé ãããã«ã¤ãã¦é³éã縮å°ããããã«ä½¿ç¨ãã¾ããåç´ãªã®ã§ linear
ã使ç¨ãã¾ãã
const distanceModel = "linear";
ã½ã¼ã¹ã¨ãªã¹ãã¼ã®éã«æå¤§è·é¢ (maxDistance
) ãè¨å®ãããã¨ãã§ãã¾ããã½ã¼ã¹ããã®ç¹ããããã«ç§»åããã¦ãé³éã¯ç¸®å°ãã¾ããã ããã¯æçãªãã¨ãã§ãã¾ããè·é¢ãã¨ãã¥ã¬ã¼ããããããé³éãè½ã¡ã¦ãã¾ããã¨ããããããã¯å®éã«ã¯æããã¨ã§ã¯ãªãããã§ãã æ¢å®ã§ã¯ 10,000 ï¼åä½ãªãã®ç¸å¯¾å¤ï¼ã§ãããã®ã¾ã¾ã§ãæ§ãã¾ããã
const maxDistance = 10000;
åç
§è·é¢ (refDistance
) ããããããã¯è·é¢ã¢ãã«ã§ä½¿ç¨ãã¾ãã ãããæ¢å®å¤ 1
ã®ã¾ã¾ã§ããã§ãããã
次ã«ãã¼ã«ãªãä¿æ°ã(rolloffFactor
)ããããã¾ããããã¼ããªã¹ãã¼ããé¢ããã«ã¤ãã¦é³éãã©ã®ããããã°ãã縮å°ãããã§ãã æ¢å®å¤ã¯ 1 ã§ããåããèªå¼µããããã«ãããå°ã大ãããã¦ã¿ã¾ãããã
ããã§ãã©ã¸ã«ã»ã®ä½ç½®ã¨æ¹åãè¨å®ãå§ãããã¨ãã§ãã¾ãã ããã¯ããªã¹ãã¼ã§è¡ã£ãæ¹æ³ã¨ããä¼¼ã¦ãã¾ãã ãããã¯ã¤ã³ã¿ã¼ãã§ã¤ã¹ã®ã³ã³ããã¼ã«ã使ç¨ããã¨ãã«å¤æ´ãã弿°ã§ãããã¾ãã
const positionX = posX;
const positionY = posY;
const positionZ = posZ;
const orientationX = 0.0;
const orientationY = 0.0;
const orientationZ = -1.0;
Z æ¹åããã¤ãã¹å¤ã§ãããã¨ã«æ³¨æãã¦ãã ãããããã§ã©ã¸ã«ã»ããã¡ããåãããã«è¨å®ãã¾ãã æ£ã®å¤ãè¨å®ããã¨ãé³ã¯ãã¡ãå´ãåãã¦è¨å®ããã¾ãã
ããã¼ãã¼ãã使ããããã«ãé¢é£ããã³ã³ã¹ãã©ã¯ã¿ã¼ã使ç¨ããä¸ã§è¨å®ãã弿°ããã¹ã¦æ¸¡ãã¦ã¿ã¾ãããã
const panner = new PannerNode(audioCtx, {
panningModel,
distanceModel,
positionX,
positionY,
positionZ,
orientationX,
orientationY,
orientationZ,
refDistance,
maxDistance,
rolloffFactor: rollOff,
coneInnerAngle: innerCone,
coneOuterAngle: outerCone,
coneOuterGain: outerGain,
});
ã©ã¸ã«ã»ã®ç§»å
ããã§ãã©ã¸ã«ã»ããé¨å±ãã®ä¸ã§ç§»åããããã¨ãã§ãã¾ãããã®ããã«ä¸é£ã®ã³ã³ããã¼ã«ãè¨å®ãã¾ããã å·¦å³ãä¸ä¸ãåå¾ã«ç§»åãããããå転ãããããããã¨ãã§ãã¾ãã é³ã®æ¹åã¯æ£é¢ã®ã©ã¸ã«ã»ã®ã¹ãã¼ã«ã¼ããæ±ºã¾ãã¾ãã®ã§ãã©ã¸ã«ã»ãå転ãããã¨ãé³ã®æ¹åãå¤ãããã¨ãã§ãã¾ãã
ã¤ã³ã¿ã¼ãã§ã¤ã¹ã®ããã«ããã¤ãã®ãã¨ãè¨å®ããå¿ è¦ãããã¾ãã æåã«ãç§»åããããè¦ç´ ã®åç §ãåå¾ããæ¬¡ã«å®éã«ç§»åãããããã« CSS 座æ¨å¤æãè¨å®ããã¨ãã«å¤æ´ããå¤ã®åç §ãæ ¼ç´ãã¾ãã æå¾ã«ãã©ã¸ã«ã»ãã©ã®æ¹åã«ãç§»åããããªãããã«ãå¢çãè¨å®ãã¾ãã
const moveControls = document
.querySelector("#move-controls")
.querySelectorAll("button");
const boombox = document.querySelector(".boombox-body");
// CSS 座æ¨å¤æã®å¤
const transform = {
xAxis: 0,
yAxis: 0,
zAxis: 0.8,
rotateX: 0,
rotateY: 0,
};
// å¢çãè¨å®
const topBound = -posY;
const bottomBound = posY;
const rightBound = posX;
const leftBound = -posX;
const innerBound = 0.1;
const outerBound = 1.5;
ç§»åãããæ¹åã弿°ã¨ãã¦åãåãã CSS 座æ¨å¤æã夿´ããããã¼ãã¼ãããããã£ã®ä½ç½®ã¨æ¹åã®å¤ãæ´æ°ããé³ãé©åã«å¤æ´ãã颿°ã使ãã¦ã¿ã¾ãããã
å§ããã«ã¯ãå·¦ãå³ãä¸ãä¸ã®å¤ãè¦ã¦ã¿ã¾ãããã ãããã®è»¸ã«æ²¿ã£ã¦ã©ã¸ã«ã»ãç§»åãããé©åãªä½ç½®ãæ´æ°ãã¾ãã
function moveBoombox(direction) {
switch (direction) {
case "left":
if (transform.xAxis > leftBound) {
transform.xAxis -= 5;
panner.positionX.value -= 0.1;
}
break;
case "up":
if (transform.yAxis > topBound) {
transform.yAxis -= 5;
panner.positionY.value -= 0.3;
}
break;
case "right":
if (transform.xAxis < rightBound) {
transform.xAxis += 5;
panner.positionX.value += 0.1;
}
break;
case "down":
if (transform.yAxis < bottomBound) {
transform.yAxis += 5;
panner.positionY.value += 0.3;
}
break;
}
}
ç§»åãããå¤ãåããããªãã®ã§ãã
case 'back':
if (transform.zAxis > innerBound) {
transform.zAxis -= 0.01;
panner.positionZ.value += 40;
}
break;
case 'forward':
if (transform.zAxis < outerBound) {
transform.zAxis += 0.01;
panner.positionZ.value -= 40;
}
break;
ããããå転å¤ã¯ãé³ããå転ãç§»åãããå¿
è¦ããããããå°ãè¤éã§ãã 2ãã¤ã®è»¸ã®å¤ãæ´æ°ããå¿
è¦ãããã ãã§ãªãï¼ä¾ãã°ããªãã¸ã§ã¯ãã x 軸ã®å¨ãã«å転ãããå ´åããã®ãªãã¸ã§ã¯ãã® y 座æ¨ã¨ z 座æ¨ãæ´æ°ãã¾ãï¼ããã®ããã«ããã«ããã¤ãã®è¨ç®ãããå¿
è¦ãããã¾ãã å転ã¯åã§ããããã®åãæç»ããããã« Math.sin
㨠Math.cos
ãå¿
è¦ã§ãã
å転çãè¨å®ãã¾ããããããã¯å¾ã§ Math.sin
㨠Math.cos
ã§ä½¿ç¨ããã©ã¸ã¢ã³ç¯å²ã®å¤ã«å¤æãã¦ãã©ã¸ã«ã»ãå転ãããã¨ãã®æ°ãã座æ¨ãæ±ããã¨ãã«ä½¿ç¨ãã¾ãã
// åè»¢å®æ°ã®è¨å®
const rotationRate = 60; // æ°åã大ããã»ã©é³ã®å転ãé
ã
const q = Math.PI / rotationRate; // å転ã®å¢å ï¼ã©ã¸ã¢ã³åä½ï¼
ããã使ç¨ãã¦å転度ãè¨ç®ãããã¨ãã§ãã¾ãããã㯠CSS 座æ¨å¤æã使ããéã«å½¹ç«ã¡ã¾ãï¼CSS 座æ¨å¤æã«ã¯ x 軸㨠y 軸ã®ä¸¡æ¹ãå¿ è¦ã§ãããã¨ã«æ³¨æãã¦ãã ããï¼ã
// CSS ã®è§åº¦ãåå¾
const degreesX = (q * 180) / Math.PI;
const degreesY = (q * 180) / Math.PI;
ä¾ãã°ãå·¦å転ãè¦ã¦ã¿ã¾ããããå·¦å転ã®ããã«ããã³ãã¼ã®åº§æ¨ã® x æ¹å㨠z æ¹åãå¤ãã¦ã y 軸ã®å¨ããç§»åããå¿ è¦ãããã¾ãã
case 'rotate-left':
transform.rotateY -= degreesY;
// 'left' 㯠y 軸ãä¸å¿ã¨ããå転ã§ãè² ã®è§åº¦ãå ãã
z = panner.orientationZ.value*Math.cos(q) - panner.orientationX.value*Math.sin(q);
x = panner.orientationZ.value*Math.sin(q) + panner.orientationX.value*Math.cos(q);
y = panner.orientationY.value;
panner.orientationX.value = x;
panner.orientationY.value = y;
panner.orientationZ.value = z;
break;
ããã¯å°ããããã«ããã§ããã sin 㨠cos ã使ç¨ãã¦ãã©ã¸ã«ã»ã®å転ã«å¿ è¦ãªåº§æ¨ã®åéåã使¥ãã¦ããã®ã§ãã
ããã¯ãã¹ã¦ã®è»¸ã«å¯¾ãã¦ã§ãã¾ããæ´æ°ããæ£ããä½ç½®æå®ã¨ãæ£ã®å¤ãè² ã®å¤ããé¸ã¶ã ãã§ãã
case 'rotate-right':
transform.rotateY += degreesY;
// 'right' 㯠y 軸ãä¸å¿ã¨ããå転ã§ãæ£ã®è§åº¦ãå ãã
z = panner.orientationZ.value*Math.cos(-q) - panner.orientationX.value*Math.sin(-q);
x = panner.orientationZ.value*Math.sin(-q) + panner.orientationX.value*Math.cos(-q);
y = panner.orientationY.value;
panner.orientationX.value = x;
panner.orientationY.value = y;
panner.orientationZ.value = z;
break;
case 'rotate-up':
transform.rotateX += degreesX;
// 'up' 㯠x 軸ãä¸å¿ã¨ããå転ã§ãè² ã®è§åº¦ãå ãã
z = panner.orientationZ.value*Math.cos(-q) - panner.orientationY.value*Math.sin(-q);
y = panner.orientationZ.value*Math.sin(-q) + panner.orientationY.value*Math.cos(-q);
x = panner.orientationX.value;
panner.orientationX.value = x;
panner.orientationY.value = y;
panner.orientationZ.value = z;
break;
case 'rotate-down':
transform.rotateX -= degreesX;
// 'down' 㯠x 軸ãä¸å¿ã¨ããå転ã§ãæ£ã®è§åº¦ãå ãã
z = panner.orientationZ.value*Math.cos(q) - panner.orientationY.value*Math.sin(q);
y = panner.orientationZ.value*Math.sin(q) + panner.orientationY.value*Math.cos(q);
x = panner.orientationX.value;
panner.orientationX.value = x;
panner.orientationY.value = y;
panner.orientationZ.value = z;
break;
æå¾ã«ããã²ã¨ã¤ã CSS ãæ´æ°ãããã¦ã¹ã¤ãã³ãç¨ã«æå¾ã«ç§»åãããã¨ãã®åç
§ãä¿æããå¿
è¦ãããã¾ãã ãããæå¾ã® moveBoombox
颿°ã§ãã
function moveBoombox(direction, prevMove) {
switch (direction) {
case "left":
if (transform.xAxis > leftBound) {
transform.xAxis -= 5;
panner.positionX.value -= 0.1;
}
break;
case "up":
if (transform.yAxis > topBound) {
transform.yAxis -= 5;
panner.positionY.value -= 0.3;
}
break;
case "right":
if (transform.xAxis < rightBound) {
transform.xAxis += 5;
panner.positionX.value += 0.1;
}
break;
case "down":
if (transform.yAxis < bottomBound) {
transform.yAxis += 5;
panner.positionY.value += 0.3;
}
break;
case "back":
if (transform.zAxis > innerBound) {
transform.zAxis -= 0.01;
panner.positionZ.value += 40;
}
break;
case "forward":
if (transform.zAxis < outerBound) {
transform.zAxis += 0.01;
panner.positionZ.value -= 40;
}
break;
case "rotate-left":
transform.rotateY -= degreesY;
// 'left' 㯠y 軸ãä¸å¿ã¨ããå転ã§ãè² ã®è§åº¦ãå ãã
z =
panner.orientationZ.value * Math.cos(q) -
panner.orientationX.value * Math.sin(q);
x =
panner.orientationZ.value * Math.sin(q) +
panner.orientationX.value * Math.cos(q);
y = panner.orientationY.value;
panner.orientationX.value = x;
panner.orientationY.value = y;
panner.orientationZ.value = z;
break;
case "rotate-right":
transform.rotateY += degreesY;
// 'right' 㯠y 軸ãä¸å¿ã¨ããå転ã§ãæ£ã®è§åº¦ãå ãã
z =
panner.orientationZ.value * Math.cos(-q) -
panner.orientationX.value * Math.sin(-q);
x =
panner.orientationZ.value * Math.sin(-q) +
panner.orientationX.value * Math.cos(-q);
y = panner.orientationY.value;
panner.orientationX.value = x;
panner.orientationY.value = y;
panner.orientationZ.value = z;
break;
case "rotate-up":
transform.rotateX += degreesX;
// 'up' 㯠x 軸ãä¸å¿ã¨ããå転ã§ãè² ã®è§åº¦ãå ãã
z =
panner.orientationZ.value * Math.cos(-q) -
panner.orientationY.value * Math.sin(-q);
y =
panner.orientationZ.value * Math.sin(-q) +
panner.orientationY.value * Math.cos(-q);
x = panner.orientationX.value;
panner.orientationX.value = x;
panner.orientationY.value = y;
panner.orientationZ.value = z;
break;
case "rotate-down":
transform.rotateX -= degreesX;
// 'down' 㯠x 軸ãä¸å¿ã¨ããå転ã§ãæ£ã®è§åº¦ãå ãã
z =
panner.orientationZ.value * Math.cos(q) -
panner.orientationY.value * Math.sin(q);
y =
panner.orientationZ.value * Math.sin(q) +
panner.orientationY.value * Math.cos(q);
x = panner.orientationX.value;
panner.orientationX.value = x;
panner.orientationY.value = y;
panner.orientationZ.value = z;
break;
}
boombox.style.transform =
`translateX(${transform.xAxis}px) ` +
`translateY(${transform.yAxis}px) ` +
`scale(${transform.zAxis}) ` +
`rotateY(${transform.rotateY}deg) ` +
`rotateX(${transform.rotateX}deg)`;
const move = prevMove || {};
move.frameId = requestAnimationFrame(() => moveBoombox(direction, move));
return move;
}
ã³ã³ããã¼ã«ãæ¥ç¶
å¶å¾¡ãããã¿ã³ã®æ¥ç¶ã¯æ¯è¼çåç´ã§ããããã§ãã³ã³ããã¼ã«ã®ãã¦ã¹ã¤ãã³ããå¾ ã¡åãã¦ãã®é¢æ°ãå®è¡ãããã¦ã¹ãé¢ããã¨ãã«ãã®é¢æ°ã忢ããããã¨ãã§ãã¾ãã
// åã³ã³ããã¼ã«ã«ã¤ãã¦ãã©ã¸ã«ã»ãç§»åãããä½ç½®å¤ã夿´ãã¾ãã
moveControls.forEach((el) => {
let moving;
el.addEventListener(
"mousedown",
() => {
const direction = this.dataset.control;
if (moving && moving.frameId) {
cancelAnimationFrame(moving.frameId);
}
moving = moveBoombox(direction);
},
false,
);
window.addEventListener(
"mouseup",
() => {
if (moving && moving.frameId) {
cancelAnimationFrame(moving.frameId);
}
},
false,
);
});
ã°ã©ããæ¥ç¶
HTML ã«ã¯ããã³ãã¼ãã¼ããå½±é¿ãããã audio è¦ç´ ãæ ¼ç´ãã¾ãã
<audio src="myCoolTrack.mp3"></audio>
ãã®è¦ç´ ããã½ã¼ã¹ãåå¾ãã AudioContext.createMediaElementSource
ã使ç¨ãã¦ã¦ã§ããªã¼ãã£ãª API ã«ãã¤ãããå¿
è¦ãããã¾ãã
// audio è¦ç´ ãåå¾
const audioElement = document.querySelector("audio");
// é³å£°ã³ã³ããã¹ãã«æ¸¡ã
const track = audioContext.createMediaElementSource(audioElement);
次ã«ãé³å£°ã°ã©ããæ¥ç¶ãããªããã°ãªãã¾ãããå ¥åï¼ãã©ãã¯ï¼ã夿´ãã¼ãï¼ãã³ãã¼ï¼ãåºåå ï¼ãã®å ´åã¯ã¹ãã¼ã«ã¼ï¼ãæ¥ç¶ãã¾ãã
track.connect(panner).connect(audioCtx.destination);
ç¾å¨ã®ç¶æ ã«å¿ãã¦ãã¯ãªãã¯ããã¨é³å£°ãåçã¾ãã¯ä¸æåæ¢ããåçãã¿ã³ã使ãã¾ãããã
<button data-playing="false" role="switch">åç/忢</button>
// åçãã¿ã³ã鏿
const playButton = document.querySelector("button");
playButton.addEventListener(
"click",
() => {
// ã³ã³ããã¹ããåæ¢ç¶æ
ï¼autoplay ããªã·ã¼ã«ããï¼ãã©ããããã§ãã¯
if (audioContext.state === "suspended") {
audioContext.resume();
}
// ç¶æ
ã«ãã£ã¦ãã©ãã¯ãåçã¾ãã¯åæ¢ããã
if (playButton.dataset.playing === "false") {
audioElement.play();
playButton.dataset.playing = "true";
} else if (playButton.dataset.playing === "true") {
audioElement.pause();
playButton.dataset.playing = "false";
}
},
false,
);
é³å£°ãé³å£°ã°ã©ãã®åçã»å¶å¾¡ã«ã¤ãã¦ãã詳ããè¦ã¦ããã«ã¯ãã¦ã§ããªã¼ãã£ãª API ã®ä½¿ç¨ãã覧ãã ããã
ã¾ã¨ããã®è¨äºã§ãã¦ã§ããªã¼ãã£ãªã®ç©ºéè¨å®ãã©ã®ããã«åä½ããã®ããã¾ã PannerNode
ããããã£ãããããä½ãããã®ãï¼ããªãããã¤ãããã¾ãï¼ã«ã¤ãã¦ããçè§£ããã ãããã¨æãã¾ãã å¤ã®æä½ãé£ããå ´åãããã使ç¨ããç¨éã«ãã£ã¦ã¯æ£ããå¤ãåå¾ããã®ã«æéãããããã¨ãããã¾ãã
ã¡ã¢: ãã¾ãã¾ãªãã©ã¦ã¶ã¼éã§ãé³å£°ã®ç©ºéåã«ã¯è¥å¹²ã®éããããã¾ãã ããã¼ãã¼ãã¯ããã³ãããã®ä¸ã§ã¨ã¦ãè¤éãªè¨ç®ããã¾ãã ããã«ã¯ããããã®ãã¹ããããã®ã§ãç°ãªããã©ãããã©ã¼ã éã§ãã®ãã¼ãã®å é¨åä½ã®ç¶æ ã追跡ãããã¨ãã§ãã¾ãã
ã¾ããæçµçãªãã¢ã¯ããã§ç¢ºèªã§ããæçµçãªã½ã¼ã¹ã³ã¼ãã¯ããã§ç¢ºèªã§ãã¾ãã Codepen ã®ãã¢ãããã¾ãã
ããããªãã 3D ã²ã¼ã ã WebXR ã§ä½æ¥ãã¦ããã®ã§ããã°ããã®ãããªæ©è½ã使ããããã« 3D ã©ã¤ãã©ãªã¼ãå©ç¨ããã®ã¯ããèãã§ãã ç§ãã¡ã¯ããããã©ã®ããã«åä½ããã®ãã®èãæ¹ãæä¾ããããã«ããã®è¨äºã§èªåèªèº«ã§å±éãã¾ããããä»ã®äººãå ã«è¡ã£ãææãå©ç¨ãããã¨ã§å¤ãã®æéãç¯ç´ãããã¨ãã§ãã¾ãã
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4