diff --git a/heartbeat.webm b/heartbeat.webm new file mode 100644 index 0000000..798aa98 Binary files /dev/null and b/heartbeat.webm differ diff --git a/index.template.html b/index.template.html index 3b7a443..2e8aa3a 100644 --- a/index.template.html +++ b/index.template.html @@ -3,9 +3,20 @@ + MEDTRACE: Case - Frontend Developer - + + + + +
+ \ No newline at end of file diff --git a/src/ECGTicker/ECGTicker.ts b/src/ECGTicker/ECGTicker.ts index a299f91..59e87b4 100644 --- a/src/ECGTicker/ECGTicker.ts +++ b/src/ECGTicker/ECGTicker.ts @@ -1,22 +1,26 @@ -const template = document.getElementById("my-card-template") as +import * as THREE from "three"; +import {OrbitControls} from "three/addons/controls/OrbitControls.js"; + + +const template = document.getElementById("ecg-ticker-template") as HTMLTemplateElement; enum Attributes { - MESSAGE = "message", + HEART_RATE = "heart-rate", } export default class ECGTicker extends HTMLElement { - static observedAttributes = [Attributes.MESSAGE]; + static observedAttributes = [Attributes.HEART_RATE]; private _content: DocumentFragment; - $message: HTMLElement; + $canvasContainer: HTMLElement; constructor() { super(); this.attachShadow({mode: "open"}); this._content = template.content.cloneNode(true) as DocumentFragment; - this.$message = this._content.getElementById("message")!; + this.$canvasContainer = this._content.getElementById("canvas-container")!; } connectedCallback() { @@ -25,18 +29,73 @@ export default class ECGTicker extends HTMLElement { } else { console.warn("No shadowRoot detected."); } + + this.initECGAnimation(); + } + + initECGAnimation() { + const renderer = new THREE.WebGLRenderer(); + + renderer.setSize(200, 300); + this.$canvasContainer.appendChild(renderer.domElement); + + const scene = new THREE.Scene(); + + const axesHelper = new THREE.AxesHelper(5); + scene.add(axesHelper); + + const camera = new THREE.PerspectiveCamera( + 75, + window.innerWidth / window.innerHeight, + 0.1, + 1000 + ); + const orbit = new OrbitControls(camera, renderer.domElement); + + camera.position.set(1, 2, 5); + orbit.update(); + + const boxGeometry = new THREE.BoxGeometry(1, 1, 1); + const boxMaterial = new THREE.MeshBasicMaterial({color: 0xffff00}); + const box = new THREE.Mesh(boxGeometry, boxMaterial); + scene.add(box); + + let heart_amp = 0.0; + + function animate(x) { + let sr = 120; + let freq = 20; + + box.position.x = 0.01 * Math.sin(2 * Math.PI * freq * (x / sr)); + box.position.y = 0.02 * Math.sin(2 * Math.PI * (freq/2) * (x / sr)); + // box.position.z = 0.05 * Math.sin(2 * Math.PI * (freq/3) * (x / sr)); + box.position.z = heart_amp * 1; + box.rotation.set(x/2000, x/2000, 0); + // camera.position.x = 1 * Math.sin(2 * Math.PI * (freq/6) * (x / sr)); + // camera.position.y = 2 * Math.sin(2 * Math.PI * (freq/11) * (x / sr)); + // camera.position.z = 1 + Math.abs(15 * Math.sin(2 * Math.PI * (freq/75) * (x / sr))); + + renderer.render(scene, camera); + } + + // const socket = new WebSocket(`${location.protocol === "https" ? "wss" : "ws"}://${location.hostname}:7890/ecg`); + const socket = new WebSocket(`ws://localhost:7890/ecg`); + socket.onmessage = (({data}) => { + heart_amp = data; + console.log(heart_amp); + + }); + + renderer.setAnimationLoop(animate); } attributeChangedCallback(name: string, _prev: string, curr: string) { - if (Attributes.MESSAGE === name) { - while (this.$message.firstChild) { - this.$message.removeChild(this.$message.lastChild!); - } - this.$message.appendChild(document.createTextNode(curr)); + if (Attributes.HEART_RATE === name) { + // send message via websocket to alter heart rate in real time } } }; -window.customElements.define("x-ecg-ticker", ECGTicker); +window.customElements.define("ecg-ticker", ECGTicker); export {}; \ No newline at end of file diff --git a/src/MyCard/MyCard.html b/src/MyCard/MyCard.html index 0f504e2..c667d7c 100644 --- a/src/MyCard/MyCard.html +++ b/src/MyCard/MyCard.html @@ -22,7 +22,7 @@ } header h1 { - font-size: 1.75rem; + font-size: 1.4rem; } #message { margin: 15px;