Skip to content
Nicholas VanFossen edited this page Jan 1, 2024 · 2 revisions

SVG Diagrams

One of the major features of this project is the interactive SVG that students can use to learn more about the spectrometer. All SVG assets can be found in an accompanying GitHub repository.

SVG Optimization and Components

SVGs created in Inkscape have tags that are not supported in default React. Because of this, we need to optimize the SVG by removing these tags. This optimization can be done easily in SVGOMG's GUI (just remember to update the settings to not cleanup IDs).

We have implemented a solution that automates the optimization and/ or translation from native SVG to .jsx (allows for finer-grain control using JavaScript). (For both of the commands listed below, svgo.config.js is used to change some of the default SVGO settings.)

  • To optimize tooltip SVGs, place the SVGs in the src/images/tooltips/ directory and run npm run svgo.

  • To optimize and translate the Instrument Window SVG, place the SVG into the src/images/ directory and run npm run svgr. This command will remove the .svg file from the directory.

SVG Interactivity and Animation

Text Interactivity

To center text in the SVG, add the following style to the text element you want centered. This was completed by using Inkscape's XML Editor:

text-anchor: middle;
dominant-baseline: central;

Then using Inkscape's Align and Distribute tool, center the text in the "parent" element. To edit text in JavaScript, use the textContent interface:

export function textInteractivity(text) {
  // DOM elements
  const textElement = document.getElementById("text");

  // set text
  textElement.textContent = text;
}

SVG Interactivity

SVG interactivity is similar to text. The major difference is the use of the setAttribute() element or setting the style of a particular CSS property. For example:

// this is good for moving a group or rectangle
document.getElementById("id-1").style.transform = "translate(0px, 0px)";

// this is good for moving a path
document
  .getElementById("id-2")
  .setAttribute("d", "M20,230 Q40,205 50,230 T90,230");

You may need to change the location of a group/element depending on a boolean:

export function componentLocation(isVisible) {
  // DOM elements
  const componentID = document.getElementById("component-1");

  lectureBottleInUse
    ? componentID.setAttribute("d", "")
    : componentID.setAttribute("d", "");
}

You may need to change the visibility of a group/element depending on a user parameter:

export function componentVisibility(parameter) {
  // DOM element
  const componentID = document.getElementById("component-1");

  // constant parameter values
  const componentValue = PARAMETER_VALUE.constantValue;

  // ternary used to show/hide cell window in the Main SVG
  componentID.style.display = parameter === componentValue ? "inline" : "none";
}

Animation

SVG animation is set in JavaScript using the animate() method. Animations are set using keyframes around the starting and ending position of the object you want to move. A simplified version of this code is shown below:

export function squareAnimation() {
  // DOM element
  const square = document.getElementById("square");

  square.animate(
    [
      // keyframes
      { transform: "translate(0px, 0px)" },   // starting position
      { transform: "translate(100px, 0px)" }, // move square to the right 100 pixels
      { transform: "translate(0px, 0px)" },   // ending position
    ],
    {
      // timing options
      duration: 1000,       // duration of total animation in milliseconds
      iterations: Infinity, // how many times the animation occurs
    }
  );

SVG Tooltips

When certain parts of the Instrument Window are clicked, a popup with additional image(s) and a description will appear. The underlying component behind this is the MUI Dialog.

In the SVG, components are collated by setting group IDs (the <g> element). In JavaScript, the SVG is setup with an onClick listener that will target the parent element's ID (event.target.parentElement.id), which is the group's ID. A dictionary (src/dictionaries/tooltips.js) is used to store and format text and images associated with each tooltip.

A simplified version of this code is shown below:

// InstrumentWindow.jsx
import React, { useState } from "react";

// components
import { Dialog } from "@mui/material";
import MySVG from "../images/InstrumentSVG";

// constants
import { BAD_ID } from "../dictionaries/constants";

// dictionaries - holds contents of the tooltips
import { tooltips } from "../dictionaries/tooltips";

// style
import "../style/routes/InstrumentWindow.css";

export default function InstrumentWindow() {
  const [toggled, setToggled] = useState(false);
  const [element, setElement] = useState();

  // looks at click event and determines ID selected
  //   some IDs are considered "bad" (do not have tooltips)
  const handleClick = (event) => {
    if (!BAD_ID.includes(event.target.parentElement.id)) {
      setElement(event.target.parentElement.id);
      setToggled(!toggled);
    }
  };

  return (
    <div>
      <MySVG onClick={handleClick} />

      {element && (
        <Dialog
          onClose={handleClick}
          open={toggled}
          fullScreen={element === "display" ? true : false}
        >
          {toolTips[element].text}
        </Dialog>
      )}
    </div>
  );
}