@tremolo-ui/react 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/cjs/index.js +435 -203
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/AnimationCanvas/index.d.ts +2 -2
  4. package/dist/cjs/types/components/AnimationCanvas/index.d.ts.map +1 -1
  5. package/dist/cjs/types/components/AnimationKnob/index.d.ts +10 -3
  6. package/dist/cjs/types/components/AnimationKnob/index.d.ts.map +1 -1
  7. package/dist/cjs/types/components/Knob/index.d.ts +13 -3
  8. package/dist/cjs/types/components/Knob/index.d.ts.map +1 -1
  9. package/dist/cjs/types/components/NumberInput/DecrementStepper.d.ts +2 -0
  10. package/dist/cjs/types/components/NumberInput/DecrementStepper.d.ts.map +1 -1
  11. package/dist/cjs/types/components/NumberInput/IncrementStepper.d.ts +2 -0
  12. package/dist/cjs/types/components/NumberInput/IncrementStepper.d.ts.map +1 -1
  13. package/dist/cjs/types/components/NumberInput/InternalInput.d.ts +18 -0
  14. package/dist/cjs/types/components/NumberInput/InternalInput.d.ts.map +1 -0
  15. package/dist/cjs/types/components/NumberInput/Stepper.d.ts +2 -0
  16. package/dist/cjs/types/components/NumberInput/Stepper.d.ts.map +1 -1
  17. package/dist/cjs/types/components/NumberInput/context.d.ts +3 -2
  18. package/dist/cjs/types/components/NumberInput/context.d.ts.map +1 -1
  19. package/dist/cjs/types/components/NumberInput/index.d.ts +12 -3
  20. package/dist/cjs/types/components/NumberInput/index.d.ts.map +1 -1
  21. package/dist/cjs/types/components/Piano/BlackKey.d.ts.map +1 -1
  22. package/dist/cjs/types/components/Piano/WhiteKey.d.ts.map +1 -1
  23. package/dist/cjs/types/components/Piano/index.d.ts +7 -4
  24. package/dist/cjs/types/components/Piano/index.d.ts.map +1 -1
  25. package/dist/cjs/types/components/Piano/key.d.ts +4 -0
  26. package/dist/cjs/types/components/Piano/key.d.ts.map +1 -1
  27. package/dist/cjs/types/components/Piano/keyboardShortcuts.d.ts +2 -2
  28. package/dist/cjs/types/components/Slider/Scale.d.ts +1 -3
  29. package/dist/cjs/types/components/Slider/Scale.d.ts.map +1 -1
  30. package/dist/cjs/types/components/Slider/Thumb.d.ts +1 -0
  31. package/dist/cjs/types/components/Slider/Thumb.d.ts.map +1 -1
  32. package/dist/cjs/types/components/Slider/Track.d.ts +2 -4
  33. package/dist/cjs/types/components/Slider/Track.d.ts.map +1 -1
  34. package/dist/cjs/types/components/Slider/context.d.ts +2 -10
  35. package/dist/cjs/types/components/Slider/context.d.ts.map +1 -1
  36. package/dist/cjs/types/components/Slider/index.d.ts +5 -1
  37. package/dist/cjs/types/components/Slider/index.d.ts.map +1 -1
  38. package/dist/cjs/types/components/WheelObserver/index.d.ts +3 -3
  39. package/dist/cjs/types/components/WheelObserver/index.d.ts.map +1 -1
  40. package/dist/cjs/types/components/XYPad/Area.d.ts +3 -3
  41. package/dist/cjs/types/components/XYPad/Area.d.ts.map +1 -1
  42. package/dist/cjs/types/components/XYPad/Thumb.d.ts +1 -0
  43. package/dist/cjs/types/components/XYPad/Thumb.d.ts.map +1 -1
  44. package/dist/cjs/types/components/XYPad/index.d.ts +6 -1
  45. package/dist/cjs/types/components/XYPad/index.d.ts.map +1 -1
  46. package/dist/cjs/types/hooks/useAnimationFrame.d.ts.map +1 -1
  47. package/dist/cjs/types/hooks/useInterval.d.ts +2 -0
  48. package/dist/cjs/types/hooks/useInterval.d.ts.map +1 -0
  49. package/dist/cjs/types/hooks/useLongPress.d.ts +2 -0
  50. package/dist/cjs/types/hooks/useLongPress.d.ts.map +1 -0
  51. package/dist/cjs/types/index.d.ts +5 -0
  52. package/dist/cjs/types/index.d.ts.map +1 -1
  53. package/dist/esm/index.js +431 -204
  54. package/dist/esm/index.js.map +1 -1
  55. package/dist/esm/types/components/AnimationCanvas/index.d.ts +2 -2
  56. package/dist/esm/types/components/AnimationCanvas/index.d.ts.map +1 -1
  57. package/dist/esm/types/components/AnimationKnob/index.d.ts +10 -3
  58. package/dist/esm/types/components/AnimationKnob/index.d.ts.map +1 -1
  59. package/dist/esm/types/components/Knob/index.d.ts +13 -3
  60. package/dist/esm/types/components/Knob/index.d.ts.map +1 -1
  61. package/dist/esm/types/components/NumberInput/DecrementStepper.d.ts +2 -0
  62. package/dist/esm/types/components/NumberInput/DecrementStepper.d.ts.map +1 -1
  63. package/dist/esm/types/components/NumberInput/IncrementStepper.d.ts +2 -0
  64. package/dist/esm/types/components/NumberInput/IncrementStepper.d.ts.map +1 -1
  65. package/dist/esm/types/components/NumberInput/InternalInput.d.ts +18 -0
  66. package/dist/esm/types/components/NumberInput/InternalInput.d.ts.map +1 -0
  67. package/dist/esm/types/components/NumberInput/Stepper.d.ts +2 -0
  68. package/dist/esm/types/components/NumberInput/Stepper.d.ts.map +1 -1
  69. package/dist/esm/types/components/NumberInput/context.d.ts +3 -2
  70. package/dist/esm/types/components/NumberInput/context.d.ts.map +1 -1
  71. package/dist/esm/types/components/NumberInput/index.d.ts +12 -3
  72. package/dist/esm/types/components/NumberInput/index.d.ts.map +1 -1
  73. package/dist/esm/types/components/Piano/BlackKey.d.ts.map +1 -1
  74. package/dist/esm/types/components/Piano/WhiteKey.d.ts.map +1 -1
  75. package/dist/esm/types/components/Piano/index.d.ts +7 -4
  76. package/dist/esm/types/components/Piano/index.d.ts.map +1 -1
  77. package/dist/esm/types/components/Piano/key.d.ts +4 -0
  78. package/dist/esm/types/components/Piano/key.d.ts.map +1 -1
  79. package/dist/esm/types/components/Piano/keyboardShortcuts.d.ts +2 -2
  80. package/dist/esm/types/components/Slider/Scale.d.ts +1 -3
  81. package/dist/esm/types/components/Slider/Scale.d.ts.map +1 -1
  82. package/dist/esm/types/components/Slider/Thumb.d.ts +1 -0
  83. package/dist/esm/types/components/Slider/Thumb.d.ts.map +1 -1
  84. package/dist/esm/types/components/Slider/Track.d.ts +2 -4
  85. package/dist/esm/types/components/Slider/Track.d.ts.map +1 -1
  86. package/dist/esm/types/components/Slider/context.d.ts +2 -10
  87. package/dist/esm/types/components/Slider/context.d.ts.map +1 -1
  88. package/dist/esm/types/components/Slider/index.d.ts +5 -1
  89. package/dist/esm/types/components/Slider/index.d.ts.map +1 -1
  90. package/dist/esm/types/components/WheelObserver/index.d.ts +3 -3
  91. package/dist/esm/types/components/WheelObserver/index.d.ts.map +1 -1
  92. package/dist/esm/types/components/XYPad/Area.d.ts +3 -3
  93. package/dist/esm/types/components/XYPad/Area.d.ts.map +1 -1
  94. package/dist/esm/types/components/XYPad/Thumb.d.ts +1 -0
  95. package/dist/esm/types/components/XYPad/Thumb.d.ts.map +1 -1
  96. package/dist/esm/types/components/XYPad/index.d.ts +6 -1
  97. package/dist/esm/types/components/XYPad/index.d.ts.map +1 -1
  98. package/dist/esm/types/hooks/useAnimationFrame.d.ts.map +1 -1
  99. package/dist/esm/types/hooks/useInterval.d.ts +2 -0
  100. package/dist/esm/types/hooks/useInterval.d.ts.map +1 -0
  101. package/dist/esm/types/hooks/useLongPress.d.ts +2 -0
  102. package/dist/esm/types/hooks/useLongPress.d.ts.map +1 -0
  103. package/dist/esm/types/index.d.ts +5 -0
  104. package/dist/esm/types/index.d.ts.map +1 -1
  105. package/dist/styles/index.css +115 -33
  106. package/package.json +41 -40
  107. package/src/components/AnimationCanvas/index.tsx +15 -7
  108. package/src/components/AnimationKnob/index.tsx +216 -187
  109. package/src/components/Knob/index.tsx +230 -171
  110. package/src/components/NumberInput/DecrementStepper.tsx +9 -2
  111. package/src/components/NumberInput/IncrementStepper.tsx +9 -2
  112. package/src/components/NumberInput/InternalInput.tsx +219 -0
  113. package/src/components/NumberInput/Stepper.tsx +2 -0
  114. package/src/components/NumberInput/context.tsx +21 -9
  115. package/src/components/NumberInput/index.css +76 -22
  116. package/src/components/NumberInput/index.tsx +94 -162
  117. package/src/components/Piano/BlackKey.tsx +14 -0
  118. package/src/components/Piano/WhiteKey.tsx +14 -0
  119. package/src/components/Piano/index.css +22 -6
  120. package/src/components/Piano/index.tsx +20 -22
  121. package/src/components/Piano/key.ts +5 -0
  122. package/src/components/Piano/keyboardShortcuts.ts +2 -2
  123. package/src/components/Slider/Scale.tsx +1 -4
  124. package/src/components/Slider/Thumb.tsx +6 -0
  125. package/src/components/Slider/Track.tsx +5 -4
  126. package/src/components/Slider/context.tsx +5 -20
  127. package/src/components/Slider/index.css +5 -2
  128. package/src/components/Slider/index.tsx +52 -26
  129. package/src/components/WheelObserver/index.tsx +4 -2
  130. package/src/components/XYPad/Area.tsx +7 -6
  131. package/src/components/XYPad/Thumb.tsx +6 -0
  132. package/src/components/XYPad/index.css +12 -2
  133. package/src/components/XYPad/index.tsx +57 -41
  134. package/src/hooks/useInterval.ts +27 -0
  135. package/src/hooks/useLongPress.ts +31 -0
  136. package/src/index.ts +6 -0
  137. package/src/styles/index.css +0 -1
@@ -2,7 +2,9 @@ import clsx from 'clsx'
2
2
  import React, {
3
3
  ComponentPropsWithoutRef,
4
4
  CSSProperties,
5
+ forwardRef,
5
6
  useCallback,
7
+ useImperativeHandle,
6
8
  useRef,
7
9
  } from 'react'
8
10
 
@@ -77,211 +79,238 @@ export type AnimationKnobProps = {
77
79
  }
78
80
 
79
81
  /**
80
- * simple rotary Knob.
81
82
  * @category AnimationKnob
82
83
  */
83
- export function AnimationKnob({
84
- value,
85
- min,
86
- max,
87
- defaultValue = min,
88
- startValue = min,
89
- step = 1,
90
- skew = 1,
91
- size,
92
- width = 50,
93
- height = 50,
94
- activeColor = '#7998ec',
95
- inactiveColor = '#eee',
96
- bg = '#ccc',
97
- thumb = '#4e76e6',
98
- lineWeight = 6,
99
- thumbWeight = 4,
100
- draw,
101
- bodyNoSelect = true,
102
- wheel = ['raw', 1],
103
- keyboard = ['raw', 1],
104
- enableDoubleClickDefault = true,
105
- style,
106
- className,
107
- onChange,
108
- ...props
109
- }: AnimationKnobProps &
110
- Omit<ComponentPropsWithoutRef<'div'>, keyof AnimationKnobProps>) {
111
- // -- state and ref ---
112
- const valueRef = useRef(0)
84
+ export interface AnimationKnobMethods {
85
+ focus: () => void
86
+ blur: () => void
87
+ }
88
+
89
+ type Props = AnimationKnobProps &
90
+ Omit<ComponentPropsWithoutRef<'div'>, keyof AnimationKnobProps>
91
+
92
+ /**
93
+ * Animatable rotary knob.
94
+ * @category AnimationKnob
95
+ */
96
+ export const AnimationKnob = forwardRef<AnimationKnobMethods, Props>(
97
+ (
98
+ {
99
+ value,
100
+ min,
101
+ max,
102
+ defaultValue = min,
103
+ startValue = min,
104
+ step = 1,
105
+ skew = 1,
106
+ size,
107
+ width = 50,
108
+ height = 50,
109
+ activeColor = '#7998ec',
110
+ inactiveColor = '#eee',
111
+ bg = '#ccc',
112
+ thumb = '#4e76e6',
113
+ lineWeight = 6,
114
+ thumbWeight = 4,
115
+ draw,
116
+ bodyNoSelect = true,
117
+ wheel = ['raw', 1],
118
+ keyboard = ['raw', 1],
119
+ enableDoubleClickDefault = true,
120
+ style,
121
+ className,
122
+ onChange,
123
+ ...props
124
+ }: Props,
125
+ ref,
126
+ ) => {
127
+ // -- state and ref ---
128
+ const valueRef = useRef(0)
129
+ const elmRef = useRef<HTMLDivElement>(null)
130
+
131
+ // --- interpret props ---
132
+ const isRelativeSize =
133
+ typeof (size ?? width) == 'string' || typeof height == 'string'
113
134
 
114
- // --- interpret props ---
115
- const isRelativeSize =
116
- typeof (size ?? width) == 'string' || typeof height == 'string'
135
+ const onDrag = useCallback(
136
+ (_x: number, y: number) => {
137
+ if (!onChange) return
138
+ const v = rawValue(valueRef.current - y / 100, min, max, skew)
139
+ const v2 = clamp(stepValue(v, step), min, max)
140
+ onChange(v2)
141
+ },
142
+ [max, min, onChange, skew, step],
143
+ )
117
144
 
118
- const onDrag = useCallback(
119
- (_x: number, y: number) => {
120
- if (!onChange) return
121
- const v = rawValue(valueRef.current - y / 100, min, max, skew)
122
- const v2 = clamp(stepValue(v, step), min, max)
123
- onChange(v2)
124
- },
125
- [max, min, onChange, skew, step],
126
- )
145
+ const handleKeyDown = useCallback(
146
+ (event: React.KeyboardEvent<HTMLDivElement>) => {
147
+ if (!keyboard || !onChange) return
148
+ const key = event.key
149
+ if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
150
+ event.preventDefault()
151
+ const x =
152
+ key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1]
153
+ let v
154
+ if (keyboard[0] == 'normalized') {
155
+ const n = normalizeValue(value, min, max, skew)
156
+ v = rawValue(n + x, min, max, skew)
157
+ } else {
158
+ v = value + x
159
+ }
160
+ onChange(clamp(stepValue(v, step), min, max))
161
+ }
162
+ },
163
+ [value, min, max, step, skew, keyboard, onChange],
164
+ )
165
+
166
+ // --- hooks ---
167
+ const [touchMoveRefCallback, pointerDownHandler] = useDrag<HTMLDivElement>({
168
+ onDrag: onDrag,
169
+ onDragStart: () => {
170
+ valueRef.current = normalizeValue(value, min, max, skew)
171
+ if (bodyNoSelect) addNoSelect()
172
+ },
173
+ onDragEnd: () => {
174
+ if (bodyNoSelect) removeNoSelect()
175
+ },
176
+ })
127
177
 
128
- const handleKeyDown = useCallback(
129
- (event: React.KeyboardEvent<HTMLDivElement>) => {
130
- if (!keyboard || !onChange) return
131
- const key = event.key
132
- if (['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown'].includes(key)) {
178
+ const wheelRefCallback = useRefCallbackEvent(
179
+ 'wheel',
180
+ (event) => {
181
+ if (!wheel) return
133
182
  event.preventDefault()
134
- const x =
135
- key == 'ArrowRight' || key == 'ArrowUp' ? keyboard[1] : -keyboard[1]
183
+ const x = event.deltaY > 0 ? -wheel[1] : wheel[1]
136
184
  let v
137
- if (keyboard[0] == 'normalized') {
185
+ if (wheel[0] == 'normalized') {
138
186
  const n = normalizeValue(value, min, max, skew)
139
187
  v = rawValue(n + x, min, max, skew)
140
188
  } else {
141
189
  v = value + x
142
190
  }
143
- onChange(clamp(stepValue(v, step), min, max))
144
- }
145
- },
146
- [value, min, max, step, skew, keyboard, onChange],
147
- )
148
-
149
- // --- hooks ---
150
- const [touchMoveRefCallback, pointerDownHandler] = useDrag<HTMLDivElement>({
151
- onDrag: onDrag,
152
- onDragStart: () => {
153
- valueRef.current = normalizeValue(value, min, max, skew)
154
- if (bodyNoSelect) addNoSelect()
155
- },
156
- onDragEnd: () => {
157
- if (bodyNoSelect) removeNoSelect()
158
- },
159
- })
191
+ if (onChange) onChange(clamp(stepValue(v, step), min, max))
192
+ },
193
+ { passive: false },
194
+ [wheel, value, min, max, skew, step, onChange],
195
+ )
160
196
 
161
- const wheelRefCallback = useRefCallbackEvent(
162
- 'wheel',
163
- (event) => {
164
- if (!wheel) return
165
- event.preventDefault()
166
- const x = event.deltaY > 0 ? -wheel[1] : wheel[1]
167
- let v
168
- if (wheel[0] == 'normalized') {
169
- const n = normalizeValue(value, min, max, skew)
170
- v = rawValue(n + x, min, max, skew)
171
- } else {
172
- v = value + x
197
+ useImperativeHandle(ref, () => {
198
+ return {
199
+ focus() {
200
+ elmRef.current?.focus()
201
+ },
202
+ blur() {
203
+ elmRef.current?.blur()
204
+ },
173
205
  }
174
- if (onChange) onChange(clamp(stepValue(v, step), min, max))
175
- },
176
- { passive: false },
177
- [wheel, value, min, max, skew, step, onChange],
178
- )
206
+ }, [])
179
207
 
180
- return (
181
- <div
182
- className={clsx('tremolo-animation-knob', className)}
183
- ref={(div) => {
184
- wheelRefCallback(div)
185
- touchMoveRefCallback(div)
186
- }}
187
- role="slider"
188
- tabIndex={0}
189
- aria-valuenow={value}
190
- aria-valuemax={max}
191
- aria-valuemin={min}
192
- style={{
193
- display: 'inline-block',
194
- width: size ?? width,
195
- height: size ?? height,
196
- cursor: 'pointer',
197
- WebkitTapHighlightColor: 'transparent',
198
- ...style,
199
- }}
200
- onPointerDown={pointerDownHandler}
201
- onDoubleClick={() => {
202
- if (enableDoubleClickDefault && onChange) {
203
- onChange(defaultValue)
204
- }
205
- }}
206
- onKeyDown={handleKeyDown}
207
- onContextMenu={(e) => e.preventDefault()}
208
- {...props}
209
- >
210
- <AnimationCanvas
211
- {...(isRelativeSize
212
- ? { relativeSize: true }
213
- : {
214
- width: size ?? width,
215
- height: size ?? height,
216
- })}
217
- draw={
218
- draw
219
- ? draw
220
- : (ctx, width, height) => {
221
- const p = normalizeValue(value, min, max, skew)
222
- const startP = normalizeValue(startValue, min, max, skew)
223
- const w = width.current,
224
- cx = w / 2
225
- const h = height.current,
226
- cy = h / 2
227
- const r = Math.min(cx, cy)
228
- if (r <= 0) return
208
+ return (
209
+ <div
210
+ className={clsx('tremolo-animation-knob', className)}
211
+ ref={(div) => {
212
+ elmRef.current = div
213
+ wheelRefCallback(div)
214
+ touchMoveRefCallback(div)
215
+ }}
216
+ role="slider"
217
+ tabIndex={0}
218
+ aria-valuenow={value}
219
+ aria-valuemax={max}
220
+ aria-valuemin={min}
221
+ style={{
222
+ display: 'inline-block',
223
+ width: size ?? width,
224
+ height: size ?? height,
225
+ cursor: 'pointer',
226
+ WebkitTapHighlightColor: 'transparent',
227
+ ...style,
228
+ }}
229
+ onPointerDown={pointerDownHandler}
230
+ onDoubleClick={() => {
231
+ if (enableDoubleClickDefault && onChange) {
232
+ onChange(defaultValue)
233
+ }
234
+ }}
235
+ onKeyDown={handleKeyDown}
236
+ {...props}
237
+ >
238
+ <AnimationCanvas
239
+ {...(isRelativeSize
240
+ ? { relativeSize: true }
241
+ : {
242
+ width: size ?? width,
243
+ height: size ?? height,
244
+ })}
245
+ draw={
246
+ draw
247
+ ? draw
248
+ : (ctx, width, height) => {
249
+ const p = normalizeValue(value, min, max, skew)
250
+ const startP = normalizeValue(startValue, min, max, skew)
251
+ const w = width.current,
252
+ cx = w / 2
253
+ const h = height.current,
254
+ cy = h / 2
255
+ const r = Math.min(cx, cy)
256
+ if (r <= 0) return
229
257
 
230
- // reset
231
- ctx.clearRect(0, 0, w, h)
232
- ctx.lineCap = 'butt'
258
+ // reset
259
+ ctx.clearRect(0, 0, w, h)
260
+ ctx.lineCap = 'butt'
233
261
 
234
- // bg
235
- ctx.beginPath()
236
- ctx.fillStyle = bg
237
- ctx.arc(cx, cy, r - 0.5, 0, 2 * Math.PI)
238
- ctx.fill()
262
+ // bg
263
+ ctx.beginPath()
264
+ ctx.fillStyle = bg
265
+ ctx.arc(cx, cy, r - 0.5, 0, 2 * Math.PI)
266
+ ctx.fill()
239
267
 
240
- ctx.lineWidth = lineWeight
268
+ ctx.lineWidth = lineWeight
241
269
 
242
- // inactive rotary
243
- ctx.beginPath()
244
- ctx.strokeStyle = inactiveColor
245
- ctx.arc(
246
- cx,
247
- cy,
248
- r - 0.5 - lineWeight / 2,
249
- radian(135),
250
- radian(45),
251
- )
252
- ctx.stroke()
270
+ // inactive rotary
271
+ ctx.beginPath()
272
+ ctx.strokeStyle = inactiveColor
273
+ ctx.arc(
274
+ cx,
275
+ cy,
276
+ r - 0.5 - lineWeight / 2,
277
+ radian(135),
278
+ radian(45),
279
+ )
280
+ ctx.stroke()
253
281
 
254
- // active rotary
255
- ctx.beginPath()
256
- ctx.strokeStyle = activeColor
257
- ctx.arc(
258
- cx,
259
- cy,
260
- r - 0.5 - lineWeight / 2,
261
- radian(135 + 270 * startP),
262
- radian(135 + 270 * p),
263
- p < startP,
264
- )
265
- ctx.stroke()
282
+ // active rotary
283
+ ctx.beginPath()
284
+ ctx.strokeStyle = activeColor
285
+ ctx.arc(
286
+ cx,
287
+ cy,
288
+ r - 0.5 - lineWeight / 2,
289
+ radian(135 + 270 * startP),
290
+ radian(135 + 270 * p),
291
+ p < startP,
292
+ )
293
+ ctx.stroke()
266
294
 
267
- // thumb
268
- ctx.lineCap = 'round'
269
- ctx.beginPath()
270
- ctx.moveTo(cx, cy)
271
- ctx.lineTo(
272
- cx +
273
- Math.cos(radian(135 + 270 * p)) *
274
- (r - 0.5 - thumbWeight / 2),
275
- cy +
276
- Math.sin(radian(135 + 270 * p)) *
277
- (r - 0.5 - thumbWeight / 2),
278
- )
279
- ctx.strokeStyle = thumb
280
- ctx.lineWidth = thumbWeight
281
- ctx.stroke()
282
- }
283
- }
284
- />
285
- </div>
286
- )
287
- }
295
+ // thumb
296
+ ctx.lineCap = 'round'
297
+ ctx.beginPath()
298
+ ctx.moveTo(cx, cy)
299
+ ctx.lineTo(
300
+ cx +
301
+ Math.cos(radian(135 + 270 * p)) *
302
+ (r - 0.5 - thumbWeight / 2),
303
+ cy +
304
+ Math.sin(radian(135 + 270 * p)) *
305
+ (r - 0.5 - thumbWeight / 2),
306
+ )
307
+ ctx.strokeStyle = thumb
308
+ ctx.lineWidth = thumbWeight
309
+ ctx.stroke()
310
+ }
311
+ }
312
+ />
313
+ </div>
314
+ )
315
+ },
316
+ )