1cb93a386Sopenharmony_ci// Returns an [x, y] point on a circle, with given origin and radius, at a given angle
2cb93a386Sopenharmony_ci// counter-clockwise from the positive horizontal axis.
3cb93a386Sopenharmony_cifunction circleCoordinates(origin, radius, radians) {
4cb93a386Sopenharmony_ci    return [
5cb93a386Sopenharmony_ci        origin[0] + Math.cos(radians) * radius,
6cb93a386Sopenharmony_ci        origin[1] + Math.sin(radians) * radius
7cb93a386Sopenharmony_ci    ];
8cb93a386Sopenharmony_ci}
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci// Animator handles calling and stopping requestAnimationFrame and keeping track of framerate.
11cb93a386Sopenharmony_ciclass Animator {
12cb93a386Sopenharmony_ci    framesCount = 0;
13cb93a386Sopenharmony_ci    totalFramesMs = 0;
14cb93a386Sopenharmony_ci    animating = false;
15cb93a386Sopenharmony_ci    renderer = null;
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ci    start() {
18cb93a386Sopenharmony_ci        if (this.animating === false) {
19cb93a386Sopenharmony_ci            this.animating = true;
20cb93a386Sopenharmony_ci            this.framesCount = 0;
21cb93a386Sopenharmony_ci            const frameStartMs = performance.now();
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ci            const drawFrame = () => {
24cb93a386Sopenharmony_ci                if (this.animating && this.renderer) {
25cb93a386Sopenharmony_ci                    requestAnimationFrame(drawFrame);
26cb93a386Sopenharmony_ci                    this.framesCount++;
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ci                    const [x, y] = circleCoordinates([-70, -70], 50, this.framesCount/100);
29cb93a386Sopenharmony_ci                    this.renderer.render(x, y);
30cb93a386Sopenharmony_ci
31cb93a386Sopenharmony_ci                    const frameTimeMs = performance.now() - frameStartMs;
32cb93a386Sopenharmony_ci                    this.totalFramesMs = frameTimeMs;
33cb93a386Sopenharmony_ci                }
34cb93a386Sopenharmony_ci            };
35cb93a386Sopenharmony_ci            requestAnimationFrame(drawFrame);
36cb93a386Sopenharmony_ci        }
37cb93a386Sopenharmony_ci    }
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ci    stop() {
40cb93a386Sopenharmony_ci        this.animating = false;
41cb93a386Sopenharmony_ci    }
42cb93a386Sopenharmony_ci}
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci// The following three renderers draw a repeating pattern of paths.
45cb93a386Sopenharmony_ci// The approximate height and width of this repeated pattern is given by PATTERN_BOUNDS:
46cb93a386Sopenharmony_ciconst PATTERN_BOUNDS = 600;
47cb93a386Sopenharmony_ci// And the spacing of the pattern (distance between repeated paths) is given by PATTERN_SPACING:
48cb93a386Sopenharmony_ciconst PATTERN_SPACING = 70;
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ciclass SVGRenderer {
51cb93a386Sopenharmony_ci    constructor(svgObjectElement) {
52cb93a386Sopenharmony_ci        this.svgObjectElement = svgObjectElement;
53cb93a386Sopenharmony_ci        this.svgElArray = [];
54cb93a386Sopenharmony_ci        // Create an SVG element for every position in the pattern
55cb93a386Sopenharmony_ci        for (let xo = 0; xo < PATTERN_BOUNDS; xo += PATTERN_SPACING) {
56cb93a386Sopenharmony_ci            for (let yo = 0; yo < PATTERN_BOUNDS; yo += PATTERN_SPACING) {
57cb93a386Sopenharmony_ci                const clonedSVG = svgObjectElement.cloneNode(true);
58cb93a386Sopenharmony_ci                this.svgElArray.push(clonedSVG);
59cb93a386Sopenharmony_ci                svgObjectElement.parentElement.appendChild(clonedSVG);
60cb93a386Sopenharmony_ci            }
61cb93a386Sopenharmony_ci        }
62cb93a386Sopenharmony_ci    }
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_ci    render(x, y) {
65cb93a386Sopenharmony_ci        let i = 0;
66cb93a386Sopenharmony_ci        for (let xo = 0; xo < PATTERN_BOUNDS; xo += PATTERN_SPACING) {
67cb93a386Sopenharmony_ci            for (let yo = 0; yo < PATTERN_BOUNDS; yo += PATTERN_SPACING) {
68cb93a386Sopenharmony_ci                this.svgElArray[i].style.transform = `translate(${x + xo}px, ${y + yo}px)`;
69cb93a386Sopenharmony_ci                i++;
70cb93a386Sopenharmony_ci            }
71cb93a386Sopenharmony_ci        }
72cb93a386Sopenharmony_ci    }
73cb93a386Sopenharmony_ci}
74cb93a386Sopenharmony_ci
75cb93a386Sopenharmony_ciclass Path2dRenderer {
76cb93a386Sopenharmony_ci    constructor(svgData, offscreenCanvas) {
77cb93a386Sopenharmony_ci        this.data = svgData.map(([pathString, fillColor]) => [new Path2D(pathString), fillColor]);
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci        this.ctx = offscreenCanvas.getContext('2d');
80cb93a386Sopenharmony_ci    }
81cb93a386Sopenharmony_ci
82cb93a386Sopenharmony_ci    render(x, y) {
83cb93a386Sopenharmony_ci        const ctx = this.ctx;
84cb93a386Sopenharmony_ci
85cb93a386Sopenharmony_ci        ctx.clearRect(0, 0, 500, 500);
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci        for (let xo = 0; xo < PATTERN_BOUNDS; xo += PATTERN_SPACING) {
88cb93a386Sopenharmony_ci            for (let yo = 0; yo < PATTERN_BOUNDS; yo += PATTERN_SPACING) {
89cb93a386Sopenharmony_ci                ctx.save();
90cb93a386Sopenharmony_ci                ctx.translate(x + xo, y + yo);
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci                for (const [path, fillColor] of this.data) {
93cb93a386Sopenharmony_ci                    ctx.fillStyle = fillColor;
94cb93a386Sopenharmony_ci                    ctx.fill(path);
95cb93a386Sopenharmony_ci                }
96cb93a386Sopenharmony_ci                ctx.restore();
97cb93a386Sopenharmony_ci            }
98cb93a386Sopenharmony_ci        }
99cb93a386Sopenharmony_ci    }
100cb93a386Sopenharmony_ci}
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_ciclass CanvasKitRenderer {
103cb93a386Sopenharmony_ci    constructor(svgData, offscreenCanvas, CanvasKit) {
104cb93a386Sopenharmony_ci        this.CanvasKit = CanvasKit;
105cb93a386Sopenharmony_ci        this.data = svgData.map(([pathString, fillColor]) => [
106cb93a386Sopenharmony_ci            CanvasKit.Path.MakeFromSVGString(pathString),
107cb93a386Sopenharmony_ci            CanvasKit.parseColorString(fillColor)
108cb93a386Sopenharmony_ci        ]);
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci        this.surface = CanvasKit.MakeWebGLCanvasSurface(offscreenCanvas, null);
111cb93a386Sopenharmony_ci        if (!this.surface) {
112cb93a386Sopenharmony_ci            throw 'Could not make canvas surface';
113cb93a386Sopenharmony_ci        }
114cb93a386Sopenharmony_ci        this.canvas = this.surface.getCanvas();
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci        this.paint = new CanvasKit.Paint();
117cb93a386Sopenharmony_ci        this.paint.setAntiAlias(true);
118cb93a386Sopenharmony_ci        this.paint.setStyle(CanvasKit.PaintStyle.Fill);
119cb93a386Sopenharmony_ci    }
120cb93a386Sopenharmony_ci
121cb93a386Sopenharmony_ci    render(x, y) {
122cb93a386Sopenharmony_ci        const canvas = this.canvas;
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci        canvas.clear(this.CanvasKit.WHITE);
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci        for (let xo = 0; xo < PATTERN_BOUNDS; xo += PATTERN_SPACING) {
127cb93a386Sopenharmony_ci            for (let yo = 0; yo < PATTERN_BOUNDS; yo += PATTERN_SPACING) {
128cb93a386Sopenharmony_ci                canvas.save();
129cb93a386Sopenharmony_ci                canvas.translate(x + xo, y + yo);
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ci                for (const [path, color] of this.data) {
132cb93a386Sopenharmony_ci                    this.paint.setColor(color);
133cb93a386Sopenharmony_ci                    canvas.drawPath(path, this.paint);
134cb93a386Sopenharmony_ci                }
135cb93a386Sopenharmony_ci                canvas.restore();
136cb93a386Sopenharmony_ci            }
137cb93a386Sopenharmony_ci        }
138cb93a386Sopenharmony_ci        this.surface.flush();
139cb93a386Sopenharmony_ci    }
140cb93a386Sopenharmony_ci}
141