work in progress [might break]
This commit is contained in:
parent
d3b767b665
commit
688713d179
File diff suppressed because one or more lines are too long
|
@ -11,6 +11,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<script src="./../../../dist/xrfragment.aframe.js"></script>
|
||||
<script src="./../../../dist/xrfragment.extras.js"></script>
|
||||
|
||||
<a-scene xr-mode-ui="XRMode: xr" renderer="colorManagement: true; highRefreshRate:true" light="defaultLightsEnabled: false">
|
||||
<a-entity id="player" wasd-controls look-controls>
|
||||
|
@ -33,7 +34,7 @@
|
|||
XRFMENU.logo = './../../assets/logo.png'
|
||||
XRFMENU.morelabel = '⚡'
|
||||
// add your menubuttons:
|
||||
XRFMENU.buttons = XRFMENU.buttons.concat([`<a class="btn" aria-label="button" aria-description="about menu" onclick="XRFMENU.showAbout()">💁 about</a><br>`])
|
||||
XRFMENU.buttons.push(`<a class="btn" aria-label="button" aria-description="about menu" onclick="XRFMENU.showAbout()">💁 about</a><br>`)
|
||||
XRFMENU.showAbout = () => window.notify(`
|
||||
<h1>💁 Hi there!</h1>
|
||||
|
||||
|
|
9
make
9
make
|
@ -84,14 +84,18 @@ build(){
|
|||
cat dist/xrfragment.three.js \
|
||||
src/3rd/js/aframe/*.js \
|
||||
example/assets/js/qr.js > dist/xrfragment.aframe.js
|
||||
|
||||
# html extras like menu & meetings
|
||||
test -f dist/alpine.min.js || wget "https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" -O dist/alpine.min.js
|
||||
cat dist/alpine.min.js src/3rd/js/extra/*.js > dist/xrfragment.extras.js
|
||||
|
||||
# fat all-in-one standalone xrf release
|
||||
test -f /tmp/xrf-aframe.js || {
|
||||
wget "https://aframe.io/releases/1.5.0/aframe.min.js" -O /tmp/xrf-aframe.js
|
||||
wget "https://cdn.jsdelivr.net/npm/aframe-blink-controls/dist/aframe-blink-controls.min.js" -O /tmp/xrf-blink.js
|
||||
#for i in /tmp/xrf-*.js; do echo -e "\n" >> $i; done # add extra linebreak to prevent bundle issues
|
||||
}
|
||||
cat /tmp/xrf-*.js dist/xrfragment.aframe.js > dist/xrfragment.aframe.all.js
|
||||
|
||||
cat /tmp/xrf-*.js dist/xrfragment.aframe.js dist/xrfragment.extras.js > dist/xrfragment.aframe.all.js
|
||||
|
||||
# add license headers
|
||||
for file in dist/xrfragment.{aframe,module,three,three.module,aframe.all}.js; do
|
||||
|
@ -142,6 +146,7 @@ repos(){
|
|||
|
||||
# remove aframe reference
|
||||
sed -i 's|<script src="https:\/\/aframe.*||g' ../xrfragment-helloworld/index.html
|
||||
sed -i 's|<script src=".*extras.*||g' ../xrfragment-helloworld/index.html
|
||||
sed -i 's|<script src=".*blink-controls.*||g' ../xrfragment-helloworld/index.html
|
||||
sed -i 's|aframe\.js|aframe.all.js|g' ../xrfragment-helloworld/index.html
|
||||
}
|
||||
|
|
|
@ -1,38 +1,8 @@
|
|||
// this project uses #vanillajs #proxies #clean #noframework
|
||||
//
|
||||
// menu = $proxy({
|
||||
// attach: 'body',
|
||||
// html: (el) => `<ul><li>${el.items.join('</li><li>')}</li></lu>`,
|
||||
// items: [1,2],
|
||||
// on: (el,k,v) => {
|
||||
// switch(k){
|
||||
// default: return el.outerHTML = el.html(el)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// menu.items = menu.items.concat([3,4])
|
||||
// $('#foo')
|
||||
// $$('.someclass').map( (el) => el.classList.toggle() )
|
||||
//
|
||||
$proxy = (opts) => {
|
||||
let el = document.createElement('div')
|
||||
el.innerHTML = opts.html(opts)
|
||||
el.querySelector = el.querySelector.bind(el)
|
||||
el.appendChild = el.appendChild.bind(el)
|
||||
let parent = typeof opts.attach == 'string' ? document.querySelector(opts.attach) : opts.attach
|
||||
parent.appendChild( el )
|
||||
el.on = el.addEventListener.bind(el)
|
||||
for( let i in opts ) el[i] = opts[i]
|
||||
return new Proxy( el, {
|
||||
get: (el,k,receiver) => el[k] || '',
|
||||
set: (el,k,v) => { el[k] = v; el.on(el,k,v) }
|
||||
})
|
||||
}
|
||||
// the core project uses #vanillajs #proxies #clean #noframework
|
||||
$ = typeof $ != 'undefined' ? $ : (s) => document.querySelector(s) // respect jquery
|
||||
$$ = typeof $$ != 'undefined' ? $$ : (s) => [...document.querySelectorAll(s)] // zepto etc.
|
||||
|
||||
$el = (html) => {
|
||||
$el = (html,tag) => {
|
||||
let el = document.createElement('div')
|
||||
el.innerHTML = html
|
||||
return el.children[0]
|
||||
|
|
|
@ -22,7 +22,8 @@ window.AFRAME.registerComponent('xrf', {
|
|||
renderer: aScene.renderer,
|
||||
loaders: {
|
||||
gltf: THREE.GLTFLoader, // which 3D assets (exts) to check for XR fragments?
|
||||
glb: THREE.GLTFLoader
|
||||
glb: THREE.GLTFLoader,
|
||||
obj: THREE.OBJLoader
|
||||
}
|
||||
})
|
||||
if( !XRF.camera ) throw 'xrfragment: no camera detected, please declare <a-entity camera..> ABOVE entities with xrf-attributes'
|
||||
|
|
|
@ -1,391 +0,0 @@
|
|||
//
|
||||
//AFRAME.registerComponent('meeting', {
|
||||
// schema:{
|
||||
// id:{ required:true, type:'string'},
|
||||
// visitorname:{required:false,type:'string'},
|
||||
// parentRoom:{required:false,type:'string'},
|
||||
// link:{required:false,type:'string'},
|
||||
// },
|
||||
//
|
||||
// // reactive HTML elements
|
||||
// $: document.createElement('div'),
|
||||
// cameras: {},
|
||||
// audios: {},
|
||||
// messages: [],
|
||||
// placeholderInput: 'enter name here',
|
||||
// hideChat: false,
|
||||
// log: [],
|
||||
// selfStream: null,
|
||||
//
|
||||
// events:{
|
||||
// connect: function(e){
|
||||
// if( !this.data.visitorname ){
|
||||
// this.data.appendHTMLMessage("Please enter your name below",["info"])
|
||||
// }else{
|
||||
// if( this.data.parentRoom ) this.data.$chat.add(`leaving ${this.data.parentRoom}`,["info"]);
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
//
|
||||
// remove: function(){
|
||||
// this.el.emit("remove",{})
|
||||
// this.data.$.remove()
|
||||
// },
|
||||
//
|
||||
// update: function(){
|
||||
// setTimeout( () => {
|
||||
// this.remove()
|
||||
// this.initMeeting()
|
||||
// },100)
|
||||
// },
|
||||
//
|
||||
// init: function(){
|
||||
//
|
||||
// //// when teleport is clicked
|
||||
// //AFRAME.XRF.addEventListener('href', (opts) => {
|
||||
// // if( !opts.click ) return // ignore mouseovers etc
|
||||
// // let url = opts.xrf.string
|
||||
// // let isTeleport = url.match(/(:\/\/|pos=)/)
|
||||
// // if( isTeleport ){
|
||||
// // url = url[0] == '#' ? document.location.href.replace(/#.*/, opts.xrf.string ) : '?'+opts.xrf.string
|
||||
// // this.notifyTeleport( url )
|
||||
// // }
|
||||
// //})
|
||||
// this.initMeeting()
|
||||
//
|
||||
// },
|
||||
//
|
||||
// initMeeting: function(){
|
||||
// this.data.link = document.location.href
|
||||
// this.initHTML()
|
||||
//
|
||||
// // load plugins
|
||||
// if( window.meeting.trystero ) new window.meeting.trystero(this.el,this,this.data)
|
||||
//
|
||||
// this.getMedia()
|
||||
// this.emit("connect",{})
|
||||
// },
|
||||
//
|
||||
// getMedia: async function(){
|
||||
// this.data.selfStream = await navigator.mediaDevices.getUserMedia({
|
||||
// audio: true,
|
||||
// video: {
|
||||
// width: 320,
|
||||
// height: 240,
|
||||
// frameRate: {
|
||||
// ideal: 30,
|
||||
// min: 10
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// let meVideo = this.createVideoElement(this.data.selfStream)
|
||||
// meVideo.muted = true
|
||||
// },
|
||||
//
|
||||
// // central function to broadcast stuff to chat
|
||||
// send: function(opts){
|
||||
// opts = { sendLocal: true, sendNetwork:true, ...opts }
|
||||
// if( opts.sendNetwork ){
|
||||
// if( !this.sendChat ) return
|
||||
// this.sendChat({opts}) // send to network
|
||||
// }
|
||||
// if( opts.sendLocal ){
|
||||
// this.data.$chat.add( opts ) // send to HTML screen
|
||||
// }
|
||||
// },
|
||||
//
|
||||
// createVideoElement: function(stream,id){
|
||||
// let video = this.data.videos[peerId]
|
||||
// const videoContainer = document.getElementById('videos')
|
||||
// // if this peer hasn't sent a stream before, create a video element
|
||||
// if (!video) {
|
||||
// video = document.createElement('video')
|
||||
// video.autoplay = true
|
||||
//
|
||||
// // add video element to the DOM
|
||||
// videoContainer.appendChild(video)
|
||||
// }
|
||||
//
|
||||
// video.srcObject = stream
|
||||
// video.resize = (state) => {
|
||||
// if( video.resize.state == undefined ) video.resize.state = false
|
||||
// if( state == undefined ) state = (video.resize.state = !video.resize.state )
|
||||
// video.style.width = state ? '320px' : '80px'
|
||||
// video.style.height = state ? '200px' : '60px'
|
||||
// }
|
||||
// this.data.videos[ id || 'me' ] = video
|
||||
// return video
|
||||
// },
|
||||
//
|
||||
// initHTML: function(){
|
||||
//
|
||||
// // add css
|
||||
// this.data.$.innerHTML += `<style type="text/css">
|
||||
// #videos{
|
||||
// display:grid-auto-columns;
|
||||
// grid-column-gap:5px;
|
||||
// margin-bottom:15px;
|
||||
// position: fixed;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// bottom: 0;
|
||||
// right: 0;
|
||||
// margin: 15px;
|
||||
// z-index:1500;
|
||||
// }
|
||||
// #videos > video{
|
||||
// border-radius:7px;
|
||||
// display:inline-block;
|
||||
// background:black;
|
||||
// width:80px;
|
||||
// height:60px;
|
||||
// margin-right:5px;
|
||||
// margin-bottom:5px;
|
||||
// vertical-align:top;
|
||||
// pointer-events:all;
|
||||
// }
|
||||
// #videos > video:hover{
|
||||
// filter: brightness(1.8);
|
||||
// cursor:pointer;
|
||||
// }
|
||||
//
|
||||
// #chatbar,
|
||||
// button#showchat{
|
||||
// z-index: 1500;
|
||||
// position: fixed;
|
||||
// bottom: 20px;
|
||||
// left: 20px;
|
||||
// width: 48%;
|
||||
// background: white;
|
||||
// padding: 0px 0px 0px 15px;
|
||||
// border-radius: 30px;
|
||||
// max-width: 500px;
|
||||
// box-sizing: border-box;
|
||||
// box-shadow: 0px 0px 5px 5px #0002;
|
||||
// }
|
||||
// button#showchat{
|
||||
// z-index:1550;
|
||||
// color:white;
|
||||
// border:0;
|
||||
// display:none;
|
||||
// height: 44px;
|
||||
// background:#07F;
|
||||
// font-weight:bold;
|
||||
// }
|
||||
// #chatbar input{
|
||||
// border:none;
|
||||
// width:90%;
|
||||
// box-sizing:border-box;
|
||||
// }
|
||||
// #chat{
|
||||
// position: absolute;
|
||||
// top: 100px;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
// bottom: 88px;
|
||||
// padding: 15px;
|
||||
// pointer-events: none;
|
||||
// overflow-y:auto;
|
||||
// }
|
||||
// #chat .msg{
|
||||
// background: #fff;
|
||||
// display: inline-block;
|
||||
// padding: 6px 17px;
|
||||
// border-radius: 20px;
|
||||
// color: #000c;
|
||||
// margin-bottom: 10px;
|
||||
// line-height:23px;
|
||||
// pointer-events:visible;
|
||||
// border: 1px solid #ccc;
|
||||
// }
|
||||
// #chat .msg.info{
|
||||
// background: #333;
|
||||
// color: #FFF;
|
||||
// font-size: 14px;
|
||||
// padding: 0px 16px;
|
||||
// }
|
||||
// #chat .msg.info a,
|
||||
// #chat .msg.info a:visited{
|
||||
// color: #aaf;
|
||||
// text-decoration: none;
|
||||
// transition:0.3s;
|
||||
// }
|
||||
// #chat .msg.info a:hover,
|
||||
// #chat button:hover{
|
||||
// filter: brightness(1.8);
|
||||
// text-decoration: underline;
|
||||
// }
|
||||
// #chat button {
|
||||
// margin: 0px 15px 10px 0px;
|
||||
// background: #07F;
|
||||
// color: #FFF;
|
||||
// border-radius: 7px;
|
||||
// padding: 11px 15px;
|
||||
// border: 0;
|
||||
// font-weight: bold;
|
||||
// box-shadow: 0px 0px 5px 5px #0002;
|
||||
// pointer-events:all;
|
||||
// }
|
||||
// #chat,#chatbar,#chatbar *, #chat *{
|
||||
// font-family:monospace;
|
||||
// font-size:15px;
|
||||
// }
|
||||
// </style>
|
||||
// <div id="videos" style="pointer-events:none"></div>
|
||||
// <div id="chat" aria-live="assertive" aria-relevant></div>
|
||||
// <div id="chatfooter"></div>
|
||||
// `
|
||||
// document.body.appendChild(this.data.$)
|
||||
//
|
||||
// // reactive logic
|
||||
// this.data = new Proxy(this.data, {
|
||||
// html: {
|
||||
// chatbar: (data) => data.showChat
|
||||
// ? `<div id="chatbar">
|
||||
// <input id="chatline" type="text" placeholder="${data.placeholderInput}"></input>
|
||||
// </div>`
|
||||
// : `<button id="showchat" class="btn">show chat</button>`
|
||||
// },
|
||||
// get(data,k ){ return data[k] },
|
||||
// set(data,k,v){
|
||||
// data[k] = v
|
||||
// switch( k ){
|
||||
//
|
||||
// case 'cameras':{
|
||||
// data.$videos.innerHTML = ''
|
||||
// for( let k in data.cameras ) el.appendChild( data.cameras[k] ) )
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// case 'audios':{
|
||||
// data.$videos.innerHTML = ''
|
||||
// for( let k in data.cameras ) el.appendChild( data.cameras[k] ) )
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// case 'showChat':{
|
||||
// data.$chatfooter.innerHTML = this.html.chatbar(data)
|
||||
// data.$chatline.addEventListener("keydown", this.onChatInput )
|
||||
// if( v ){
|
||||
// data.$chatfooter.querySelector('#chatline').focus()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// },
|
||||
// })
|
||||
//
|
||||
// // trigger 1st render
|
||||
// this.data.showChat = true
|
||||
//
|
||||
// // setup handles
|
||||
// this.data.$videos = this.data.$.querySelector('#meeting #videos')
|
||||
// this.data.$chatfooter = this.data.$.querySelector('#meeting #chatfooter')
|
||||
// this.data.$chatline = this.data.$chatfooter.querySelector('#chatline')
|
||||
// this.data.$chat = this.data.$.querySelector('#meeting #chat')
|
||||
// this.data.$chat.add = this.addMessage
|
||||
//
|
||||
// },
|
||||
//
|
||||
// addMessage: function(opts){
|
||||
// let {str,classes,buttons) = opts
|
||||
// let el = this.data.$chat
|
||||
//
|
||||
// const createMsg = (str) => {
|
||||
// let el = document.createElement("div")
|
||||
// el.className = "msg"
|
||||
// el.innerHTML = this.linkify(str)
|
||||
// return el
|
||||
// }
|
||||
//
|
||||
// if( str ){
|
||||
// str = str.replace('\n', "<br>")
|
||||
// .replace(/^[a-zA-Z-0-9]+?[:\.]/,'<b>$&</b>')
|
||||
// let msg = this.createMsg(str)
|
||||
// if( classes ) classes.map( (c) => msg.classList.add(c) )
|
||||
// el.appendChild(msg) // send to screen
|
||||
// el.innerHTML += '<br>'
|
||||
// if( !classes ) this.data.log.push(str)
|
||||
// }
|
||||
// if( buttons ){
|
||||
// for( let i in buttons ){
|
||||
// let btn = document.createElement("button")
|
||||
// btn.innerText = i
|
||||
// btn.addEventListener('click', () => buttons[i]() )
|
||||
// el.appendChild(btn)
|
||||
// }
|
||||
// el.innerHTML += '<br>'
|
||||
// }
|
||||
// el.scrollTop = el.scrollHeight; // scroll to bottom
|
||||
// }
|
||||
// },
|
||||
//
|
||||
// onChatInput: function(e){
|
||||
// if( e.key !== "Enter" ) return
|
||||
// let $chatline = this.data.$chatline
|
||||
// if( !this.data.visitorname ){
|
||||
// this.data.visitorname = chatline.value.toLowerCase()
|
||||
// this.data.visitorname = this.data.visitorname.replace(/[^a-z]+/g,'-')
|
||||
// this.send({message:"note: camera/mic access is totally optional ♥️", classes:["info"], isLocal:false})
|
||||
// $chatline.setAttribute("placeholder","chat here")
|
||||
// }else{
|
||||
// let str = `${this.data.visitorname}: ${$chatline.value.substr(0,65515).trim()}`
|
||||
// this.send({message:str})
|
||||
// }
|
||||
// $chatline.value = ''
|
||||
// event.preventDefault();
|
||||
// event.target.blur()
|
||||
// },
|
||||
//
|
||||
// enableSmallScreen: function(){
|
||||
// // on small screens/mobile make chat toggle-able
|
||||
// if( window.outerWidth < 1024 ){
|
||||
// let show = (state) => () => this.data.showChat = state
|
||||
// $('.a-canvas').addEventListener('click', () => show(false) )
|
||||
// $('.a-canvas').addEventListener('touchstart', () => show(false) )
|
||||
// $('#showchat').addEventListener('touchstart', () => show(true) )
|
||||
// $('#showchat').addEventListener('click', () => show(true) )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //notifyTeleport: function(url){
|
||||
// // url = url || this.roomname
|
||||
// // url = url.replace(/(#|&)meet/,'')
|
||||
// // let message = `${this.data.visitorname} teleported to ${url}`
|
||||
// // this.send({
|
||||
// // message,
|
||||
// // classes: ["info"]
|
||||
// // })
|
||||
// //},
|
||||
//
|
||||
// //linkify: function(t){
|
||||
// // const m = t.match(/(?<=\s|^)[a-zA-Z0-9-:/]+[?\.][a-zA-Z0-9-].+?(?=[.,;:?!-]?(?:\s|$))/g)
|
||||
// // if (!m) return t
|
||||
// // const a = []
|
||||
// // m.forEach(x => {
|
||||
// // const [t1, ...t2] = t.split(x)
|
||||
// // a.push(t1)
|
||||
// // t = t2.join(x)
|
||||
// // let url = (!(x.match(/:\/\//)) ? 'https://' : '') + x
|
||||
// // let attr = `href="${url}" target="_blank"`
|
||||
// // if (isNaN(x) ){
|
||||
// // let url_human = url.split('/')[2]
|
||||
// // let isXRFragment = url.match(/\.(glb|gltf|obj|usdz|fbx|col)/)
|
||||
// // if( isXRFragment ){ // detect xr fragments
|
||||
// // isMeeting = url.match(/(#|&)meet/)
|
||||
// // url_human = url.replace(/.*[?\?]/,'') // shorten xr fragment links
|
||||
// // .replace(/[?\&]meet/,'')
|
||||
// // let onclick = [ `xrf.navigator.to('${url}')` ]
|
||||
// // if( isMeeting ) onclick.push(`$('[meeting]').coms['meeting'].update()`)
|
||||
// // attr = `onclick="${onclick.join(';')}"`
|
||||
// // }
|
||||
// // a.push(`<a ${attr} style="pointer-events:all">${url_human}</a>`)
|
||||
// // }else
|
||||
// // a.push(x)
|
||||
// // })
|
||||
// // a.push(t)
|
||||
// // return a.join('')
|
||||
// //}
|
||||
//
|
||||
//})
|
|
@ -1,49 +0,0 @@
|
|||
AFRAME.registerComponent('xrf-menu', {
|
||||
schema:{
|
||||
id:{ required:true, type:'string'}
|
||||
},
|
||||
init: function(){
|
||||
// add css+html
|
||||
window.XRFMENU.init()
|
||||
|
||||
$('a-scene').addEventListener('XRF', this.onXRFready )
|
||||
|
||||
if( document.location.search.length > 2 ){
|
||||
$('[xrf]').setAttribute('xrf', document.location.search.substr(1)+document.location.hash )
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
onXRFready: function(){
|
||||
|
||||
let XRF = window.AFRAME.XRF
|
||||
return
|
||||
XRFMENU.setupMenu( XRF )
|
||||
|
||||
// on localhost enable debugging mode for developer convenience
|
||||
let loc = document.location
|
||||
if( loc.host.match(/^localhost/) ){
|
||||
$('a-scene').setAttribute('stats')
|
||||
XRF.debug = 1
|
||||
}
|
||||
|
||||
// add screenshot component with camera to capture bigger size equirects
|
||||
// document.querySelector('a-scene').components.screenshot.capture('perspective')
|
||||
$('a-scene').setAttribute("screenshot",{camera: "[camera]",width: 4096*2, height:2048*2})
|
||||
|
||||
if( window.outerWidth > 800 )
|
||||
setTimeout( () => window.notify("use WASD-keys and mouse-drag to move around",{timeout:false}),2000 )
|
||||
|
||||
window.AFRAME.XRF.addEventListener('href', (data) => data.selected ? window.notify(`href: ${data.xrf.string}`) : false )
|
||||
|
||||
// enable user-uploaded asset files
|
||||
let fileLoaders = XRFMENU.loadFile({
|
||||
".gltf": (file) => file.arrayBuffer().then( (data) => xrf.navigator.to(file.name,null, (new xrf.loaders.gltf()), data) ),
|
||||
".glb": (file) => file.arrayBuffer().then( (data) => xrf.navigator.to(file.name,null, (new xrf.loaders.gltf()), data) )
|
||||
})
|
||||
$("#overlay > input[type=submit]").addEventListener("click", fileLoaders )
|
||||
|
||||
}
|
||||
|
||||
|
||||
});
|
|
@ -0,0 +1,159 @@
|
|||
// alpine component for displaying meetings
|
||||
|
||||
$XRFMEETING = $el(
|
||||
`<div x-show="enabled" id="meeting" x-data="XRFMEETING">
|
||||
<style type="text/css">
|
||||
#videos{
|
||||
display:grid-auto-columns;
|
||||
grid-column-gap:5px;
|
||||
margin-bottom:15px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: 15px;
|
||||
z-index:1500;
|
||||
}
|
||||
#videos > video{
|
||||
border-radius:7px;
|
||||
display:inline-block;
|
||||
background:black;
|
||||
width:80px;
|
||||
height:60px;
|
||||
margin-right:5px;
|
||||
margin-bottom:5px;
|
||||
vertical-align:top;
|
||||
pointer-events:all;
|
||||
}
|
||||
#videos > video:hover{
|
||||
filter: brightness(1.8);
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
#chatbar,
|
||||
button#showchat{
|
||||
z-index: 1500;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
width: 48%;
|
||||
background: white;
|
||||
padding: 0px 0px 0px 15px;
|
||||
border-radius: 30px;
|
||||
max-width: 500px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 0px 5px 5px #0002;
|
||||
}
|
||||
button#showchat{
|
||||
z-index:1550;
|
||||
color:white;
|
||||
border:0;
|
||||
display:none;
|
||||
height: 44px;
|
||||
background:#07F;
|
||||
font-weight:bold;
|
||||
}
|
||||
#chatbar input{
|
||||
border:none;
|
||||
width:90%;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
#chat{
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 88px;
|
||||
padding: 15px;
|
||||
pointer-events: none;
|
||||
overflow-y:auto;
|
||||
}
|
||||
#chat .msg{
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
padding: 6px 17px;
|
||||
border-radius: 20px;
|
||||
color: #000c;
|
||||
margin-bottom: 10px;
|
||||
line-height:23px;
|
||||
pointer-events:visible;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
#chat .msg.info{
|
||||
background: #333;
|
||||
color: #FFF;
|
||||
font-size: 14px;
|
||||
padding: 0px 16px;
|
||||
}
|
||||
#chat .msg.info a,
|
||||
#chat .msg.info a:visited{
|
||||
color: #aaf;
|
||||
text-decoration: none;
|
||||
transition:0.3s;
|
||||
}
|
||||
#chat .msg.info a:hover,
|
||||
#chat button:hover{
|
||||
filter: brightness(1.8);
|
||||
text-decoration: underline;
|
||||
}
|
||||
#chat button {
|
||||
margin: 0px 15px 10px 0px;
|
||||
background: #07F;
|
||||
color: #FFF;
|
||||
border-radius: 7px;
|
||||
padding: 11px 15px;
|
||||
border: 0;
|
||||
font-weight: bold;
|
||||
box-shadow: 0px 0px 5px 5px #0002;
|
||||
pointer-events:all;
|
||||
}
|
||||
#chat,#chatbar,#chatbar *, #chat *{
|
||||
font-family:monospace;
|
||||
font-size:15px;
|
||||
}
|
||||
</style>
|
||||
<div id="videos" style="pointer-events:none"></div>
|
||||
<div id="chat" aria-live="assertive" aria-relevant></div>
|
||||
<div id="chatfooter">
|
||||
<div id="chatbar">
|
||||
<input id="chatline" type="text" placeholder=""></input>
|
||||
</div>
|
||||
<button id="showchat" class="btn">show chat</button>
|
||||
</div>
|
||||
</div>`
|
||||
)
|
||||
|
||||
window.XRFMEETING = {
|
||||
|
||||
scene: null,
|
||||
enabled: false,
|
||||
active: false,
|
||||
init(){
|
||||
window.XRFMEETING = this.$data // replace so we can modify state in global scope
|
||||
window.meeting
|
||||
},
|
||||
toggle(){
|
||||
this.enabled = !this.enabled
|
||||
if( !this.active ) this.start()
|
||||
},
|
||||
|
||||
install(opts){
|
||||
this.scene = opts.scene
|
||||
window.XRFMENU.buttons.push(`<a class="btn" aria-label="button" aria-description="start text/audio/video chat" id="meeting" onclick="XRFMEETING.toggle()" target="_blank">🧑🤝🧑 meeting</a><br>`)
|
||||
document.body.appendChild( $XRFMEETING )
|
||||
|
||||
},
|
||||
|
||||
start(){
|
||||
this.scene.addEventListener('meeting.peer.add', () => console.log("$meeting.peer.add") )
|
||||
this.scene.addEventListener('meeting.peer.remove', () => console.log("$meeting.peer.remove") )
|
||||
}
|
||||
}
|
||||
|
||||
// finally we alpinify
|
||||
document.addEventListener('XRFMENU:ready', (opts) => {
|
||||
opts = opts.detail
|
||||
Alpine.data('XRFMEETING', () => window.XRFMEETING)
|
||||
XRFMEETING.install(opts)
|
||||
})
|
|
@ -1,7 +1,7 @@
|
|||
// alpine component for displaying the menu
|
||||
|
||||
$XRFMENU = $el(
|
||||
`<div id="menu" x-bind="XRFMENU">
|
||||
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" type="text/javascript"></script>
|
||||
`<div id="menu" x-data="XRFMENU">
|
||||
<style type="text/css">
|
||||
:root {
|
||||
--xrf-primary: #6839dc;
|
||||
|
@ -150,6 +150,7 @@ $XRFMENU = $el(
|
|||
|
||||
|
||||
.menu .btn{
|
||||
display:inline-block;
|
||||
background: var(--xrf-primary);
|
||||
border-radius: 25px;
|
||||
border: 0;
|
||||
|
@ -163,7 +164,6 @@ $XRFMENU = $el(
|
|||
cursor:pointer;
|
||||
min-width:107px;
|
||||
text-decoration:none;
|
||||
display:none;
|
||||
margin-top: 15px;
|
||||
line-height:36px;
|
||||
margin-right:10px;
|
||||
|
@ -369,8 +369,8 @@ $XRFMENU = $el(
|
|||
}
|
||||
|
||||
</style>
|
||||
<div id="overlay" class="xrf" style="display:none">
|
||||
<div class="logo" :style="'background-image: url('+$store.XRFMENU.logo+')'"></div>
|
||||
<div id="overlay" class="xrf" x-show="enabled">
|
||||
<div class="logo" :style="'background-image: url('+logo+')'"></div>
|
||||
<button id="navback" onclick="history.back()"><</button>
|
||||
<button id="navforward" onclick="history.forward()">></button>
|
||||
<input type="submit" value="load 3D file"></input>
|
||||
|
@ -378,36 +378,34 @@ $XRFMENU = $el(
|
|||
</div>
|
||||
<div class="xrf footer">
|
||||
<div id="buttons" class="menu">
|
||||
<a class="btn" id="more" style="display:inline-block" x-text="$store.XRFMENU.morelabel"></a>
|
||||
<template x-for="btn in buttons.reverse()">
|
||||
<div x-show="enabled" x-html="btn"></div>
|
||||
</template>
|
||||
<a class="btn" id="more" @click="toggle" x-text="morelabel"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
)
|
||||
//${window.XRFMENU.html.map( (html) => typeof html == "function" ? html() : html ).join('\n')}
|
||||
|
||||
window.XRFMENU = {
|
||||
|
||||
$: $XRFMENU,
|
||||
|
||||
morelabel: '⚡',
|
||||
enabled: false,
|
||||
logo: './../../assets/logo.png',
|
||||
buttons: [
|
||||
`<a class="btn" aria-label="button" aria-description="start text/audio/video chat" id="meeting" target="_blank">🧑🤝🧑 meeting</a><br>`,
|
||||
`<a class="btn" aria-label="button" aria-description="share URL/screenshot/embed" id="share" target="_blank" onclick="window.share()">🔗 share</a><br>`
|
||||
],
|
||||
buttons: [`<a class="btn" aria-label="button" aria-description="share URL/screenshot/embed" id="share" onclick="window.share()">🔗 share</a><br>`],
|
||||
init(){ window.XRFMENU = this.$data }, // replace so we can modify state in global scope
|
||||
toggle(){ this.enabled = !this.enabled },
|
||||
|
||||
init: () => {
|
||||
install(opts){
|
||||
|
||||
// bind object as alpine app
|
||||
document.body.appendChild( XRFMENU.$ )
|
||||
document.addEventListener('alpine:init', () => Alpine.bind('XRFMENU', () => XRFMENU ) )
|
||||
document.body.appendChild( $XRFMENU )
|
||||
document.dispatchEvent( new CustomEvent("XRFMENU:ready", {detail: opts}) )
|
||||
|
||||
//if( XRFMENU.logo ) $('.logo').style['background-image'] = `url(${XRFMENU.logo})`
|
||||
window.notify = XRFMENU.notify(window)
|
||||
window.share = XRFMENU.share
|
||||
window.download = XRFMENU.download
|
||||
window.notify('loading '+document.location.search.substr(1))
|
||||
|
||||
// reroute console messages to snackbar notifications
|
||||
console.log = ( (log) => function(str){
|
||||
if( String(str).match(/(:.*#|note:)/) ) window.notify(str)
|
||||
|
@ -441,48 +439,6 @@ window.XRFMENU = {
|
|||
}
|
||||
},
|
||||
|
||||
setupMenu(XRF){
|
||||
let aScene = document.querySelector('a-scene')
|
||||
let urlbar = $('input#uri')
|
||||
let inIframe = window.location !== window.parent.location
|
||||
let els = [ ...document.querySelectorAll('.menu .btn') ]
|
||||
els = els.filter( (el) => el.id != "more" ? el : false )
|
||||
|
||||
let showMenu = (state) => {
|
||||
els.map( (el) => el.style.display = state ? 'inline-block' : 'none' )
|
||||
$('a#more').style.display = state ? 'none' : 'inline-block'
|
||||
$('#overlay').style.display = state ? 'inline-block' : 'none'
|
||||
if( inIframe ) $('#uri').style.display = 'block'
|
||||
}
|
||||
|
||||
els.map( (el) => el.addEventListener('click', () => showMenu(false) ) )
|
||||
$('a#more').addEventListener('click', () => showMenu(true) )
|
||||
$('.a-canvas').addEventListener('click', () => showMenu(false) )
|
||||
|
||||
// enable meetings
|
||||
let startMeeting = () => {
|
||||
aScene.setAttribute('meeting', 'id: xrfragments')
|
||||
$('a#meeting').innerText = '🧑🤝🧑 breakout meeting'
|
||||
$('a#meeting').setAttribute('aria-description','breakout room')
|
||||
}
|
||||
$('a#meeting').addEventListener('click', () => {
|
||||
if( aScene.getAttribute('meeting') ){ // meeting already, start breakout room
|
||||
let parentRoom = document.location.href
|
||||
XRFMENU.updateHashPosition(true)
|
||||
let meeting = $('[meeting]').components['meeting']
|
||||
meeting.data.parentRoom = parentRoom
|
||||
meeting.update()
|
||||
}else startMeeting()
|
||||
})
|
||||
if( document.location.hash.match(/(#|&)meet/) ) startMeeting()
|
||||
|
||||
XRF.addEventListener('hash', () => reflectUrl() )
|
||||
const reflectUrl = window.reflectUrl = (url) => {
|
||||
urlbar.value = url || document.location.search.substr(1) + document.location.hash
|
||||
}
|
||||
reflectUrl()
|
||||
},
|
||||
|
||||
SnackBar(userOptions) {
|
||||
var snackbar = this || (window.snackbar = {});
|
||||
var _Interval;
|
||||
|
@ -709,3 +665,69 @@ window.XRFMENU = {
|
|||
}
|
||||
}
|
||||
|
||||
//$('a-scene').addEventListener('XRF', this.onXRFready )
|
||||
//
|
||||
// if( document.location.search.length > 2 ){
|
||||
// $('[xrf]').setAttribute('xrf', document.location.search.substr(1)+document.location.hash )
|
||||
// }
|
||||
//
|
||||
// },
|
||||
//
|
||||
// onXRFready: function(){
|
||||
//
|
||||
// let XRF = window.AFRAME.XRF
|
||||
// //setupMenu(XRF){
|
||||
// // let aScene = document.querySelector('a-scene')
|
||||
// // let urlbar = $('input#uri')
|
||||
// // let inIframe = window.location !== window.parent.location
|
||||
// // let els = [ ...document.querySelectorAll('.menu .btn') ]
|
||||
// // els = els.filter( (el) => el.id != "more" ? el : false )
|
||||
//
|
||||
// // // enable meetings
|
||||
// // let startMeeting = () => {
|
||||
// // aScene.setAttribute('meeting', 'id: xrfragments')
|
||||
// // $('a#meeting').innerText = '🧑🤝🧑 breakout meeting'
|
||||
// // $('a#meeting').setAttribute('aria-description','breakout room')
|
||||
// // }
|
||||
// // $('a#meeting').addEventListener('click', () => {
|
||||
// // if( aScene.getAttribute('meeting') ){ // meeting already, start breakout room
|
||||
// // let parentRoom = document.location.href
|
||||
// // XRFMENU.updateHashPosition(true)
|
||||
// // let meeting = $('[meeting]').components['meeting']
|
||||
// // meeting.data.parentRoom = parentRoom
|
||||
// // meeting.update()
|
||||
// // }else startMeeting()
|
||||
// // })
|
||||
// // if( document.location.hash.match(/(#|&)meet/) ) startMeeting()
|
||||
//
|
||||
// // XRF.addEventListener('hash', () => reflectUrl() )
|
||||
// // const reflectUrl = window.reflectUrl = (url) => {
|
||||
// // urlbar.value = url || document.location.search.substr(1) + document.location.hash
|
||||
// // }
|
||||
// // reflectUrl()
|
||||
// //},
|
||||
//
|
||||
//
|
||||
// // on localhost enable debugging mode for developer convenience
|
||||
// let loc = document.location
|
||||
// if( loc.host.match(/^localhost/) ){
|
||||
// $('a-scene').setAttribute('stats')
|
||||
// XRF.debug = 1
|
||||
// }
|
||||
//
|
||||
// // add screenshot component with camera to capture bigger size equirects
|
||||
// // document.querySelector('a-scene').components.screenshot.capture('perspective')
|
||||
// $('a-scene').setAttribute("screenshot",{camera: "[camera]",width: 4096*2, height:2048*2})
|
||||
//
|
||||
// if( window.outerWidth > 800 )
|
||||
// setTimeout( () => window.notify("use WASD-keys and mouse-drag to move around",{timeout:false}),2000 )
|
||||
//
|
||||
// window.AFRAME.XRF.addEventListener('href', (data) => data.selected ? window.notify(`href: ${data.xrf.string}`) : false )
|
||||
//
|
||||
// // enable user-uploaded asset files
|
||||
// let fileLoaders = XRFMENU.loadFile({
|
||||
// ".gltf": (file) => file.arrayBuffer().then( (data) => xrf.navigator.to(file.name,null, (new xrf.loaders.gltf()), data) ),
|
||||
// ".glb": (file) => file.arrayBuffer().then( (data) => xrf.navigator.to(file.name,null, (new xrf.loaders.gltf()), data) )
|
||||
// })
|
||||
// $("#overlay > input[type=submit]").addEventListener("click", fileLoaders )
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// this orchestrates multiplayer events from the scene graph
|
||||
|
||||
window.meeting = (THREE, scene) => ({
|
||||
peer:{
|
||||
peers: [],
|
||||
add(peer){
|
||||
let defaults = {lastUpdated: new Date().getTime() }
|
||||
peer = { ...defaults,...peer}
|
||||
this.peers.push(peer)
|
||||
scene.dispatchEvent({type:'meeting.peer.add', peer})
|
||||
},
|
||||
remove(peer){
|
||||
scene.dispatchEvent({type:'meeting.peer.remove', peer})
|
||||
},
|
||||
send(opts){
|
||||
|
||||
},
|
||||
receive(opts){
|
||||
|
||||
}
|
||||
}
|
||||
})
|
|
@ -14,6 +14,10 @@ xrf.init = ((init) => function(opts){
|
|||
xrf.navigator.init()
|
||||
// return xrfragment lib as 'xrf' query functor (like jquery)
|
||||
for ( let i in xrf ) xrf.query[i] = xrf[i]
|
||||
|
||||
// install menu+extras if available
|
||||
if( typeof XRFMENU != 'undefined' ) XRFMENU.install(xrf)
|
||||
|
||||
return xrf.query
|
||||
})(xrf.init)
|
||||
|
||||
|
|
Loading…
Reference in New Issue