chore: slider to change width and height in image
This commit is contained in:
@@ -6,5 +6,6 @@
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,40 @@ import './index.less';
|
||||
interface AudioAnimationProps {
|
||||
width: number;
|
||||
height: number;
|
||||
scaleFactor?: number;
|
||||
analyserData: {
|
||||
data: Uint8Array;
|
||||
analyser: any;
|
||||
};
|
||||
}
|
||||
|
||||
const AudioAnimation: React.FC<AudioAnimationProps> = (props) => {
|
||||
const { width, height, analyserData } = props;
|
||||
const AudioAnimation: React.FC<AudioAnimationProps> = ({
|
||||
width,
|
||||
height,
|
||||
scaleFactor = 1.2,
|
||||
analyserData
|
||||
}) => {
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(null);
|
||||
const animationId = React.useRef<number>(0);
|
||||
const isScaled = React.useRef<boolean>(false);
|
||||
const oscillationOffset = React.useRef(0);
|
||||
const direction = React.useRef(1);
|
||||
const maxBarCount = 128;
|
||||
|
||||
const calculateJitter = (
|
||||
i: number,
|
||||
timestamp: number,
|
||||
baseHeight: number,
|
||||
minJitter: number,
|
||||
jitterAmplitude: number
|
||||
) => {
|
||||
//
|
||||
const jitterFactor = Math.sin(timestamp / 200 + i) * 0.5 + 0.5;
|
||||
const jitter =
|
||||
minJitter +
|
||||
jitterFactor * (jitterAmplitude - minJitter) * (baseHeight / maxBarCount);
|
||||
return jitter;
|
||||
};
|
||||
|
||||
const startAudioVisualization = () => {
|
||||
if (!canvasRef.current || !analyserData.data?.length) return;
|
||||
@@ -33,42 +54,51 @@ const AudioAnimation: React.FC<AudioAnimationProps> = (props) => {
|
||||
isScaled.current = true;
|
||||
}
|
||||
|
||||
const barWidth = 3;
|
||||
const barSpacing = 2;
|
||||
const centerX = HEIGHT / 2;
|
||||
const barWidth = 4;
|
||||
const barSpacing = 6;
|
||||
const centerLine = Math.floor(HEIGHT / 2);
|
||||
const jitterAmplitude = 60; // 最大抖动幅度
|
||||
const minJitter = 15; // 最小抖动幅度
|
||||
|
||||
const frameInterval = 2;
|
||||
let frameCount = 0;
|
||||
const jitterAmplitude = 40;
|
||||
const minJitter = 10;
|
||||
|
||||
canvasCtx.fillStyle = '#0073EF';
|
||||
let lastFrameTime = 0;
|
||||
|
||||
const draw = () => {
|
||||
frameCount++;
|
||||
if (frameCount % frameInterval !== 0) {
|
||||
const gradient = canvasCtx.createLinearGradient(0, 0, 0, HEIGHT);
|
||||
gradient.addColorStop(0, '#007BFF');
|
||||
gradient.addColorStop(1, '#0069DA');
|
||||
canvasCtx.fillStyle = gradient;
|
||||
|
||||
const draw = (timestamp: number) => {
|
||||
const elapsed = timestamp - lastFrameTime;
|
||||
if (elapsed < 16) {
|
||||
animationId.current = requestAnimationFrame(draw);
|
||||
return;
|
||||
}
|
||||
lastFrameTime = timestamp;
|
||||
|
||||
analyserData.analyser?.current?.getByteFrequencyData(analyserData.data);
|
||||
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
|
||||
|
||||
const barCount = analyserData.data.length;
|
||||
const barCount = Math.min(maxBarCount, analyserData.data.length);
|
||||
const totalWidth = barCount * (barWidth + barSpacing) - barSpacing;
|
||||
let x = centerX - totalWidth / 2 + oscillationOffset.current;
|
||||
oscillationOffset.current += direction.current * 0.5;
|
||||
let x = WIDTH / 2 - totalWidth / 2 + oscillationOffset.current;
|
||||
|
||||
if (oscillationOffset.current > 20 || oscillationOffset.current < -20) {
|
||||
oscillationOffset.current += direction.current;
|
||||
if (Math.abs(oscillationOffset.current) > 20) {
|
||||
direction.current *= -1;
|
||||
}
|
||||
|
||||
for (let i = 0; i < barCount; i++) {
|
||||
const baseHeight = Math.floor(analyserData.data[i] / 2);
|
||||
const jitter =
|
||||
minJitter +
|
||||
Math.round((Math.random() - 0.5) * (jitterAmplitude - minJitter));
|
||||
const barHeight = baseHeight + jitter;
|
||||
const baseHeight = Math.floor(analyserData.data[i] / 2) * scaleFactor;
|
||||
|
||||
const jitter = calculateJitter(
|
||||
i,
|
||||
timestamp,
|
||||
baseHeight,
|
||||
minJitter,
|
||||
jitterAmplitude
|
||||
);
|
||||
const barHeight = baseHeight + Math.round(jitter);
|
||||
|
||||
const topY = Math.round(centerLine - barHeight / 2);
|
||||
const bottomY = Math.round(centerLine + barHeight / 2);
|
||||
@@ -87,21 +117,29 @@ const AudioAnimation: React.FC<AudioAnimationProps> = (props) => {
|
||||
animationId.current = requestAnimationFrame(draw);
|
||||
};
|
||||
|
||||
draw();
|
||||
draw(performance.now());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!analyserData.data?.length || !analyserData.analyser.current) {
|
||||
canvasRef.current
|
||||
?.getContext('2d')
|
||||
?.clearRect(0, 0, width * 2, height * 2);
|
||||
const clearCanvas = () => {
|
||||
if (canvasRef.current) {
|
||||
const ctx = canvasRef.current.getContext('2d');
|
||||
if (ctx) ctx.clearRect(0, 0, width * 2, height * 2);
|
||||
}
|
||||
};
|
||||
|
||||
if (!analyserData.data?.length || !analyserData.analyser?.current) {
|
||||
clearCanvas();
|
||||
cancelAnimationFrame(animationId.current);
|
||||
animationId.current = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
startAudioVisualization();
|
||||
|
||||
return () => {
|
||||
if (animationId.current) cancelAnimationFrame(animationId.current);
|
||||
cancelAnimationFrame(animationId.current);
|
||||
clearCanvas();
|
||||
};
|
||||
}, [analyserData, width, height]);
|
||||
|
||||
|
||||
@@ -75,6 +75,12 @@
|
||||
.file-name {
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ant-slider-horizontal {
|
||||
|
||||
@@ -53,7 +53,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
});
|
||||
const [playOn, setPlayOn] = React.useState<boolean>(false);
|
||||
const [speakerOn, setSpeakerOn] = React.useState<boolean>(false);
|
||||
const [volume, setVolume] = React.useState<number>(0.5);
|
||||
const [volume, setVolume] = React.useState<number>(1);
|
||||
const [speed, setSpeed] = React.useState<number>(defaultSpeed);
|
||||
const timer = React.useRef<any>(null);
|
||||
|
||||
@@ -98,12 +98,12 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
}, []);
|
||||
|
||||
const handlePlay = useCallback(() => {
|
||||
setPlayOn(!playOn);
|
||||
if (playOn) {
|
||||
audioRef.current?.pause();
|
||||
} else {
|
||||
audioRef.current?.play();
|
||||
}
|
||||
setPlayOn(!playOn);
|
||||
}, [playOn]);
|
||||
|
||||
const handleFormatVolume = (val?: number) => {
|
||||
@@ -118,10 +118,12 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
setVolume(round(value, 2));
|
||||
}, []);
|
||||
|
||||
const initPlayerConfig = useCallback(() => {
|
||||
audioRef.current!.volume = volume;
|
||||
audioRef.current!.playbackRate = speed;
|
||||
}, []);
|
||||
const initPlayerConfig = () => {
|
||||
if (audioRef.current) {
|
||||
audioRef.current!.volume = volume;
|
||||
audioRef.current!.playbackRate = speed;
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoadedMetadata = useCallback(
|
||||
(data: any) => {
|
||||
@@ -168,6 +170,10 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleOnLoad = (e: any) => {
|
||||
console.log('onload', e);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (audioRef.current) {
|
||||
initPlayerConfig();
|
||||
@@ -195,6 +201,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
<Slider
|
||||
tooltip={{ open: false }}
|
||||
min={0}
|
||||
step={1}
|
||||
max={audioState.duration}
|
||||
value={audioState.currentTime}
|
||||
onChange={handleCurrentChange}
|
||||
@@ -203,23 +210,6 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
<span className="time">{formatTime(audioState.duration)}</span>
|
||||
</div>
|
||||
<div className="controls">
|
||||
{/* <Tooltip
|
||||
overlayInnerStyle={{
|
||||
backgroundColor: 'var(--color-white-1)'
|
||||
}}
|
||||
arrow={false}
|
||||
title={
|
||||
<CheckButtons
|
||||
options={speedOptions}
|
||||
onChange={handleSeepdChange}
|
||||
size="small"
|
||||
></CheckButtons>
|
||||
}
|
||||
>
|
||||
<span style={{ cursor: 'pointer' }}>
|
||||
{speed ? `${speed}x` : '1x'}
|
||||
</span>
|
||||
</Tooltip> */}
|
||||
<Tooltip
|
||||
title={intl.formatMessage({
|
||||
id: 'playground.audio.button.slow'
|
||||
@@ -276,38 +266,6 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <span className="speaker">
|
||||
<Button
|
||||
size="middle"
|
||||
type="text"
|
||||
icon={
|
||||
volume > 0 ? (
|
||||
<IconFont
|
||||
type="icon-SpeakerHigh"
|
||||
style={{ fontSize: '22px' }}
|
||||
></IconFont>
|
||||
) : (
|
||||
<IconFont
|
||||
type="icon-speaker-slash"
|
||||
style={{ fontSize: '22px' }}
|
||||
></IconFont>
|
||||
)
|
||||
}
|
||||
></Button>
|
||||
{speakerOn && (
|
||||
<Slider
|
||||
tooltip={{ formatter: handleFormatVolume }}
|
||||
style={{ height: '100px' }}
|
||||
className="volume-slider"
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01}
|
||||
value={volume}
|
||||
vertical
|
||||
onChange={handleVolumeChange}
|
||||
/>
|
||||
)}
|
||||
</span> */}
|
||||
</div>
|
||||
<audio
|
||||
controls
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
backdrop-filter: blur(100px);
|
||||
filter: blur(100px);
|
||||
backdrop-filter: blur(100px);
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
@@ -109,7 +109,13 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<Progress percent={progress} type="circle" />
|
||||
<Progress
|
||||
percent={progress}
|
||||
type="dashboard"
|
||||
steps={{ count: 50, gap: 2 }}
|
||||
format={() => <span className="font-size-20">{progress}%</span>}
|
||||
trailColor="var(--ant-color-fill-secondary)"
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Checkbox, Slider } from 'antd';
|
||||
import { Checkbox } from 'antd';
|
||||
import SealInput from '../seal-input';
|
||||
import SealSelect from '../seal-select';
|
||||
import Slider from '../seal-slider';
|
||||
|
||||
const components: {
|
||||
InputNumber: typeof SealInput.Number;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useIntl } from '@umijs/max';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import LabelInfo from './components/label-info';
|
||||
import componentsMap from './config/components';
|
||||
|
||||
const FieldComponent: React.FC<ParamsSchema> = (props) => {
|
||||
const intl = useIntl();
|
||||
const { type, label, attrs, style, value, ...rest } = props;
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { INPUT_WIDTH } from '@/constants';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Form,
|
||||
InputNumber,
|
||||
Slider,
|
||||
Tooltip,
|
||||
type SliderSingleProps
|
||||
} from 'antd';
|
||||
import React from 'react';
|
||||
import FieldWrapper from './field-wrapper';
|
||||
import SliderStyles from './styles/slider.less';
|
||||
|
||||
interface SealSliderProps extends SliderSingleProps {
|
||||
required?: boolean;
|
||||
label?: React.ReactNode;
|
||||
labelWidth?: number | string;
|
||||
description?: string;
|
||||
isInFormItems?: boolean;
|
||||
inputnumber?: boolean;
|
||||
checkStatus?: 'success' | 'error' | 'warning' | '';
|
||||
}
|
||||
|
||||
const SealSlider: React.FC<SealSliderProps> = (props) => {
|
||||
const {
|
||||
label,
|
||||
value,
|
||||
required,
|
||||
description,
|
||||
isInFormItems = true,
|
||||
max,
|
||||
min,
|
||||
step,
|
||||
defaultValue,
|
||||
checkStatus,
|
||||
inputnumber = false,
|
||||
labelWidth,
|
||||
tooltip = { open: false },
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
let status = '';
|
||||
if (isInFormItems) {
|
||||
const statusData = Form?.Item?.useStatus?.();
|
||||
status = statusData?.status || '';
|
||||
}
|
||||
|
||||
const handleChange = (value: number) => {
|
||||
props.onChange?.(value);
|
||||
};
|
||||
|
||||
const handleInput = (value: number | null) => {
|
||||
const newValue = value || 0;
|
||||
props.onChange?.(newValue);
|
||||
};
|
||||
|
||||
const renderLabel = React.useMemo(() => {
|
||||
return (
|
||||
<span
|
||||
className={SliderStyles['slider-label']}
|
||||
style={{ width: labelWidth || INPUT_WIDTH.mini }}
|
||||
>
|
||||
<span className="text">
|
||||
{description ? (
|
||||
<Tooltip title={description}>
|
||||
<span> {label}</span>
|
||||
<span className="m-l-5">
|
||||
<InfoCircleOutlined />
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span>{label}</span>
|
||||
)}
|
||||
</span>
|
||||
|
||||
{inputnumber ? (
|
||||
<InputNumber
|
||||
className="label-val"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
value={value}
|
||||
controls={false}
|
||||
onChange={handleInput}
|
||||
></InputNumber>
|
||||
) : (
|
||||
<span className="val">{value}</span>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
}, [label, labelWidth, description, value, max, min, step, defaultValue]);
|
||||
return (
|
||||
<FieldWrapper
|
||||
required={required}
|
||||
status={checkStatus || status}
|
||||
label={renderLabel}
|
||||
style={{ padding: '20px 2px 0' }}
|
||||
variant="borderless"
|
||||
>
|
||||
<Slider
|
||||
{...rest}
|
||||
defaultValue={defaultValue}
|
||||
max={max}
|
||||
min={min}
|
||||
step={step}
|
||||
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
|
||||
tooltip={tooltip}
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
></Slider>
|
||||
</FieldWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default SealSlider;
|
||||
@@ -0,0 +1,24 @@
|
||||
:local(.slider-label) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
|
||||
:global(.val) {
|
||||
color: var(--ant-color-text);
|
||||
}
|
||||
|
||||
:global(.label-val) {
|
||||
position: absolute !important;
|
||||
top: -14px;
|
||||
right: -14px;
|
||||
width: 80px;
|
||||
border-radius: var(--border-radius-base);
|
||||
text-align: center;
|
||||
border: 1px solid var(--ant-color-border) !important;
|
||||
|
||||
:global(.ant-input-number-input) {
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef
|
||||
} from 'react';
|
||||
import useWavesurfer from './hooks/use-wavesurfer';
|
||||
import WaveSurfer, { WaveSurferOptions } from 'wavesurfer.js';
|
||||
|
||||
interface AudioPlayerProps {
|
||||
autoplay: boolean;
|
||||
@@ -15,27 +16,104 @@ interface AudioPlayerProps {
|
||||
width?: number;
|
||||
onReady?: () => void;
|
||||
onClick?: (value: number) => void;
|
||||
onFinish?: () => void;
|
||||
onAnalyse?: (analyseData: any, frequencyBinCount: any) => void;
|
||||
}
|
||||
|
||||
const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
const AudioPlayer: React.FC<
|
||||
AudioPlayerProps & Omit<WaveSurferOptions, 'container'>
|
||||
> = forwardRef((props, ref) => {
|
||||
const { autoplay, audioUrl, speed = 1, ...rest } = props;
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
const {
|
||||
createWavesurfer,
|
||||
play,
|
||||
pause,
|
||||
duration,
|
||||
destroyWavesurfer,
|
||||
wavesurfer
|
||||
} = useWavesurfer({
|
||||
container,
|
||||
autoplay: autoplay,
|
||||
url: audioUrl,
|
||||
audioRate: speed,
|
||||
onReady: props.onReady,
|
||||
onClick: props.onClick,
|
||||
...rest
|
||||
});
|
||||
const wavesurfer = useRef<WaveSurfer | null>(null);
|
||||
const container = useRef<any>(null);
|
||||
const audioContext = useRef<any>(null);
|
||||
const analyser = useRef<any>(null);
|
||||
const dataArray = useRef<any>(null);
|
||||
const audioStream = useRef<any>(null);
|
||||
const mediaElement = useRef<any>(null);
|
||||
|
||||
const initAudioContext = useCallback(() => {
|
||||
audioContext.current = new (window.AudioContext ||
|
||||
window.webkitAudioContext)();
|
||||
|
||||
analyser.current = audioContext.current.createAnalyser();
|
||||
analyser.current.fftSize = 512;
|
||||
dataArray.current = new Uint8Array(analyser.current.frequencyBinCount);
|
||||
}, []);
|
||||
|
||||
const generateVisualData = useCallback(() => {
|
||||
const source = audioContext.current.createMediaElementSource(
|
||||
mediaElement.current
|
||||
);
|
||||
source.connect(analyser.current);
|
||||
analyser.current.connect(audioContext.current.destination);
|
||||
}, []);
|
||||
|
||||
const listenEvents = () => {
|
||||
wavesurfer.current?.on('ready', () => {
|
||||
props.onReady?.();
|
||||
});
|
||||
|
||||
wavesurfer.current?.on('click', (value) => {
|
||||
props.onClick?.(value);
|
||||
});
|
||||
wavesurfer.current?.on('finish', () => {
|
||||
props.onFinish?.();
|
||||
});
|
||||
wavesurfer.current?.on('play', () => {
|
||||
// analyser.current?.getByteFrequencyData(dataArray.current);
|
||||
// props.onAnalyse?.(dataArray.current, analyser);
|
||||
});
|
||||
};
|
||||
|
||||
const createWavesurfer = () => {
|
||||
wavesurfer.current = WaveSurfer.create({
|
||||
container: container.current,
|
||||
url: audioUrl,
|
||||
autoplay: autoplay,
|
||||
audioRate: speed,
|
||||
waveColor: '#4096ff',
|
||||
progressColor: 'rgb(100, 0, 100)',
|
||||
height: 60,
|
||||
barWidth: 2,
|
||||
barGap: 1,
|
||||
barRadius: 2,
|
||||
interact: true,
|
||||
cursorWidth: 0,
|
||||
...rest
|
||||
});
|
||||
|
||||
mediaElement.current = wavesurfer.current?.getMediaElement();
|
||||
|
||||
initAudioContext();
|
||||
generateVisualData();
|
||||
listenEvents();
|
||||
};
|
||||
|
||||
const destroyWavesurfer = () => {
|
||||
if (wavesurfer.current) {
|
||||
wavesurfer.current.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
const play = () => {
|
||||
if (wavesurfer.current) {
|
||||
wavesurfer.current.play();
|
||||
}
|
||||
};
|
||||
|
||||
const duration = () => {
|
||||
if (wavesurfer.current) {
|
||||
return wavesurfer.current.getDuration();
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const pause = () => {
|
||||
if (wavesurfer.current) {
|
||||
wavesurfer.current.pause();
|
||||
}
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
@@ -46,13 +124,13 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (container.current) {
|
||||
if (container.current && audioUrl) {
|
||||
createWavesurfer();
|
||||
}
|
||||
return () => {
|
||||
destroyWavesurfer();
|
||||
};
|
||||
}, [container.current]);
|
||||
}, [audioUrl, container.current]);
|
||||
return <div ref={container} className="audio-container"></div>;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import { useRef } from 'react';
|
||||
import WaveSurfer from 'wavesurfer.js';
|
||||
|
||||
interface Options {
|
||||
container: React.RefObject<HTMLDivElement>;
|
||||
waveColor?: string;
|
||||
progressColor?: string;
|
||||
url: string;
|
||||
barWidth?: number;
|
||||
barGap?: number;
|
||||
barRadius?: number;
|
||||
autoplay?: boolean;
|
||||
audioRate?: number;
|
||||
onReady?: () => void;
|
||||
onClick: (value: number) => void;
|
||||
}
|
||||
const useWavesurfer = (options: Options) => {
|
||||
const wavesurfer = useRef<WaveSurfer | null>(null);
|
||||
|
||||
const { container, url, ...rest } = options;
|
||||
|
||||
const createWavesurfer = () => {
|
||||
if (!container.current) {
|
||||
return;
|
||||
}
|
||||
if (wavesurfer.current) {
|
||||
wavesurfer.current.destroy();
|
||||
}
|
||||
wavesurfer.current = WaveSurfer.create({
|
||||
container: container.current,
|
||||
waveColor: '#4096ff',
|
||||
progressColor: 'rgb(100, 0, 100)',
|
||||
url: url,
|
||||
height: 60,
|
||||
barWidth: 2,
|
||||
barGap: 1,
|
||||
barRadius: 2,
|
||||
interact: true,
|
||||
cursorWidth: 0,
|
||||
...rest
|
||||
});
|
||||
wavesurfer.current?.on('ready', () => {
|
||||
options.onReady?.();
|
||||
});
|
||||
|
||||
wavesurfer.current?.on('click', (value) => {
|
||||
options.onClick?.(value);
|
||||
});
|
||||
};
|
||||
|
||||
const destroyWavesurfer = () => {
|
||||
if (wavesurfer.current) {
|
||||
wavesurfer.current.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
const play = () => {
|
||||
if (wavesurfer.current) {
|
||||
wavesurfer.current.play();
|
||||
}
|
||||
};
|
||||
|
||||
const duration = () => {
|
||||
if (wavesurfer.current) {
|
||||
return wavesurfer.current.getDuration();
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const pause = () => {
|
||||
if (wavesurfer.current) {
|
||||
wavesurfer.current.pause();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
createWavesurfer,
|
||||
play,
|
||||
pause,
|
||||
wavesurfer,
|
||||
duration,
|
||||
destroyWavesurfer
|
||||
};
|
||||
};
|
||||
|
||||
export default useWavesurfer;
|
||||
@@ -37,9 +37,13 @@ interface SpeechContentProps {
|
||||
const SpeechItem: React.FC<SpeechContentProps> = (props) => {
|
||||
const intl = useIntl();
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [isPlay, setIsPlay] = useState(false);
|
||||
const [isPlay, setIsPlay] = useState(props.autoplay);
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [currentTime, setCurrentTime] = useState(0);
|
||||
const [audioChunks, setAudioChunks] = useState<any>({
|
||||
data: [],
|
||||
analyser: null
|
||||
});
|
||||
const ref = useRef<any>(null);
|
||||
|
||||
const handlePlay = () => {
|
||||
@@ -52,8 +56,13 @@ const SpeechItem: React.FC<SpeechContentProps> = (props) => {
|
||||
setIsPlay(true);
|
||||
};
|
||||
|
||||
const handleCollapse = () => {
|
||||
setCollapsed(!collapsed);
|
||||
const handleOnAnalyse = (data: any, analyser: any) => {
|
||||
setAudioChunks((pre: any) => {
|
||||
return {
|
||||
data: data,
|
||||
analyser: analyser
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const handleReay = () => {
|
||||
@@ -80,6 +89,13 @@ const SpeechItem: React.FC<SpeechContentProps> = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="speech-item">
|
||||
{/* {isPlay && (
|
||||
<AudioAnimation
|
||||
height={82}
|
||||
width={500}
|
||||
analyserData={audioChunks}
|
||||
></AudioAnimation>
|
||||
)} */}
|
||||
<div className="voice">
|
||||
<IconFont type="icon-user_voice" className="font-size-16" />
|
||||
</div>
|
||||
@@ -89,6 +105,8 @@ const SpeechItem: React.FC<SpeechContentProps> = (props) => {
|
||||
audioUrl={props.audioUrl}
|
||||
onReady={handleReay}
|
||||
onClick={handleOnClick}
|
||||
onFinish={() => setIsPlay(false)}
|
||||
onAnalyse={handleOnAnalyse}
|
||||
ref={ref}
|
||||
></AudioPlayer>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user