Frontend Development 37 min read

Design and Implementation of Front‑End Animations for NetEase Cloud Music 2023 Annual Listening Report

The 2023 NetEase Cloud Music Annual Listening Report showcases a front‑end animation pipeline where the motion designer codes directly in React, using CSSTransition for page fades, CSS keyframes for text and particle effects, SVG masks for data visualisation, 3D perspective tricks, and extensive device testing to balance visual richness with performance.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
Design and Implementation of Front‑End Animations for NetEase Cloud Music 2023 Annual Listening Report

The 2023 NetEase Cloud Music "Annual Listening Report" is a yearly visual summary of user listening habits. The author, responsible for the animation design and implementation, shares the whole front‑end animation workflow and the concrete code used in the project.

Traditional workflow vs. adjusted workflow : normally a motion designer creates animation drafts in design software, hands over video demos and assets to developers, and developers code the animation. For this heavyweight project the animation part was split out early; the motion designer coded the animation directly in React, iterated with visual designers, and later reviewed together with front‑end developers.

Page transition (fade‑in/out) using React CSSTransition :

<CSSTransition
    // in 的值从 false 变为 true 时触发'页面进入',反之触发'页面消失'
    in={match}
    timeout={100}
    classNames={{
        // 页面进入前初始化
        enter: styles.reportEnter,
        // 页面进入完成
        enterDone: styles.reportEnterDone,
        // 页面离开前初始化
        exit: styles.reportExit,
        // 页面离开完成
        exitDone: styles.reportExitDone,
    }}
    appear
    unmountOnExit>
    {/* 页面DOM */}
</CSSTransition>

Corresponding CSS keyframes:

.reportEnter {
    opacity: 0;
}
.reportEnterDone {
    opacity: 1;
    transition: opacity 2000ms;
}
.reportExit {
    transition: opacity 300ms;
    opacity: 0;
}
.reportExitDone {
    opacity: 0;
}

Text appearance animation (fade‑in with upward movement) using pure CSS animation:

.textAni{
  animation-name:textAniKey;
  animation-duration:1.5s;
  animation-timing-function:cubic-bezier(0,0,0.5,1);
  animation-iteration-count:1;
  animation-direction:normal;
  animation-fill-mode:both;
}
@keyframes textAniKey{
  0%{
    transform:translateY(2vw);
    opacity: 0;
  }
  100%{
    transform:translateY(0vw);
    opacity: 1;
  }
}

Simple page animations such as the "first encounter" star falling effect are built with a wrapper div that handles overall opacity and translation, while inner img elements run their own looping glow animation:

<div
    className={styles.starOnSky}
    style={{
        position: 'absolute',
        transition: 'all 1.5s cubic-bezier(0,0,0,1) 0.2s',
        opacity: starIn ? 1 : 0,
        transform: `translateY(${starIn ? 0 : -20}vw)`,
    }}>
    {/* ... */}
</div>

Particle‑like effects (e.g., snow) are implemented with CSS variables and random numbers generated in React. The CSS defines the animation, while the React code injects per‑particle variables:

.snow {
  position: absolute;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  animation:snowAniKey 10s linear 0s infinite normal both;
}
@keyframes snowAniKey{
  0%{
    transform:translate(0,-20px) scale(var(--snow-scale)) rotate(0deg);
  }
  100%{
    transform:translate(var(--snow-end-x),var(--snow-end-y)) scale(var(--snow-scale)) rotate(360deg);
  }
}

3D effects are achieved by setting perspective on a parent container and transform‑style: preserve‑3d on child elements, then applying 3D rotations:

.A{
    perspective: 800px;
}
.B{
    transform-style:preserve-3d;
    transform: rotateX(45deg);
}
.C{
    transform-style:preserve-3d;
    transform: rotateY(45deg);
}

Data‑visualisation page uses SVG <path> with cubic‑bezier curves to draw mountain‑shaped masks for each month, combined with <clipPath> to reveal the background graph. The React code calculates the control points and generates the SVG mask dynamically:

<svg width={maskWidth} height={maskHeight}>
    <defs>
        {graphData.map((month,i)=> {
            const x0 = 0;
            const y0 = maskHeight - heightLimit;
            // ... compute x1,y1,control points ...
            return (
                <clipPath key={`clipPath_${i}`} id={`mask${i}`} clipPathUnits="objectBoundingBox"
                    transform={`scale(${1/maskWidth}, ${1/maskHeight})`}>
                    <path d={`M ${x0},${y0}
                        C ${cx1},${cy1} ${cx2},${cy2} ${x1},${y1}
                        C ${cx3},${cy3} ${cx4},${cy4} ${x2},${y2}
                        L ${x3},${y3}
                        L ${x3},${maskHeight}
                        L ${x0},${maskHeight}
                        L ${x0},${y0} Z`}
                        fill="#ffffff" />
                </clipPath>
            );
        })}
    </defs>
</svg>

More complex animations such as the "dopamine" effect, the "listening period" visual, and the "forgotten songs" particle‑dandelion are built by nesting multiple div s, applying separate position, rotation, and opacity animations, and using animation-delay to stagger the layers. Some of the circular motions are exported from After Effects via the AE2CSS plugin, resulting in long keyframe lists that approximate a circle with many straight‑line steps.

Challenges include device compatibility (different screen sizes, low‑end performance), extensive testing on many phone models, and occasional bugs that required either heavy optimisation or dropping an effect entirely.

Conclusion : Most of the visual richness is achieved with a handful of common techniques – animation delays, randomised parameters, fine‑tuned easing curves, SVG‑CSS synergy, occasional 3D perspective, and falling back to animated GIFs when the effect is too complex to code efficiently.

Front-endReactSVG3dCSS animationParticle System
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

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.