Skip to content

Commit bcce1fc

Browse files
committed
feat(LFO): realize delay effect
1 parent a6011e2 commit bcce1fc

2 files changed

Lines changed: 46 additions & 23 deletions

File tree

  • example/src/components/visualizaion
  • packages/components/visualization/LFO

example/src/components/visualizaion/EchoLFO.tsx

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,40 @@ import React from 'react'
22
import { LFO, Knob } from '@echo-ui'
33

44
export const EchoLFO = () => {
5-
const [speed, setSpeed] = React.useState(0.1)
6-
const [frequency, setFrequency] = React.useState(0.1)
5+
const [frequency, setFrequency] = React.useState(0)
6+
const [speed, setSpeed] = React.useState(0)
7+
const [delay, setDelay] = React.useState(0)
78

89
return (
910
<section className="h-32 w-2/3 mb-20">
10-
<LFO speed={speed} frequency={frequency} />
11+
<LFO speed={speed} frequency={frequency} delay={delay} lineWidth={5} />
1112

12-
<Knob.Group size={50} trackWidth={3}>
13+
<Knob.Group size={50} trackWidth={3} min={0} max={1} step={0.1}>
1314
<Knob
1415
value={frequency}
1516
onChange={setFrequency}
16-
min={0}
17-
max={1}
18-
step={0.1}
1917
topLabel="Frequency"
2018
bottomLabel={frequency * 100 + '%'}
2119
/>
2220

2321
<Knob
22+
className="mr-3"
2423
value={speed}
2524
onChange={setSpeed}
26-
min={0.1}
27-
max={1}
28-
step={0.1}
2925
topLabel="Speed"
3026
bottomLabel={speed * 100 + '%'}
3127
/>
28+
29+
<Knob
30+
value={delay}
31+
min={0}
32+
max={100}
33+
step={1}
34+
sensitivity={8}
35+
onChange={setDelay}
36+
topLabel="Delay"
37+
bottomLabel={delay}
38+
/>
3239
</Knob.Group>
3340
</section>
3441
)

packages/components/visualization/LFO/LFO.tsx

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as d3 from 'd3'
2-
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'
2+
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'
33
import { useResizeObserver } from '../../../lib/hooks/useResizeObserver'
44
import { cn, validValue } from '../../../lib/utils'
55
import { LFOProps, LFORef } from './types'
@@ -21,7 +21,7 @@ export const LFO = forwardRef<LFORef, LFOProps>((props, ref) => {
2121
const {
2222
type = TYPE,
2323
frequency: _frequency = FREQUENCY,
24-
delay = DELAY,
24+
delay: _delay = DELAY,
2525
speed: _speed = SPEED,
2626
min = MIN,
2727
max = MAX,
@@ -30,15 +30,21 @@ export const LFO = forwardRef<LFORef, LFOProps>((props, ref) => {
3030
...restProps
3131
} = props
3232

33-
const speed = validValue(_speed, 0.1, 1)
3433
const frequency = validValue(_frequency, 0, 1)
34+
const speed = validValue(_speed, 0.1, 1)
35+
const delay = validValue(_delay, 1, 100)
3536

3637
const LFORef = useRef<LFORef | null>(null)
3738
const xScale = useRef<d3.ScaleLinear<number, number> | null>(null)
3839
const yScale = useRef<d3.ScaleLinear<number, number> | null>(null)
3940

4041
useImperativeHandle(ref, () => LFORef.current as LFORef)
4142

43+
useEffect(() => {
44+
generateScales()
45+
generateWave()
46+
}, [speed, frequency, delay])
47+
4248
const dimensions = useResizeObserver(LFORef, WIDTH, HEIGHT, () => {
4349
generateScales()
4450
generateWave()
@@ -54,11 +60,6 @@ export const LFO = forwardRef<LFORef, LFOProps>((props, ref) => {
5460
yScale.current = d3.scaleLinear().domain([min, max]).range([height, 0])
5561
}
5662

57-
useEffect(() => {
58-
generateScales()
59-
generateWave()
60-
}, [speed, frequency])
61-
6263
const generateWave = () => {
6364
generateSineWave()
6465
}
@@ -67,19 +68,32 @@ export const LFO = forwardRef<LFORef, LFOProps>((props, ref) => {
6768
if (type !== 'sine') return
6869

6970
const { width, height } = dimensions.current
70-
7171
const svg = d3.select(LFORef.current).select('svg').attr('width', width).attr('height', height)
7272
svg.selectAll('*').remove()
7373

74-
const data = d3.range(0, 4 * Math.PI * (speed * 10), 0.01).map((x) => ({
75-
x,
76-
y: Math.sin(x) * frequency * (max - min) * 0.5 + (max + min) / 2,
77-
}))
74+
if (delay > 0) {
75+
svg
76+
.append('line')
77+
.attr('x1', 0)
78+
.attr('y1', height / 2)
79+
.attr('x2', delay)
80+
.attr('y2', height / 2)
81+
.attr('stroke', lineColor)
82+
.attr('stroke-width', lineWidth)
83+
}
84+
85+
const data = d3.range(0, 4 * Math.PI * (speed * 10), 0.01).map((x) => {
86+
return {
87+
x,
88+
y: Math.sin(x) * frequency * (max - min) * 0.5 + (max + min) / 2,
89+
}
90+
})
7891

7992
const line = d3
8093
.line<{ x: number; y: number }>()
8194
.x((d) => xScale.current!(d.x))
8295
.y((d) => yScale.current!(d.y))
96+
.curve(d3.curveBasis)
8397

8498
svg
8599
.append('path')
@@ -88,6 +102,8 @@ export const LFO = forwardRef<LFORef, LFOProps>((props, ref) => {
88102
.attr('stroke', lineColor)
89103
.attr('stroke-width', lineWidth)
90104
.attr('d', line)
105+
// -1 to fix the gap between the line and the delay line
106+
.attr('transform', `translate(${delay - 0.8}, 0)`)
91107
}
92108

93109
const { base, svg } = useStyle()

0 commit comments

Comments
 (0)