Skip to content

3. Bouwen

Arex edited this page Jun 25, 2023 · 13 revisions

Chat room (Jalal)

image

Doormiddel van deze code in de client-server heb ik de realtime chat kunnen realiseren.

let socketWrite = io()
let messages = document.querySelector('.chat-box ul')
let input = document.querySelector('.chat-input')


document.querySelector('.chat-form').addEventListener('submit', (event) => {
    event.preventDefault()

    console.log(input, messages)
    if (input.value) {
        socketWrite.emit('message', input.value)
        input.value = ''
    }
})

socketWrite.on('message', (message) => {
    addMessage(message)
})

socketWrite.on('whatever', (message) => {
    addMessage(message)
})

socketWrite.on('history', (history) => {
    history.forEach((message) => {
        addMessage(message)
    })
})

function addMessage(message) {
    messages.appendChild(Object.assign(document.createElement('li'), { textContent: message }))
    messages.scrollTop = messages.scrollHeight
}

Met deze html elementen had ik een een knop kunnen realiseren voor de oba chatart.

image

  <details open id="chat">
        <div class="chat-box">
            <summary>
                <h1>ChatArt room</h1>
            </summary>

Draw- Art Room (Stefan)

Voor het bouwen heb ik gebruik gemaakt van node, express, css, javascript en socket.io. Ook heb ik in dit project een UI-stack gebouwd waarmee er informatie met de gebruiker word gedeeld over de status van de webapp. De UI-stack bestaat uit:

  • Loading state
  • Empty state
  • Error state
  • Partial state
  • Ideal state

UI-stack

Loading state

De pagina begint met de loading state waarmee de pagina word voorbereid. De animatie van de loading state heb ik van LottieFiles, een platform met animaties in een .lottie formaat dat is afgeleid van JSON waardoor animaties vele malen kleiner zijn dan bijvoorbeeld .gif bestanden.

Schermafbeelding 2023-05-31 om 14 22 38

Empty state

De empty state is bijna vergelijkbaar met de ideal state. Het enige wat verschilt is dat deze state ook een LottieFiles animatie heeft waarbij de gebruiker een impuls krijgt om iets te tekenen.

Schermafbeelding 2023-05-31 om 14 22 41

Error state

Bij de error state heb ik een aparte 404.ejs pagina opgebouwd zodat wanneer de gebruiker een pagina bezoekt die niet bestaat deze als vangnet word ingeladen.

In node heb ik hiervoor de volgende code geschreven:

// Maakt een route naar de 404 pagina
app.get("*", (request, response) => {
  response.status(404).render("404");
})

Dit is het resultaat ervan:

Schermafbeelding 2023-05-31 om 14 22 51

Partial state / Ideal state

De gecombineerde partial en ideal state werkt de functie om te tekenen en is het ook mogelijk om de input van anderen te weergeven.

Schermafbeelding 2023-05-31 om 14 22 46

Node

In node heb ik de draw pagina gedefineerd in het volgende statement:

app.get("/draw", (request, response) => {
  fetchJson(defaultUrl)
    .then((data) => {
      response.render("draw", data);
    })
    .catch((error) => {
      response.render("error", { error: "Fout bij het ophalen van gegevens" });
    });
});

html

De html bouwde ik op in een header, een main en een footer

header

De header bevat 2 onderdelen: een icoontje van de gebruiker zodra die is ingelogd en een link terug naar de homepage.

    <header class="draw-header">
        <section>
            <span>
            <svg width="27" height="27" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path fill-rule="evenodd" clip-rule="evenodd" d="M26.0647 13.0286C26.0725 17.7262 23.5834 21.839 19.8473 24.1192C17.8731 25.3239 15.5508 26.017 13.0646 26.0143C9.91828 26.0108 7.03073 24.8935 4.77676 23.0366C4.19368 22.5562 3.65299 22.0263 3.1612 21.4534C1.20874 19.1789 0.0268493 16.2259 0.0215113 13C0.00963093 5.82029 5.82996 0.00639811 13.0216 0.0142906C20.2132 0.022183 26.0528 5.84887 26.0647 13.0286ZM23.4604 13.0257C23.4646 15.5546 22.5643 17.8718 21.0641 19.6728C18.8521 17.9474 16.07 16.9176 13.0496 16.9143C10.029 16.911 7.25036 17.9346 5.04415 19.6552C3.53799 17.8509 2.63001 15.5317 2.62583 13.0028C2.61632 7.25908 7.27259 2.60797 13.0259 2.61429C18.7792 2.6206 23.4509 7.28195 23.4604 13.0257ZM16.9454 10.4183C16.9489 12.5722 15.2029 14.3164 13.0454 14.314C10.8879 14.3117 9.13599 12.5637 9.13243 10.4098C9.12887 8.25585 10.875 6.51169 13.0325 6.51405C15.1899 6.51642 16.9418 8.26442 16.9454 10.4183Z" fill="#333333"/>
            </svg>    
            Stefan
            </span>   
            <h1 hidden>oba chatArt</h1>
            <a href="/" title="Ga terug naar de homepage">
                <svg width="24" height="23" viewBox="0 0 24 23" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path fill-rule="evenodd" clip-rule="evenodd" d="M20 0C21.0609 0 22.0783 0.421427 22.8284 1.17157C23.5786 1.92172 24 2.93913 24 4V14.6667C24 15.7275 23.5786 16.7449 22.8284 17.4951C22.0783 18.2452 21.0609 18.6667 20 18.6667H13.3333L6.66667 22.6667V18.6667H4C2.93913 18.6667 1.92172 18.2452 1.17157 17.4951C0.421427 16.7449 0 15.7275 0 14.6667V4C0 2.93913 0.421427 1.92172 1.17157 1.17157C1.92172 0.421427 2.93913 0 4 0H20ZM5.41669 6.66675C5.41669 5.97639 5.97633 5.41675 6.66669 5.41675H17.3334C18.0237 5.41675 18.5834 5.97639 18.5834 6.66675C18.5834 7.3571 18.0237 7.91675 17.3334 7.91675H6.66669C5.97633 7.91675 5.41669 7.3571 5.41669 6.66675ZM6.66669 10.75C5.97633 10.75 5.41669 11.3096 5.41669 12C5.41669 12.6904 5.97633 13.25 6.66669 13.25H14.6667C15.357 13.25 15.9167 12.6904 15.9167 12C15.9167 11.3096 15.357 10.75 14.6667 10.75H6.66669Z" fill="#FF0000"/>
                </svg>
            </a> 
        </section>
    </header>

Main De main bevat alleen het canvas waarbinnen er getekend kan worden.

  <main>
        <canvas width="320" height="320">
        </canvas>
    </main>

Footer De footer is het toolboard waar tools gekozen kunnen worden om mee te tekenen. Voor zover werkt alleen de pen & rechthoek tool.

    <footer>
        <nav>
            [...]
            <button title="pen gereedschap" class="draw-tool active" id="pencil">
                <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
                 [...]
                </svg>                  
            </button>
        </nav>
    </footer>

css

In de css wordt veel stijling mogelijk gemaakt voor de UI-stack. Ook een stukje enhancement wordt mogelijk gemaakt waardoor de link naar de tekenapp toegankelijk is.

/* UI-states */
.draw-loading-state {
    width: 100%;
    height: 100%;
    background-color: var(--c-default);
    display: flex;
    align-items: center;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    opacity: 1;
    transition: opacity var(--a-loading), visibility var(--a-loading );
    z-index: 10;
}

.draw-state-hide {
   /* opacity: 0; */
   visibility: hidden;
}

.draw-loading-state picture {
    margin: auto;
}

.draw-loading-state lottie-player {
    width: 33vw;
}

.draw-error-state {
    width: 100%;
    height: 100vh;
    display: flex;
    position: relative;
    align-items: center;
    justify-content: center;

}

.draw-error-state section {
    width: fit-content;
    display: block;
    position: absolute;
    top: 40%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.draw-empty-state {
    width: fit-content;
    height: fit-content;
    display: flex;
    position: absolute;
    top: 45%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 9;
    text-align: center;
    transition: opacity var(--a-loading), visibility var(--a-loading );
}

.draw-empty-state p {
    width: fit-content;
    margin: auto;
}

.draw-empty-state lottie-player {
    width: 40vw;
}

.js-enabled .link-draw {
    display: block;
}

.link-draw {
    display: none;
}

Ook heb ik met css de layout fixed aan het scherm gemaakt. Dit maakte ik door middel van display: grid:

.draw {
    width: 100%;
    height: 100vh;
    padding: var(--u-default);
    position: relative;
    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 1fr 18fr 1fr;
    flex-direction: column;
    grid-gap: 1.5em;
}

javascript

In het bestand draw.js ontwikkelde ik de functie om te tekenen.

Eerst bouwde ik een variabel die het beginpunt definieert.

const startDraw = (e) => {
  isDrawing = true;
  previousMouseX = e.offsetX;
  previousMouseY = e.offsetY;
  canvasContext.beginPath();
  canvasContext.lineWidth = brushWidth;
  snapshot = canvasContext.getImageData(0, 0, canvas.width, canvas.height);
};

Daarna maakte ik de functie die een lijn tekent.

const drawing = (e) => {
  if (!isDrawing) return;
  canvasContext.putImageData(snapshot, 0, 0);

  if (selectedTool === "brush") {
    canvasContext.lineTo(e.offsetX, e.offsetY);
    canvasContext.stroke();

    socket.emit("drawing", {
      tool: selectedTool,
      x: e.offsetX,
      y: e.offsetY,
      width: brushWidth,
    });
  } else if (selectedTool === "rectangle") {
    drawRectangle(e);
  } else if (selectedTool === "circle") {
    const circle = {
      x: previousMouseX,
      y: previousMouseY,
      radius: Math.sqrt(Math.pow(previousMouseX - e.offsetX, 2) + Math.pow(previousMouseY - e.offsetY, 2)),
    };

    const endDraw = (e) => {
      if (selectedTool === "circle" && isDrawing) {
        const circle = {
          x: previousMouseX,
          y: previousMouseY,
          radius: Math.sqrt(Math.pow(previousMouseX - e.offsetX, 2) + Math.pow(previousMouseY - e.offsetY, 2)),
        };
    
        drawCircle(circle);
        socket.emit("drawCircle", circle);
      }
    
      isDrawing = false;
    };
  }
};