sound proximity
This commit is contained in:
parent
7523465c0b
commit
14a519b705
139
demo/music.html
139
demo/music.html
|
|
@ -62,41 +62,6 @@
|
||||||
</video>
|
</video>
|
||||||
</spatial-geometry>
|
</spatial-geometry>
|
||||||
|
|
||||||
<spatial-geometry x="415" y="295" width="166" height="200">
|
|
||||||
<video loop>
|
|
||||||
<source src="/dancing-flower.mov" type="video/quicktime" />
|
|
||||||
<source src="/dancing-flower.webm" type="video/webm" />
|
|
||||||
</video>
|
|
||||||
</spatial-geometry>
|
|
||||||
|
|
||||||
<spatial-geometry x="20" y="525" width="166" height="200">
|
|
||||||
<video loop>
|
|
||||||
<source src="/dancing-flower.mov" type="video/quicktime" />
|
|
||||||
<source src="/dancing-flower.webm" type="video/webm" />
|
|
||||||
</video>
|
|
||||||
</spatial-geometry>
|
|
||||||
|
|
||||||
<spatial-geometry x="160" y="545" width="166" height="200">
|
|
||||||
<video loop>
|
|
||||||
<source src="/dancing-flower.mov" type="video/quicktime" />
|
|
||||||
<source src="/dancing-flower.webm" type="video/webm" />
|
|
||||||
</video>
|
|
||||||
</spatial-geometry>
|
|
||||||
|
|
||||||
<spatial-geometry x="290" y="530" width="166" height="200">
|
|
||||||
<video loop>
|
|
||||||
<source src="/dancing-flower.mov" type="video/quicktime" />
|
|
||||||
<source src="/dancing-flower.webm" type="video/webm" />
|
|
||||||
</video>
|
|
||||||
</spatial-geometry>
|
|
||||||
|
|
||||||
<spatial-geometry x="420" y="520" width="166" height="200">
|
|
||||||
<video loop>
|
|
||||||
<source src="/dancing-flower.mov" type="video/quicktime" />
|
|
||||||
<source src="/dancing-flower.webm" type="video/webm" />
|
|
||||||
</video>
|
|
||||||
</spatial-geometry>
|
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { SpatialGeometry } from '../src/canvas/spatial-geometry.ts';
|
import { SpatialGeometry } from '../src/canvas/spatial-geometry.ts';
|
||||||
import { RecordPlayer } from '../src/music/record-player.ts';
|
import { RecordPlayer } from '../src/music/record-player.ts';
|
||||||
|
|
@ -104,28 +69,112 @@
|
||||||
SpatialGeometry.register();
|
SpatialGeometry.register();
|
||||||
RecordPlayer.register();
|
RecordPlayer.register();
|
||||||
|
|
||||||
const flowers = Array.from(document.querySelectorAll('video'));
|
let proximityDistance = 200;
|
||||||
const audio = document.querySelector('audio');
|
const proximitySet = new Set();
|
||||||
|
const recordPlayerGeometry = document.querySelector('spatial-geometry:has(record-player)');
|
||||||
|
const recordPlayer = recordPlayerGeometry.firstElementChild;
|
||||||
|
const flowers = document.querySelectorAll('spatial-geometry:has(video)');
|
||||||
|
// set playback rate when video is ready
|
||||||
function setPlayback(e) {
|
function setPlayback(e) {
|
||||||
e.target.playbackRate = (91 / 60) * e.target.duration;
|
e.target.playbackRate = (91 / 60) * e.target.duration;
|
||||||
e.target.removeEventListener('canplay', setPlayback);
|
e.target.removeEventListener('canplay', setPlayback);
|
||||||
}
|
}
|
||||||
|
|
||||||
flowers.forEach((el) => {
|
flowers.forEach((el) => {
|
||||||
el.addEventListener('canplay', setPlayback);
|
el.firstElementChild.addEventListener('canplay', setPlayback);
|
||||||
});
|
});
|
||||||
|
|
||||||
audio.addEventListener('play', () => {
|
function collisionDetection(rect1, rect2) {
|
||||||
setTimeout(() => flowers.forEach((el) => el.play()), 1200);
|
return (
|
||||||
|
rect1.left < rect2.right &&
|
||||||
|
rect1.right > rect2.left &&
|
||||||
|
rect1.top < rect2.bottom &&
|
||||||
|
rect1.bottom > rect2.top
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function proximityDetection(rect1, rect2, distance = 30) {
|
||||||
|
return collisionDetection(
|
||||||
|
DOMRectReadOnly.fromRect({
|
||||||
|
x: rect1.x - distance,
|
||||||
|
y: rect1.y - distance,
|
||||||
|
height: rect1.height + distance * 2,
|
||||||
|
width: rect1.width + distance * 2,
|
||||||
|
}),
|
||||||
|
rect2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFlowerProximity(flower) {
|
||||||
|
const alreadyIntersection = proximitySet.has(flower);
|
||||||
|
// TODO: refactor this hack once resizing and the vertices API are figured out
|
||||||
|
const isNowIntersecting = proximityDetection(
|
||||||
|
DOMRectReadOnly.fromRect({
|
||||||
|
x: recordPlayerGeometry.x,
|
||||||
|
y: recordPlayerGeometry.y,
|
||||||
|
height: recordPlayerGeometry.height,
|
||||||
|
width: recordPlayerGeometry.width,
|
||||||
|
}),
|
||||||
|
DOMRectReadOnly.fromRect({
|
||||||
|
x: flower.x,
|
||||||
|
y: flower.y,
|
||||||
|
height: flower.height,
|
||||||
|
width: flower.width,
|
||||||
|
}),
|
||||||
|
proximityDistance
|
||||||
|
);
|
||||||
|
|
||||||
|
const video = flower.firstElementChild;
|
||||||
|
if (isNowIntersecting && !alreadyIntersection) {
|
||||||
|
proximitySet.add(flower);
|
||||||
|
if (!recordPlayer.paused) {
|
||||||
|
video.play();
|
||||||
|
}
|
||||||
|
} else if (alreadyIntersection && !isNowIntersecting) {
|
||||||
|
proximitySet.delete(flower);
|
||||||
|
video.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRecordPlayerProximity() {
|
||||||
|
proximitySet.forEach((el) => el.firstElementChild.pause?.());
|
||||||
|
proximitySet.clear();
|
||||||
|
|
||||||
|
for (const flower of flowers) {
|
||||||
|
updateFlowerProximity(flower);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleProximity(e) {
|
||||||
|
if (e.target === recordPlayerGeometry) {
|
||||||
|
updateRecordPlayerProximity();
|
||||||
|
} else {
|
||||||
|
updateFlowerProximity(e.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('move', handleProximity);
|
||||||
|
document.addEventListener('resize', handleProximity);
|
||||||
|
|
||||||
|
document.addEventListener('playing', (e) => {
|
||||||
|
proximitySet.forEach((el) => el.firstElementChild.play?.());
|
||||||
});
|
});
|
||||||
|
|
||||||
audio.addEventListener('pause', () => {
|
document.addEventListener('stopped', (e) => {
|
||||||
flowers.forEach((el) => {
|
proximitySet.forEach((el) => {
|
||||||
el.pause();
|
el.firstElementChild.pause?.();
|
||||||
el.currentTime = 0;
|
el.firstElementChild.currentTime = 0;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function updateVolume() {
|
||||||
|
proximityDistance = recordPlayer.volume * 500;
|
||||||
|
updateRecordPlayerProximity();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVolume();
|
||||||
|
|
||||||
|
document.addEventListener('volume', updateVolume);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,10 @@ styles.replaceSync(`
|
||||||
top: 25px;
|
top: 25px;
|
||||||
right: 95px;
|
right: 95px;
|
||||||
transform-origin: top;
|
transform-origin: top;
|
||||||
|
|
||||||
|
--move-time: 3s;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
animation-timing-function: linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control {
|
.control {
|
||||||
|
|
@ -150,20 +154,17 @@ styles.replaceSync(`
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(:state(playing)) .tone-arm {
|
:host(:state(playing)) .tone-arm {
|
||||||
--move-time: 3s;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
animation-timing-function: linear;
|
|
||||||
animation:
|
animation:
|
||||||
position-arm var(--move-time),
|
ready-arm var(--move-time),
|
||||||
move-arm var(--duration) var(--move-time),
|
move-arm var(--duration) var(--move-time),
|
||||||
reset-arm var(--move-time) calc(var(--duration) + var(--move-time));
|
reset-arm var(--move-time) calc(var(--duration) + var(--move-time));
|
||||||
}
|
}
|
||||||
|
|
||||||
:host(:state(playing)) .record {
|
:host(:state(playing)) .record {
|
||||||
animation-play-state: running;
|
animation-play-state: running;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes position-arm {
|
@keyframes ready-arm {
|
||||||
20% {
|
20% {
|
||||||
transform: rotateX(20deg);
|
transform: rotateX(20deg);
|
||||||
}
|
}
|
||||||
|
|
@ -184,14 +185,18 @@ styles.replaceSync(`
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
rotate: 43deg;
|
rotate: 42deg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes reset-arm {
|
@keyframes reset-arm {
|
||||||
|
0% {
|
||||||
|
rotate: 42deg;
|
||||||
|
}
|
||||||
|
|
||||||
20% {
|
20% {
|
||||||
transform: rotateX(20deg);
|
transform: rotateX(20deg);
|
||||||
rotate: 43deg;
|
rotate: 42deg;
|
||||||
}
|
}
|
||||||
|
|
||||||
80% {
|
80% {
|
||||||
|
|
@ -225,6 +230,8 @@ export class RecordPlayer extends HTMLElement {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.addEventListener('click', this);
|
this.addEventListener('click', this);
|
||||||
|
|
||||||
|
this.#audio.volume = 0.5;
|
||||||
this.#audio.addEventListener('ended', this);
|
this.#audio.addEventListener('ended', this);
|
||||||
|
|
||||||
const shadow = this.attachShadow({ mode: 'open' });
|
const shadow = this.attachShadow({ mode: 'open' });
|
||||||
|
|
@ -239,7 +246,7 @@ export class RecordPlayer extends HTMLElement {
|
||||||
<div class="control"></div>
|
<div class="control"></div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn"></button>
|
<button class="btn"></button>
|
||||||
<input type="range" class="slider" min="0" max="1" step="0.05" value="0.75">
|
<input type="range" class="slider" min="0" max="1" step="0.05" value="0.5">
|
||||||
</div>
|
</div>
|
||||||
<slot></slot>`;
|
<slot></slot>`;
|
||||||
|
|
||||||
|
|
@ -251,6 +258,10 @@ export class RecordPlayer extends HTMLElement {
|
||||||
return this.#audio.paused;
|
return this.#audio.paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get volume() {
|
||||||
|
return this.#audio.volume;
|
||||||
|
}
|
||||||
|
|
||||||
#playTimeout: number = -1;
|
#playTimeout: number = -1;
|
||||||
|
|
||||||
play() {
|
play() {
|
||||||
|
|
@ -259,7 +270,10 @@ export class RecordPlayer extends HTMLElement {
|
||||||
this.#audio.volume = this.#volumeInput.valueAsNumber;
|
this.#audio.volume = this.#volumeInput.valueAsNumber;
|
||||||
this.style.setProperty('--duration', `${this.#audio.duration}s`);
|
this.style.setProperty('--duration', `${this.#audio.duration}s`);
|
||||||
this.#internals.states.add('playing');
|
this.#internals.states.add('playing');
|
||||||
this.#playTimeout = window.setTimeout(() => this.#audio.play(), 3000);
|
this.#playTimeout = window.setTimeout(() => {
|
||||||
|
this.#audio.play();
|
||||||
|
this.dispatchEvent(new Event('playing', { bubbles: true }));
|
||||||
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
|
|
@ -269,6 +283,7 @@ export class RecordPlayer extends HTMLElement {
|
||||||
this.#internals.states.delete('playing');
|
this.#internals.states.delete('playing');
|
||||||
this.#audio.pause();
|
this.#audio.pause();
|
||||||
this.#audio.currentTime = 0;
|
this.#audio.currentTime = 0;
|
||||||
|
this.dispatchEvent(new Event('stopped', { bubbles: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(event: Event) {
|
handleEvent(event: Event) {
|
||||||
|
|
@ -283,7 +298,9 @@ export class RecordPlayer extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'input': {
|
case 'input': {
|
||||||
|
event.stopPropagation();
|
||||||
this.#audio.volume = this.#volumeInput.valueAsNumber;
|
this.#audio.volume = this.#volumeInput.valueAsNumber;
|
||||||
|
this.dispatchEvent(new Event('volume', { bubbles: true }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue