feature (audio): revamp audio player - #497

This commit is contained in:
Mickael Kerjean
2023-04-12 20:33:36 +10:00
parent eac2572854
commit 149fbd9980
9 changed files with 158 additions and 40 deletions

View File

@ -1,4 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 47.607 47.607" style="enable-background:new 0 0 47.607 47.607;">
<path d="M17.991,40.976c0,3.662-2.969,6.631-6.631,6.631l0,0c-3.662,0-6.631-2.969-6.631-6.631V6.631C4.729,2.969,7.698,0,11.36,0 l0,0c3.662,0,6.631,2.969,6.631,6.631V40.976z" fill="#6F6F6F"/>
<path d="M42.877,40.976c0,3.662-2.969,6.631-6.631,6.631l0,0c-3.662,0-6.631-2.969-6.631-6.631V6.631 C29.616,2.969,32.585,0,36.246,0l0,0c3.662,0,6.631,2.969,6.631,6.631V40.976z" fill="#6F6F6F"/>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<path style="fill:#6f6f6f;fill-opacity:1;stroke-width:1.06176" d="m 320.29305,81.98884 v 345.78144 c 0,9.34336 7.64456,16.98788 16.98786,16.98788 h 33.97572 c 9.34333,0 16.98786,-7.64452 16.98786,-16.98788 V 81.98884 c 0,-9.343332 -7.64453,-16.987868 -16.98786,-16.987868 h -33.97572 c -9.3433,0 -16.98786,7.644536 -16.98786,16.987868 z m -185.87858,0 v 345.78144 c 0,9.34336 7.64453,16.98788 16.98786,16.98788 h 33.97572 c 9.3433,0 16.98786,-7.64452 16.98786,-16.98788 V 81.98884 c 0,-9.343332 -7.64456,-16.987868 -16.98786,-16.987868 h -33.97572 c -9.34333,0 -16.98786,7.644536 -16.98786,16.987868 z" />
</svg>

Before

Width:  |  Height:  |  Size: 516 B

After

Width:  |  Height:  |  Size: 678 B

View File

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 232.153 232.153" style="enable-background:new 0 0 232.153 232.153;">
<path fill="#6F6F6F" style="fill-rule:evenodd;clip-rule:evenodd;" d="M203.791,99.628L49.307,2.294c-4.567-2.719-10.238-2.266-14.521-2.266 c-17.132,0-17.056,13.227-17.056,16.578v198.94c0,2.833-0.075,16.579,17.056,16.579c4.283,0,9.955,0.451,14.521-2.267 l154.483-97.333c12.68-7.545,10.489-16.449,10.489-16.449S216.471,107.172,203.791,99.628z" />
<svg viewBox="0 0 24 24" fill="none" stroke="#6f6f6f" stroke-width="2.5" xmlns="http://www.w3.org/2000/svg">
<path d="M 8.9281724,2.5998029 C 8.2216149,2.1532873 7.0721143,2.3920918 7.0718277,3.4001971 l -0.00489,17.2050459 c -2.889e-4,1.015715 1.2121979,1.160372 1.8661307,0.789513 C 23.97574,8.7289856 23.930152,14.104463 8.9281584,2.5998029 Z" />
</svg>

Before

Width:  |  Height:  |  Size: 478 B

After

Width:  |  Height:  |  Size: 359 B

View File

@ -0,0 +1,5 @@
<svg viewBox="0 0 24 24" fill="none" stroke="#6f6f6f" stroke-width="2.5" xmlns="http://www.w3.org/2000/svg">
<path d="m 16.350225,8.193787 c 1.449626,1.933559 1.449626,5.678884 0,7.612446" />
<path d="m 19.612703,4.3875644 c 4.336919,4.1411704 4.363014,11.1109086 0,15.2248876" />
<path d="M 1.1253356,15.217897 V 8.7810328 c 0,-0.6242205 0.4871967,-1.1309917 1.0874923,-1.1309917 H 6.1125748 A 1.0657422,1.0657422 0 0 0 6.8814318,7.3183562 L 10.143909,3.6339328 c 0.68512,-0.713395 1.856345,-0.2077111 1.856345,0.8003942 v 15.131368 c 0,1.015715 -1.185362,1.517045 -1.866131,0.789513 L 6.882519,16.69145 A 1.0657422,1.0657422 0 0 0 6.1038748,16.349975 H 2.2128279 c -0.6002956,0 -1.0874923,-0.506768 -1.0874923,-1.132078 z" />
</svg>

After

Width:  |  Height:  |  Size: 741 B

View File

@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" stroke="#6f6f6f" stroke-width="2.5" xmlns="http://www.w3.org/2000/svg">
<path d="m 16.350225,8.193787 c 1.449626,1.933559 1.449626,5.678884 0,7.612446" />
<path d="M 1.1253356,15.217897 V 8.7810328 c 0,-0.6242205 0.4871967,-1.1309917 1.0874923,-1.1309917 H 6.1125748 A 1.0657422,1.0657422 0 0 0 6.8814318,7.3183562 L 10.143909,3.6339328 c 0.68512,-0.713395 1.856345,-0.2077111 1.856345,0.8003942 v 15.131368 c 0,1.015715 -1.185362,1.517045 -1.866131,0.789513 L 6.882519,16.69145 A 1.0657422,1.0657422 0 0 0 6.1038748,16.349975 H 2.2128279 c -0.6002956,0 -1.0874923,-0.506768 -1.0874923,-1.132078 z" />
</svg>

After

Width:  |  Height:  |  Size: 650 B

View File

@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" stroke="#6f6f6f" stroke-width="2.5" xmlns="http://www.w3.org/2000/svg">
<path d="m 21.983883,14.883883 -6,-5.9999995 m 6,0 -6,5.9999995" />
<path d="M 1.1253356,15.217897 V 8.7810328 c 0,-0.6242205 0.4871967,-1.1309917 1.0874923,-1.1309917 H 6.1125748 A 1.0657422,1.0657422 0 0 0 6.8814318,7.3183562 L 10.143909,3.6339328 c 0.68512,-0.713395 1.856345,-0.2077111 1.856345,0.8003942 v 15.131368 c 0,1.015715 -1.185362,1.517045 -1.866131,0.789513 L 6.882519,16.69145 A 1.0657422,1.0657422 0 0 0 6.1038748,16.349975 H 2.2128279 c -0.6002956,0 -1.0874923,-0.506768 -1.0874923,-1.132078 z" />
</svg>

After

Width:  |  Height:  |  Size: 635 B

View File

@ -42,6 +42,10 @@ import img_stop from "../assets/img/stop.svg";
import img_refresh from "../assets/img/refresh.svg";
import img_copy from "../assets/img/copy.svg";
import img_eye from "../assets/img/eye.svg";
import img_volume from "../assets/img/volume.svg";
import img_volume_mute from "../assets/img/volume_mute.svg";
import img_volume_low from "../assets/img/volume_low.svg";
export const img_placeholder = "/assets/icons/placeholder.png";
export const Icon = (props) => {
@ -141,6 +145,12 @@ export const Icon = (props) => {
img = "/assets/icons/empty_search.svg";
} else if (props.name === "eye") {
img = img_eye;
} else if (props.name === "volume") {
img = img_volume;
} else if (props.name === "volume_mute") {
img = img_volume_mute;
} else if (props.name === "volume_low") {
img = img_volume_low;
} else {
throw (new Error(`unknown icon: "${props.name}"`));
}

View File

@ -3,11 +3,14 @@ import WaveSurfer from "wavesurfer.js";
import { MenuBar } from "./menubar";
import { NgIf, Icon } from "../../components/";
import { settings_get, settings_put } from "../../helpers/";
import "./audioplayer.scss";
export function AudioPlayer({ filename, data }) {
const [isPlaying, setIsPlaying] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [purcentLoading, setPurcentLoading] = useState(0);
const [volume, setVolume] = useState(settings_get("volume") === null ? 50 : settings_get("volume"));
const [error, setError] = useState(null);
const wavesurfer = useRef(null);
@ -15,19 +18,30 @@ export function AudioPlayer({ filename, data }) {
wavesurfer.current = WaveSurfer.create({
container: "#waveform",
waveColor: "#323639",
progressColor: "#6f6f6f",
cursorColor: "#323639",
cursorWidth: 2,
height: 250,
progressColor: "#808080",
cursorColor: "#6f6f6f",
cursorWidth: 3,
height: 200,
barWidth: 1,
});
wavesurfer.current.load(data);
let $currentTime = document.getElementById("currentTime");
let $totalDuration = document.getElementById("totalDuration");
wavesurfer.current.on("ready", () => {
setIsLoading(false);
wavesurfer.current.setVolume(volume / 100);
$totalDuration.innerHTML = formatTimecode(wavesurfer.current.getDuration());
});
wavesurfer.current.on("audioprocess", () => {
$currentTime.innerHTML = formatTimecode(wavesurfer.current.getCurrentTime());
})
wavesurfer.current.on("loading", (n) => {
setPurcentLoading(n);
});
wavesurfer.current.on("error", (err) => {
setIsLoading(false);
setError(err)
setError(err);
});
return () => wavesurfer.current.destroy();
}, []);
@ -50,14 +64,26 @@ export function AudioPlayer({ filename, data }) {
e.stopPropagation();
wavesurfer.current.play();
setIsPlaying(true);
}
};
const onPause = (e) => {
e.preventDefault();
e.stopPropagation();
wavesurfer.current.pause();
setIsPlaying(false);
}
};
const onVolumeChange = (e) => {
const v = Number(e.target.value);
settings_put("volume", v);
setVolume(v);
wavesurfer.current.setVolume(v / 100);
};
const onVolumeClick = () => {
onVolumeChange({ target: { value: 0 }});
};
const formatTimecode = (seconds) => (new Date(seconds * 1000).toISOString().substr(11, 8));
return (
<div className="component_audioplayer">
@ -67,25 +93,36 @@ export function AudioPlayer({ filename, data }) {
{error}
</NgIf>
<NgIf cond={error === null}>
<NgIf cond={isLoading === true}>
<Icon name="loading" />
<div className="audioplayer_box">
<NgIf cond={isLoading}>
<div className="audioplayer_loader" style={{width: purcentLoading + "%"}}></div>
<Icon name="loading"/>
<span className="percent">{purcentLoading}%</span>
</NgIf>
<div
className="audioplayer_box"
style={{ opacity: isLoading? "0" : "1" }}>
<div className="audioplayer_control">
<NgIf cond={isPlaying === false}>
<span onClick={onPlay}>
<Icon name="play"/>
</span>
</NgIf>
<NgIf cond={isPlaying === true}>
<div id="waveform"></div>
<div className="audioplayer_control" style={{ opacity: isLoading? 0 : 1 }}>
<div className="buttons">
{
isPlaying ? (
<span onClick={onPause}>
<Icon name="pause"/>
</span>
</NgIf>
) : (
<span onClick={onPlay}>
<Icon name="play"/>
</span>
)
}
<span><Icon onClick={onVolumeClick} name={volume === 0 ? "volume_mute" : volume < 50 ? "volume_low" : "volume"}/></span>
<input onChange={onVolumeChange} type="range" min="0" max="100" value={volume}/>
</div>
<div className="timecode">
<span id="currentTime">00:00:00</span>
<span id="separator">/</span>
<span id="totalDuration">00:00:00</span>
</div>
</div>
<div id="waveform"></div>
</div>
</NgIf>
</div>

View File

@ -9,8 +9,6 @@
flex-direction: column;
flex: 1;
width: 100%;
text-align: center;
background: #525659;
height: 100%;
@ -26,21 +24,81 @@
background: #f1f1f1;
box-shadow: rgba(0, 0, 0, 0.14) 0px 4px 5px 0px, rgba(0, 0, 0, 0.12) 0px 1px 10px 0px, rgba(0, 0, 0, 0.2) 0px 2px 4px -1px;
position: relative;
border-radius: 3px;
padding: 10px 0 30px 0;
.audioplayer_control {
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
height: 30px;
width: 30px;
> div {
display: inline;
> span {
padding-top: 20px;
img {
height: 25px;
width: 25px;
cursor: pointer;
}
.buttons {
float: left;
padding-left: 15px;
margin-top: -10px;
display: flex;
> span {
margin-right: 10px;
}
input[type="range"] {
margin-left: -5px;
cursor: pointer;
width: 100%;
outline: none;
-webkit-appearance: none;
background: transparent;
&::-webkit-slider-thumb {
-webkit-appearance: none;
height: 15px;
width: 15px;
border: none;
border-radius: 50%;
background: #6f6f6f;
margin-top: -6px;
}
&::-webkit-slider-runnable-track {
width: 100%;
height: 3px;
background-color: #6f6f6f;
border-radius: 2px;
}
}
}
.timecode {
float: right;
padding-right: 25px;
margin-top: -5px;
#separator {
padding: 0 5px;
}
@media screen and (max-width: 450px) {
#separator, #currentTime { display: none; }
}
}
}
.audioplayer_loader {
height: 260px;
background: var(--color);
position: absolute;
opacity: 0.1;
top: 0;
left: 0;
}
.percent {
position: absolute;
margin: 100px 0px;
width: 120px;
font-size: 1.4rem;
}
.component_icon[alt="loading"] {
position: absolute;
margin: 50px 0px
}
}
}

View File

@ -69,6 +69,7 @@ const config = {
{ from: "locales/*.json", to: "assets/" },
{ from: "worker/*.js", to: "assets/" },
{ from: "assets/logo/*" },
{ from: "assets/img/*" },
{ from: "assets/icons/*" },
{ from: "assets/fonts/*" },
], { context: path.join(__dirname, "client") }),