Browse Source

Better voice message animation

master
Eduard Kuzmenko 2 years ago
parent
commit
7f9c298a05
  1. 75
      src/components/audio.ts
  2. 53
      src/scss/partials/_audio.scss

75
src/components/audio.ts

@ -30,6 +30,7 @@ import throttleWithRaf from "../helpers/schedulers/throttleWithRaf"; @@ -30,6 +30,7 @@ import throttleWithRaf from "../helpers/schedulers/throttleWithRaf";
import { NULL_PEER_ID } from "../lib/mtproto/mtproto_config";
import formatBytes from "../helpers/formatBytes";
import { clamp } from "../helpers/number";
import { animateSingle } from "../helpers/animation";
rootScope.addEventListener('messages_media_read', ({mids, peerId}) => {
mids.forEach(mid => {
@ -78,16 +79,7 @@ export function decodeWaveform(waveform: Uint8Array | number[]) { @@ -78,16 +79,7 @@ export function decodeWaveform(waveform: Uint8Array | number[]) {
return result;
}
function wrapVoiceMessage(audioEl: AudioElement) {
audioEl.classList.add('is-voice');
const message = audioEl.message;
const doc = appMessagesManager.getMediaFromMessage(message) as MyDocument;
if(message.pFlags.out) {
audioEl.classList.add('is-out');
}
function createWaveformBars(waveform: Uint8Array, duration: number) {
const barWidth = 2;
const barMargin = 2; //mediaSizes.isMobile ? 2 : 1;
const barHeightMin = 4; //mediaSizes.isMobile ? 3 : 2;
@ -96,22 +88,14 @@ function wrapVoiceMessage(audioEl: AudioElement) { @@ -96,22 +88,14 @@ function wrapVoiceMessage(audioEl: AudioElement) {
const minW = mediaSizes.isMobile ? 152 : 190;
const maxW = mediaSizes.isMobile ? 190 : 256;
const duration = doc.duration;
const availW = clamp(duration / 60 * maxW, minW, maxW); // mediaSizes.isMobile ? 152 : 224;
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.classList.add('audio-waveform');
svg.classList.add('audio-waveform-bars');
svg.setAttributeNS(null, 'width', '' + availW);
svg.setAttributeNS(null, 'height', '' + barHeightMax);
svg.setAttributeNS(null, 'viewBox', `0 0 ${availW} ${barHeightMax}`);
const timeDiv = document.createElement('div');
timeDiv.classList.add('audio-time');
audioEl.append(svg, timeDiv);
let waveform = (doc.attributes.find(attribute => attribute._ === 'documentAttributeAudio') as DocumentAttribute.documentAttributeAudio).waveform || new Uint8Array([]);
waveform = decodeWaveform(waveform.slice(0, 63));
//console.log('decoded waveform:', waveform);
const normValue = Math.max(...waveform);
@ -151,23 +135,55 @@ function wrapVoiceMessage(audioEl: AudioElement) { @@ -151,23 +135,55 @@ function wrapVoiceMessage(audioEl: AudioElement) {
}
}
const container = document.createElement('div');
container.classList.add('audio-waveform');
container.append(svg);
svg.insertAdjacentHTML('beforeend', html);
const rects = Array.from(svg.children) as HTMLElement[];
return {svg, container, availW};
}
function wrapVoiceMessage(audioEl: AudioElement) {
audioEl.classList.add('is-voice');
const message = audioEl.message;
const doc = appMessagesManager.getMediaFromMessage(message) as MyDocument;
if(message.pFlags.out) {
audioEl.classList.add('is-out');
}
let waveform = (doc.attributes.find(attribute => attribute._ === 'documentAttributeAudio') as DocumentAttribute.documentAttributeAudio).waveform || new Uint8Array([]);
waveform = decodeWaveform(waveform.slice(0, 63));
const {svg, container: svgContainer, availW} = createWaveformBars(waveform, doc.duration);
const fakeSvgContainer = svgContainer.cloneNode(true) as HTMLElement;
fakeSvgContainer.classList.add('audio-waveform-fake');
svgContainer.classList.add('audio-waveform-background');
const waveformContainer = document.createElement('div');
waveformContainer.classList.add('audio-waveform-container');
waveformContainer.append(svgContainer, fakeSvgContainer);
const timeDiv = document.createElement('div');
timeDiv.classList.add('audio-time');
audioEl.append(waveformContainer, timeDiv);
let progress = audioEl.querySelector('.audio-waveform') as HTMLDivElement;
let progress = svg as any as HTMLElement;
const onLoad = () => {
let audio = audioEl.audio;
const setAnimation = () => {
animateSingle(() => {
onTimeUpdate();
return !audio.paused;
}, audioEl);
};
const onTimeUpdate = () => {
const lastIndex = audio.currentTime === audio.duration ? 0 : Math.ceil(audio.currentTime / audio.duration * barCount);
//svg.children[lastIndex].setAttributeNS(null, 'fill', '#000');
//svg.children[lastIndex].classList.add('active'); #Иногда пропускает полоски..
rects.forEach((node, idx) => node.classList.toggle('active', idx < lastIndex));
//++lastIndex;
//console.log('lastIndex:', lastIndex, audio.currentTime);
//}, duration * 1000 / svg.childElementCount | 0/* 63 * duration / 10 */);
fakeSvgContainer.style.width = (audio.currentTime / audio.duration * 100) + '%';
};
if(!audio.paused || (audio.currentTime > 0 && audio.currentTime !== audio.duration)) {
@ -177,6 +193,7 @@ function wrapVoiceMessage(audioEl: AudioElement) { @@ -177,6 +193,7 @@ function wrapVoiceMessage(audioEl: AudioElement) {
const throttledTimeUpdate = throttleWithRaf(onTimeUpdate);
audioEl.addAudioListener('timeupdate', throttledTimeUpdate);
audioEl.addAudioListener('ended', throttledTimeUpdate);
audioEl.addAudioListener('play', setAnimation);
audioEl.readyPromise.then(() => {
let mousedown = false, mousemove = false;

53
src/scss/partials/_audio.scss

@ -379,29 +379,42 @@ @@ -379,29 +379,42 @@
}
&-waveform {
height: 23px;
margin-top: 1px;
/* @include respond-to(handhelds) {
margin-top: -4px;
} */
height: 100%;
//overflow: visible!important;
&-background {
rect {
opacity: .3;
@include hover() {
opacity: 1;
}
}
}
&-fake {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 0;
overflow: hidden;
}
&-container {
position: relative;
height: 23px;
margin-top: 1px;
}
rect {
fill: var(--primary-color);
//overflow: visible!important;
opacity: .3;
@include hover() {
opacity: 1;
}
&.active,
.audio.is-unread:not(.is-out) .audio-toggle:not(.playing) + & {
opacity: 1;
}
}
/* @include respond-to(handhelds) {
margin-top: -4px;
} */
}
// //&.audio-54 {
@ -499,7 +512,7 @@ @@ -499,7 +512,7 @@
margin-top: .3125rem;
}
.audio-waveform {
.audio-waveform-container {
height: 16px;
margin-top: 0;
}
@ -516,6 +529,10 @@ @@ -516,6 +529,10 @@
margin-left: .375rem;
}
}
&:not(.is-out) .audio-toggle:not(.playing) + .audio-waveform-container .audio-waveform-background rect {
opacity: 1;
}
}
}

Loading…
Cancel
Save