Looking for direction on particular effect

If you go to https://www.leagueoflegends.com/en-us/ and go to the "Choose Your Champion" section, you'll see that there are animated lines on the border of the circle. I'm curious how to go about achieving this effect? I tried looking through the devtools and asking chatgpt for some guidance but wasn't really able to find anything.
2 Replies
Jochem
Jochem16mo ago
it's a canvas animation if you open the debugger and search all files for drawCircle you'll find how they do it it doesn't look like it's a library they used, but luckily they're serving their webpack sourcemaps along with the code. Here's one of the functions in src/lib/components/hex-outline/hex-canvas/index.ts:
private _drawCircular(ctx: CanvasRenderingContext2D, delta: number, elapsed: number) {
const { width } = this._config;
const { style, countdown, transition } = this._options;

const activationProgress = this.getActivationProgress(elapsed, transition.duration, transition.delay);

const radius = width / 2 - CANVAS_BLEED;
const twoPi = Math.PI * 2;
const third = twoPi / 3;

// Don't draw at all until transition has been activated
if (!transition.active) return;

// Draw outline
// ---
ctx.strokeStyle = style.stroke;
drawCircle(ctx, 0, 0, radius, -Math.PI, -Math.PI + ease.outQuart(activationProgress) * twoPi);
ctx.stroke();

// Draw accents
// ---
const progress = ((elapsed - countdown.startTime) % countdown.duration) / countdown.duration;

const position = countdown.active ? ease.inOutQuart(progress) * twoPi : progress * twoPi;

const thickness = countdown.active
? style.accentThickness * progress * (1 - Math.pow(progress, 18))
: style.accentThickness;

const growth = countdown.active
? ease.inOutCubic(progress) * third
: third * 0.5 + third * 0.2 * Math.sin(progress * twoPi);

ctx.strokeStyle = style.accent;
ctx.lineWidth = thickness * activationProgress;

drawCircle(ctx, 0, 0, radius - thickness / 2, position, position + growth);
ctx.stroke();

drawCircle(ctx, 0, 0, radius - thickness / 2, position + third, position + third + growth);
ctx.stroke();

drawCircle(ctx, 0, 0, radius - thickness / 2, position + third * 2, position + third * 2 + growth);
ctx.stroke();
}
private _drawCircular(ctx: CanvasRenderingContext2D, delta: number, elapsed: number) {
const { width } = this._config;
const { style, countdown, transition } = this._options;

const activationProgress = this.getActivationProgress(elapsed, transition.duration, transition.delay);

const radius = width / 2 - CANVAS_BLEED;
const twoPi = Math.PI * 2;
const third = twoPi / 3;

// Don't draw at all until transition has been activated
if (!transition.active) return;

// Draw outline
// ---
ctx.strokeStyle = style.stroke;
drawCircle(ctx, 0, 0, radius, -Math.PI, -Math.PI + ease.outQuart(activationProgress) * twoPi);
ctx.stroke();

// Draw accents
// ---
const progress = ((elapsed - countdown.startTime) % countdown.duration) / countdown.duration;

const position = countdown.active ? ease.inOutQuart(progress) * twoPi : progress * twoPi;

const thickness = countdown.active
? style.accentThickness * progress * (1 - Math.pow(progress, 18))
: style.accentThickness;

const growth = countdown.active
? ease.inOutCubic(progress) * third
: third * 0.5 + third * 0.2 * Math.sin(progress * twoPi);

ctx.strokeStyle = style.accent;
ctx.lineWidth = thickness * activationProgress;

drawCircle(ctx, 0, 0, radius - thickness / 2, position, position + growth);
ctx.stroke();

drawCircle(ctx, 0, 0, radius - thickness / 2, position + third, position + third + growth);
ctx.stroke();

drawCircle(ctx, 0, 0, radius - thickness / 2, position + third * 2, position + third * 2 + growth);
ctx.stroke();
}
vince
vince16mo ago
Thanks Jochem! I never used canvas so this will be a good learning opportunity.
Want results from more Discord servers?
Add your server