forked from EXT/VR180-Web-Player
177 lines
4.8 KiB
TypeScript
177 lines
4.8 KiB
TypeScript
export type LucideIconName =
|
|
| 'circle-play'
|
|
| 'play'
|
|
| 'pause'
|
|
| 'maximize'
|
|
| 'arrow-left'
|
|
| 'rotate-ccw'
|
|
| 'rotate-cw'
|
|
| 'volume-2'
|
|
| 'volume-x'
|
|
| 'log-out';
|
|
|
|
type IconAttrs = Record<string, string>;
|
|
type IconNode = readonly [tagName: string, attrs: IconAttrs];
|
|
|
|
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
|
|
const ICONS: Record<LucideIconName, readonly IconNode[]> = {
|
|
'circle-play': [
|
|
['circle', { cx: '12', cy: '12', r: '10' }],
|
|
['polygon', { points: '10 8 16 12 10 16 10 8' }]
|
|
],
|
|
play: [
|
|
['polygon', { points: '6 3 20 12 6 21 6 3' }]
|
|
],
|
|
pause: [
|
|
['rect', { x: '14', y: '4', width: '4', height: '16', rx: '1' }],
|
|
['rect', { x: '6', y: '4', width: '4', height: '16', rx: '1' }]
|
|
],
|
|
maximize: [
|
|
['path', { d: 'M8 3H5a2 2 0 0 0-2 2v3' }],
|
|
['path', { d: 'M21 8V5a2 2 0 0 0-2-2h-3' }],
|
|
['path', { d: 'M3 16v3a2 2 0 0 0 2 2h3' }],
|
|
['path', { d: 'M16 21h3a2 2 0 0 0 2-2v-3' }]
|
|
],
|
|
'arrow-left': [
|
|
['path', { d: 'm12 19-7-7 7-7' }],
|
|
['path', { d: 'M19 12H5' }]
|
|
],
|
|
'rotate-ccw': [
|
|
['path', { d: 'M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8' }],
|
|
['path', { d: 'M3 3v5h5' }]
|
|
],
|
|
'rotate-cw': [
|
|
['path', { d: 'M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8' }],
|
|
['path', { d: 'M21 3v5h-5' }]
|
|
],
|
|
'volume-2': [
|
|
['path', { d: 'M11 4.702a1 1 0 0 0-1.664-.747L5.5 7.5H4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h1.5l3.836 3.545A1 1 0 0 0 11 19.298z' }],
|
|
['path', { d: 'M16 9a5 5 0 0 1 0 6' }],
|
|
['path', { d: 'M19.364 18.364a9 9 0 0 0 0-12.728' }]
|
|
],
|
|
'volume-x': [
|
|
['path', { d: 'M11 4.702a1 1 0 0 0-1.664-.747L5.5 7.5H4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h1.5l3.836 3.545A1 1 0 0 0 11 19.298z' }],
|
|
['line', { x1: '22', y1: '9', x2: '16', y2: '15' }],
|
|
['line', { x1: '16', y1: '9', x2: '22', y2: '15' }]
|
|
],
|
|
'log-out': [
|
|
['path', { d: 'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4' }],
|
|
['polyline', { points: '16 17 21 12 16 7' }],
|
|
['line', { x1: '21', y1: '12', x2: '9', y2: '12' }]
|
|
]
|
|
};
|
|
|
|
export function createLucideIcon(name: LucideIconName, className = 'vrwp-icon'): SVGSVGElement {
|
|
const svg = document.createElementNS(SVG_NS, 'svg');
|
|
svg.setAttribute('class', className);
|
|
svg.setAttribute('viewBox', '0 0 24 24');
|
|
svg.setAttribute('fill', 'none');
|
|
svg.setAttribute('stroke', 'currentColor');
|
|
svg.setAttribute('stroke-width', '2');
|
|
svg.setAttribute('stroke-linecap', 'round');
|
|
svg.setAttribute('stroke-linejoin', 'round');
|
|
svg.setAttribute('aria-hidden', 'true');
|
|
svg.setAttribute('focusable', 'false');
|
|
|
|
for (const [tagName, attrs] of ICONS[name]) {
|
|
const node = document.createElementNS(SVG_NS, tagName);
|
|
for (const [key, value] of Object.entries(attrs)) {
|
|
node.setAttribute(key, value);
|
|
}
|
|
svg.appendChild(node);
|
|
}
|
|
|
|
return svg;
|
|
}
|
|
|
|
export function setLucideIcon(target: HTMLElement, name: LucideIconName): void {
|
|
const existingIcon = target.querySelector('.vrwp-icon');
|
|
if (existingIcon) {
|
|
existingIcon.replaceWith(createLucideIcon(name));
|
|
return;
|
|
}
|
|
target.prepend(createLucideIcon(name));
|
|
}
|
|
|
|
export function drawLucideIcon(
|
|
ctx: CanvasRenderingContext2D,
|
|
name: LucideIconName,
|
|
x: number,
|
|
y: number,
|
|
size: number,
|
|
color = '#ffffff',
|
|
strokeWidth = 2
|
|
): void {
|
|
ctx.save();
|
|
ctx.translate(x, y);
|
|
ctx.scale(size / 24, size / 24);
|
|
ctx.strokeStyle = color;
|
|
ctx.lineWidth = strokeWidth;
|
|
ctx.lineCap = 'round';
|
|
ctx.lineJoin = 'round';
|
|
|
|
for (const [tagName, attrs] of ICONS[name]) {
|
|
drawIconNode(ctx, tagName, attrs);
|
|
}
|
|
|
|
ctx.restore();
|
|
}
|
|
|
|
function drawIconNode(ctx: CanvasRenderingContext2D, tagName: string, attrs: IconAttrs): void {
|
|
switch (tagName) {
|
|
case 'path':
|
|
ctx.stroke(new Path2D(attrs.d));
|
|
break;
|
|
case 'line':
|
|
ctx.beginPath();
|
|
ctx.moveTo(Number(attrs.x1), Number(attrs.y1));
|
|
ctx.lineTo(Number(attrs.x2), Number(attrs.y2));
|
|
ctx.stroke();
|
|
break;
|
|
case 'polyline':
|
|
case 'polygon':
|
|
drawPoints(ctx, attrs.points, tagName === 'polygon');
|
|
break;
|
|
case 'rect':
|
|
drawRect(ctx, attrs);
|
|
break;
|
|
case 'circle':
|
|
ctx.beginPath();
|
|
ctx.arc(Number(attrs.cx), Number(attrs.cy), Number(attrs.r), 0, Math.PI * 2);
|
|
ctx.stroke();
|
|
break;
|
|
}
|
|
}
|
|
|
|
function drawPoints(ctx: CanvasRenderingContext2D, pointsAttr: string, closePath: boolean): void {
|
|
const points = pointsAttr.trim().split(/\s+/).map((pair) => pair.split(',').map(Number));
|
|
if (points.length === 0) return;
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(points[0][0], points[0][1]);
|
|
for (const [x, y] of points.slice(1)) {
|
|
ctx.lineTo(x, y);
|
|
}
|
|
if (closePath) {
|
|
ctx.closePath();
|
|
}
|
|
ctx.stroke();
|
|
}
|
|
|
|
function drawRect(ctx: CanvasRenderingContext2D, attrs: IconAttrs): void {
|
|
const x = Number(attrs.x);
|
|
const y = Number(attrs.y);
|
|
const width = Number(attrs.width);
|
|
const height = Number(attrs.height);
|
|
const radius = Number(attrs.rx || 0);
|
|
|
|
ctx.beginPath();
|
|
if (radius > 0 && typeof ctx.roundRect === 'function') {
|
|
ctx.roundRect(x, y, width, height, radius);
|
|
} else {
|
|
ctx.rect(x, y, width, height);
|
|
}
|
|
ctx.stroke();
|
|
}
|