Make it more like an app

This commit is contained in:
Neale Pickett 2020-03-06 18:01:14 -07:00
parent 69bf1c0de6
commit ab799618f1
3 changed files with 227 additions and 122 deletions

48
convulse.css Normal file
View File

@ -0,0 +1,48 @@
body {
background-color: #000;
color: #fff;
margin: 0;
font-family: sans-serif;
}
video {
height: 100vh;
width: 100vw;
}
canvas {
width: 100vw;
filter: grayscale(100%);
}
canvas.recording {
filter: initial;
}
.hidden {
display: none;
}
#download {
display: none;
}
#toasts {
position: fixed;
z-index: 2;
top: 0;
background-color: blue;
width: 100%;
opacity: 0.3;
text-align: center;
}
#controls {
position: fixed;
z-index: 2;
width: 100%;
background-color: ;
opacity: 0.8;
bottom: 0;
}
video {
display: none;
}
dt {
font-weight: bold;
}

View File

@ -2,130 +2,48 @@
<html>
<head>
<title>Convulse</title>
<style>
body {
background-color: #000;
color: #fff;
margin: 0;
font-family: sans-serif;
}
video {
height: 100vh;
width: 100vw;
}
#download {
display: none;
}
#toasts {
position: fixed;
z-index: 2;
top: 0;
background-color: blue;
width: 100%;
opacity: 0.3;
text-align: center;
}
video {
filter: grayscale(100%);
}
video.recording {
filter: initial;
}
</style>
<script>
function toast(text, timeout=8000) {
let toasts = document.querySelector("#toasts")
if (! text) {
while (toasts.firstChild) {
toasts.firstChild.remove()
}
} else {
let p = document.querySelector("#toasts").appendChild(document.createElement("p"))
p.textContent = text
if (timeout) {
setTimeout(() => p.remove(), timeout)
}
}
}
class Convulse {
constructor() {
this.video = document.querySelector("video")
this.dllink = document.querySelector("#download")
this.init()
this.chunks = []
}
download(event) {
let recording = window.URL.createObjectURL(new Blob(this.chunks, {type: this.recorder.mimeType}))
let now = new Date().toISOString()
this.dllink.addEventListener('progress', event => console.log(event))
this.dllink.href = recording
this.dllink.download = "convulse-" + now + ".webm"
this.dllink.click()
}
start() {
toast(null)
this.chunks = []
this.recorder.start(10)
this.video.classList.add("recording")
}
stop() {
toast("stopped and downloaded")
this.recorder.stop()
this.video.classList.remove("recording")
}
toggle() {
if (this.recorder.state == "recording") {
this.stop()
this.download()
} else {
this.start()
}
}
async init() {
document.querySelector("h1").remove()
this.webcam = await navigator.mediaDevices.getUserMedia({video: true})
this.video.srcObject = this.webcam
this.video.play()
this.video.addEventListener("click", event => this.toggle())
this.desktop = await navigator.mediaDevices.getDisplayMedia({video: {cursor: "always"}})
this.recorder = new MediaRecorder(this.desktop, {mimeType: "video/webm"})
this.recorder.addEventListener("dataavailable", event => {
if (event.data && event.data.size > 0) {
this.chunks.push(event.data)
}
})
toast("Click anywhere to start and stop recording")
}
}
function init() {
window.app = new Convulse()
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init)
} else {
init()
}
</script>
<link rel="stylesheet" href="convulse.css">
<script src="convulse.js"></script>
</head>
<body>
<h1>Convulse: it's sorta like Twitch!</h1>
<video>
It's too bad about your browser.
</video>
<div id="hello">
<h1>Convulse: it's sorta like Twitch!</h1>
<p>
I need the following permissions:
</p>
<dl>
<dt>Use your microphone</dt>
<dd>So I can record your velvety-smooth voice</dd>
<dt>Use your camera</dt>
<dd>So I can record your velvety-smooth face</dd>
<dt>Share your screen</dt>
<dd>So I can record your velvety-smooth computer</dd>
</dl>
</div>
<canvas></canvas>
<div id="videos" class="hidden">
<video id="webcam"></video>
<video id="desktop"></video>
</div>
<div id="controls">
<p>
UR FACE
<input id="webcam-size" type="range" min="0.05" max="1.0" step="0.05" value="0.2">
<button id="webcam-pos">Move</button>
</p>
<p>
Capture Area
<input id="capture-size" type="range" min="0.04" max="1.0" step="0.05" value="0.2">
<button id="range-pos">Move</button>
</p>
</div>
<div id="toasts"></div>
<a id="download"></a>
</body>
</html>

139
convulse.js Normal file
View File

@ -0,0 +1,139 @@
// jshint asi:true
function toast(text, timeout=8000) {
let toasts = document.querySelector("#toasts")
if (! text) {
while (toasts.firstChild) {
toasts.firstChild.remove()
}
} else {
let p = document.querySelector("#toasts").appendChild(document.createElement("p"))
p.textContent = text
if (timeout) {
setTimeout(() => p.remove(), timeout)
}
}
}
class Convulse {
constructor() {
this.canvas = document.querySelector("canvas")
this.ctx = this.canvas.getContext("2d")
this.dllink = document.querySelector("#download")
this.webcamVideo = document.querySelector("#webcam")
this.desktopVideo = document.querySelector("#desktop")
document.addEventListener("click", event => this.toggle())
this.chunks = []
document.addEventListener("mouseenter", e => this.showControls(true))
document.addEventListener("mouseleave", e => this.showControls(false))
this.init()
}
showControls(show) {
let controls = document.querySelector("#controls")
if (show) {
controls.classList.remove("hidden")
} else {
controls.classList.add("hidden")
}
}
download(event) {
let recording = window.URL.createObjectURL(new Blob(this.chunks, {type: this.recorder.mimeType}))
let now = new Date().toISOString()
this.dllink.addEventListener('progress', event => console.log(event))
this.dllink.href = recording
this.dllink.download = "convulse-" + now + ".webm"
this.dllink.click()
}
start() {
toast(null)
this.chunks = []
this.recorder.start(10)
this.canvas.classList.add("recording")
}
stop() {
toast("stopped and downloaded")
this.recorder.stop()
this.canvas.classList.remove("recording")
}
toggle() {
if (this.recorder.state == "recording") {
this.stop()
this.download()
} else {
this.start()
}
}
frame(timestamp) {
if (this.webcamVideo.videoWidth > 0) {
let webcamAR = this.webcamVideo.videoWidth / this.webcamVideo.videoHeight
let desktopAR = this.desktopVideo.videoWidth / this.desktopVideo.videoHeight
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.ctx.drawImage(this.desktopVideo, 0, 0, this.canvas.height * desktopAR, this.canvas.height)
this.ctx.drawImage(this.webcamVideo, 0, 0, webcamAR * 400, 400)
if (timestamp % 2000 < 1000) {
this.ctx.beginPath()
this.ctx.strokeStyle = "red"
this.ctx.lineWidth = "6"
this.ctx.rect(20, 20, 150, 100)
this.ctx.stroke()
}
}
requestAnimationFrame(ts => this.frame(ts))
}
async init() {
this.canvas.width = 1920
this.canvas.height = 1080
this.webcamVideo.srcObject = await navigator.mediaDevices.getUserMedia({video: true, audio: true})
this.webcamVideo.play()
this.desktopVideo.srcObject = await navigator.mediaDevices.getDisplayMedia({video: {cursor: "always"}})
this.desktopVideo.play()
document.querySelector("#hello").classList.add("hidden")
this.mediaStream = new MediaStream()
let canvasStream = this.canvas.captureStream(30)
for (let vt of canvasStream.getVideoTracks()) {
this.mediaStream.addTrack(vt)
console.log("Adding video track", vt)
}
for (let at of this.webcamVideo.srcObject.getAudioTracks()) {
this.mediaStream.addTrack(at)
console.log("Adding audio track", at)
}
this.recorder = new MediaRecorder(this.canvas.captureStream(30), {mimeType: "video/webm"})
this.recorder.addEventListener("dataavailable", event => {
if (event.data && event.data.size > 0) {
this.chunks.push(event.data)
}
})
this.frame()
toast("Click anywhere to start and stop recording")
}
}
function init() {
window.app = new Convulse()
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init)
} else {
init()
}