|
1 | | -export default function generateSvgSquircle(height, width, radius, clip) { |
| 1 | +/** |
| 2 | + * @param {number} height |
| 3 | + * @param {number} width |
| 4 | + * @param {number | Array<number>} radius |
| 5 | + * |
| 6 | + * @returns {string} SVG path data |
| 7 | + * */ |
| 8 | +export default function generateSvgSquircle(height, width, radius) { |
2 | 9 | /* from right to left top left corner upper (right half) */ |
3 | 10 | const ratios = [1.528665037, 1.0884928889, 0.8684068148, 0.07491140741, 0.6314939259, 0.1690595556, 0.3728238519]; |
4 | 11 |
|
| 12 | + if (typeof radius === 'number') |
| 13 | + radius = Array(4).fill(radius) |
| 14 | + else if (radius.length === 2) |
| 15 | + radius.push(radius[0]) |
| 16 | + if (radius.length === 3) |
| 17 | + radius.push(radius[1]) |
| 18 | + |
5 | 19 | height = Number(height); |
6 | 20 | width = Number(width); |
7 | 21 |
|
8 | | - radius = clip === false |
9 | | - ? Number(radius) |
10 | | - : Math.min(Number(radius), height / 2, width / 2); |
| 22 | + radius = radius.map(radius => Math.min(Number(radius), height / 2, width / 2)) |
11 | 23 |
|
12 | | - const [a0x, a1x, a2x, a3y, a3x, b1y, b1x] = Array(7).fill(0).map((a, i) => radius * ratios[i]), |
| 24 | + const [a0x, a1x, a2x, a3y, a3x, b1y, b1x] = Array(7) |
| 25 | + .fill(Array(4).fill(0)) |
| 26 | + .map((a, i) => a.map((b, j) => radius[j] * ratios[i])), |
13 | 27 | [b0y, b0x] = [a3y, a3x] |
14 | 28 |
|
15 | 29 | if (isNaN(height)) throw new Error(`'height' must be a number`); |
16 | 30 | if (isNaN(width)) throw new Error(`'width' must be a number`); |
17 | | - if (isNaN(radius)) throw new Error(`'radius' must be a number`); |
| 31 | + if (radius.includes(NaN)) throw new Error(`'radius' must be a number or an array containing 2 to 4 numbers`); |
18 | 32 |
|
19 | | - const a0xF = x => Math.min(x / 2, a0x), |
| 33 | + const a0xF = x => Math.min(x / 2, a0x[0]), |
20 | 34 | a0xw = a0xF(width), |
21 | 35 | a0xh = a0xF(height) |
22 | 36 |
|
23 | 37 | function mapRange(number, in_min, in_max, out_min, out_max) { |
24 | 38 | return (number - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; |
25 | 39 | } |
26 | 40 |
|
| 41 | + const maxRadius = Math.max(...radius); |
| 42 | + |
27 | 43 | const yOffsetF = (x) => |
28 | 44 | Math.max(0, Math.min( |
29 | | - mapRange(radius, (x / 2) * .90, x / 2, 0, 1), |
| 45 | + mapRange(maxRadius, (x / 2) * .90, x / 2, 0, 1), |
30 | 46 | 1 |
31 | | - )) * 1.7, |
32 | | - hyOffset = clip !== false ? yOffsetF(height) : 0, |
33 | | - wyOffset = clip !== false ? yOffsetF(width) : 0 |
| 47 | + )) * 200 / maxRadius, |
| 48 | + hyOffset = yOffsetF(height), |
| 49 | + wyOffset = yOffsetF(width) |
| 50 | + |
| 51 | + console.log(hyOffset, wyOffset) |
34 | 52 |
|
35 | 53 | const startPoint = `${a0xw},${wyOffset}` |
36 | 54 |
|
37 | 55 | return `M${startPoint} |
38 | | - ${width / 2 < a0x && clip !== false |
| 56 | + ${width / 2 < a0x[1] |
39 | 57 | ? '' |
40 | 58 | : `L${width - a0xw},0` |
41 | 59 | } |
42 | 60 | |
43 | | - C${width - a1x},0,${width - a2x},0,${width - a3x},${a3y} |
44 | | - C${width - b1x},${b1y},${width - b1y},${b1x},${width - b0y},${b0x} |
45 | | - C${width},${a2x},${width},${a1x}, |
| 61 | + C${width - a1x[1]},0,${width - a2x[1]},0,${width - a3x[1]},${a3y[1]} |
| 62 | + C${width - b1x[1]},${b1y[1]},${width - b1y[1]},${b1x[1]},${width - b0y[1]},${b0x[1]} |
| 63 | + C${width},${a2x[1]},${width},${a1x[1]}, |
46 | 64 | |
47 | 65 | ${width - hyOffset},${a0xh} |
48 | | - ${height / 2 < a0x && clip !== false |
| 66 | + ${height / 2 < a0x[2] |
49 | 67 | ? '' |
50 | 68 | : `L${width},${height - a0xh}` |
51 | 69 | } |
52 | 70 | |
53 | | - C${width},${height - a1x},${width},${height - a2x},${width - a3y},${height - a3x} |
54 | | - C${width - b1y},${height - b1x},${width - b1x},${height - b1y},${width - b0x},${height - b0y} |
55 | | - C${width - a2x},${height},${width - a1x},${height}, |
| 71 | + C${width},${height - a1x[2]},${width},${height - a2x[2]},${width - a3y[2]},${height - a3x[2]} |
| 72 | + C${width - b1y[2]},${height - b1x[2]},${width - b1x[2]},${height - b1y[2]},${width - b0x[2]},${height - b0y[2]} |
| 73 | + C${width - a2x[2]},${height},${width - a1x[2]},${height}, |
56 | 74 | |
57 | 75 | ${width - a0xw},${height - wyOffset} |
58 | | - ${width / 2 < a0x && clip !== false |
| 76 | + ${width / 2 < a0x[3] |
59 | 77 | ? '' |
60 | 78 | : `L${a0xw},${height}` |
61 | 79 | } |
62 | 80 | |
63 | | - C${a1x},${height},${a2x},${height},${a3x},${height - a3y} |
64 | | - C${b1x},${height - b1y},${b1y},${height - b1x},${b0y},${height - b0x} |
65 | | - C0,${height - a2x},0,${height - a1x}, |
| 81 | + C${a1x[3]},${height},${a2x[3]},${height},${a3x[3]},${height - a3y[3]} |
| 82 | + C${b1x[3]},${height - b1y[3]},${b1y[3]},${height - b1x[3]},${b0y[3]},${height - b0x[3]} |
| 83 | + C0,${height - a2x[3]},0,${height - a1x[3]}, |
66 | 84 | |
67 | 85 | ${hyOffset},${height - a0xh} |
68 | | - ${height / 2 < a0x && clip !== false |
| 86 | + ${height / 2 < a0x[0] |
69 | 87 | ? '' |
70 | 88 | : `L0,${a0xh}` |
71 | 89 | } |
72 | 90 | |
73 | | - C0,${a1x},0,${a2x},${a3y},${a3x} |
74 | | - C${b1y},${b1x},${b1x},${b1y},${b0x},${b0y} |
75 | | - C${a2x},0,${a1x},0,${startPoint} |
| 91 | + C0,${a1x[0]},0,${a2x[0]},${a3y[0]},${a3x[0]} |
| 92 | + C${b1y[0]},${b1x[0]},${b1x[0]},${b1y[0]},${b0x[0]},${b0y[0]} |
| 93 | + C${a2x[0]},0,${a1x[0]},0,${startPoint} |
76 | 94 | Z`.replace(/\n */g, ''); |
77 | 95 | } |
0 commit comments