Frontend Development 12 min read

Building Interactive Exam Charts with Recharts, SVG and CSS Mask in React

This article details how to redesign a learning report by selecting a lightweight React‑compatible visualization library, implementing exam score line charts with Recharts, customizing SVG labels and Bezier curves, creating a carousel bar chart, and using CSS‑mask techniques to render non‑continuous status bars.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Building Interactive Exam Charts with Recharts, SVG and CSS Mask in React

Demand Overview

Penguin Tutor pushes a "Learning Report" after each class, allowing parents to view attendance, interaction, quiz results, homework scores, class ranking and other data. The redesign optimizes data and presentation by adding an exam module, simplifying knowledge point expressions, using a younger visual style, and incorporating more data visualizations.

Exam Module – Data Visualization

1. Choosing a Visualization Library

To display student score trends, a third‑party chart library is needed. Options considered include HighChart, ECharts, and AntV (F2). The selection criteria were mobile support, lightweight, React compatibility, and high customizability.

Recharts was chosen because it is built with React and D3, supports declarative components, native SVG, and an API that handles personalized requirements.

Supports React components with simple declarative syntax.

Native SVG support via lightweight D3 sub‑modules.

API allows extensive customization.

Below is a sample usage of Recharts:

<code>&lt;ResponsiveContainer width="100%" height={200}&gt;
  &lt;LineChart data={data}&gt;
    &lt;CartesianGrid horizontal={false} strokeDasharray="2 3"/&gt;
    &lt;Line type={lineStyle} dataKey="avgScore" stroke="#CCCCCC" fill="#CCCCCC" label={&lt;CustomizedLabel data={data} relateKey="avgScore"/&gt;}&gt;
    &lt;Line type={lineStyle} dataKey="actualScore" stroke="#08CB6A" fill="#08CB6A" label={&lt;CustomizedLabel data={data} relateKey="actualScore"/&gt;}&gt;
    &lt;Legend align="left" verticalAlign="top" iconSize={4} iconType="rect"/&gt;
    &lt;XAxis dataKey="name" padding={{left: padding, right: padding}} axisLine={false} tickLine={false}/&gt;
    &lt;YAxis domain={[-8,108]} hide/&gt;
  &lt;/LineChart&gt;
&lt;/ResponsiveContainer&gt;</code>

Additional SVG‑like configuration options such as

strokeDasharray

make it easy for developers familiar with SVG to fine‑tune styles.

2. Drawing a Bezier Curve

To create a smooth curve between two points, a custom

BezierLineShape

extends D3’s shape utilities. Control points are calculated based on parameters, and the curve is rendered using

bezierCurveTo

on the canvas context.

<code>BezierLineShape.prototype = {
  lineStart() { /* set initial points */ },
  lineEnd() { /* set end points */ },
  point(x, y) { /* calculate control points and draw */ }
};</code>

A

CustomizedLabel

component is defined to render SVG text labels with dynamic direction and styling based on data values.

<code>export default class CustomizedLabel extends React.PureComponent {
  static defaultProps = { direction: 'up', stroke: '#777' };
  render() {
    const { x, y, value, direction } = this.props;
    const dy = direction === 'up' ? -10 : 18;
    return <text x={x} y={y} dy={dy} fill={stroke} fontSize={14} textAnchor="middle">{value}</text>;
  }
}</code>

Final effect:

Learning Review – Carousel Bar Chart

The learning review module allows horizontal swipe or click on bar charts to switch courses. A carousel component was chosen to host simple DOM‑based bar charts, avoiding complex chart libraries while meeting customization needs.

To handle edge cases where the first and last items should not show extra dashed axes, pseudo‑elements are added before and after the carousel container, preserving layout calculations.

<code>.time-chart-item:nth-of-type(1)::after { content: ''; width: pxToRem(116); height: pxToRem(144); position: absolute; background: url(./img/dashline.png) pxToRem(29) no-repeat; top: pxToRem(28); left: -pxToRem(116); }</code>

This Class – CSS Mask for Status Bar

A simple status bar shows online time. To make the green bar overlay the gray border and handle non‑continuous segments, the

-webkit-mask

property is used with a tiny PNG mask and calculated positions.

<code>const maskArray = [];
InClassState.map(item => {
  const { start, end } = item;
  const left = (start - lessonBeginTime) / allTime;
  const width = (end - start) / allTime;
  const maskLeft = left / (1 - width);
  maskArray.push(`url(${maskImg}) no-repeat ${maskLeft.toFixed(2)}% 0%`);
  maskArray.push(`${width * 100}% 0%`);
});
style.WebkitMask = maskArray.join(',');</code>

This technique replaces multiple layered divs with a single element, achieving the desired visual effect efficiently.

reactsvgCSSdata-visualizationBezier CurveRecharts
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.