const VolumeMap = { isOn: false, size: 320, positions: new Map(), toggle() { VolumeMap.isOn = !VolumeMap.isOn }, onremove() { VolumeMap.positions.clear() }, getDot(username) { if(VolumeMap.positions.has(username)) { return VolumeMap.positions.get(username) } return [VolumeMap.size / 2, VolumeMap.size / 2] }, moveNotify({layerX, layerY}) { wire({kind: 'volumeMapMove', value: [layerX, layerY]}) }, moveDot({detail: {source, value}}) { VolumeMap.positions.set(source, value) const [x0, y0] = VolumeMap.getDot(State.username); for(const video of document.querySelectorAll('video:not(.self')) { const [x1, y1] = VolumeMap.getDot(video.username) const dist1 = Math.sqrt(Math.pow(x1 - x0, 2) + Math.pow(y1 - y0, 2)) const dist2 = VolumeMap.size / Math.pow(dist1, 1.5) - 0.2 const dist3 = Math.max(Math.min(dist2, 1), 0) video.style.opacity = dist3 video.volume = dist3 video.muted = !dist3 // iPhone does not allow graded volume } }, view() { const style = { 'z-index': '999', 'background-color': 'rgba(65, 105, 225, 0.8)', 'font-family': 'monospace', 'position': 'fixed', 'height': VolumeMap.size + 'px', 'width': VolumeMap.size + 'px', } return m('svg', {onclick: VolumeMap.moveNotify, style}, m('style', 'text { pointer-events: none }'), State.online.map(username => { const [x, y] = VolumeMap.getDot(username) return m('text', {x, y, fill: 'white'}, `・${username}`) }) ) }, } addEventListener('volumeMapMove', VolumeMap.moveDot)