Browse Source

Reimplemented round videos

master
Eduard Kuzmenko 4 years ago
parent
commit
8fe6968c35
  1. 17
      src/components/animationIntersector.ts
  2. 4
      src/components/appMediaPlaybackController.ts
  3. 179
      src/components/wrappers.ts
  4. 59
      src/lib/mediaPlayer.ts
  5. 6
      src/scss/partials/_chatBubble.scss
  6. 49
      src/scss/partials/_ckin.scss
  7. 42
      src/scss/style.scss

17
src/components/animationIntersector.ts

@ -19,6 +19,7 @@ export class AnimationIntersector { @@ -19,6 +19,7 @@ export class AnimationIntersector {
private onlyOnePlayableGroup: string = '';
private intersectionLockedGroups: {[group: string]: true} = {};
private videosLocked = false;
constructor() {
this.observer = new IntersectionObserver((entries) => {
@ -52,6 +53,20 @@ export class AnimationIntersector { @@ -52,6 +53,20 @@ export class AnimationIntersector {
}
}
});
rootScope.on('audio_play', ({doc}) => {
if(doc.type === 'round') {
this.videosLocked = true;
this.checkAnimations();
}
});
rootScope.on('audio_pause', () => {
if(this.videosLocked) {
this.videosLocked = false;
this.checkAnimations();
}
});
}
public getAnimations(element: HTMLElement) {
@ -132,7 +147,7 @@ export class AnimationIntersector { @@ -132,7 +147,7 @@ export class AnimationIntersector {
return;
}
if(blurred || (this.onlyOnePlayableGroup && this.onlyOnePlayableGroup !== group)) {
if(blurred || (this.onlyOnePlayableGroup && this.onlyOnePlayableGroup !== group) || (animation instanceof HTMLVideoElement && this.videosLocked)) {
if(!animation.paused) {
//console.warn('pause animation:', animation);
animation.pause();

4
src/components/appMediaPlaybackController.ts

@ -177,7 +177,7 @@ class AppMediaPlaybackController { @@ -177,7 +177,7 @@ class AppMediaPlaybackController {
media.safariBuffering = value;
}
onPause = (e: Event) => {
onPause = (e?: Event) => {
/* const target = e.target as HTMLMediaElement;
if(!isInDOM(target)) {
this.container.append(target);
@ -188,7 +188,7 @@ class AppMediaPlaybackController { @@ -188,7 +188,7 @@ class AppMediaPlaybackController {
rootScope.broadcast('audio_pause');
};
onEnded = (e: Event) => {
onEnded = (e?: Event) => {
this.onPause(e);
//console.log('on media end');

179
src/components/wrappers.ts

@ -30,6 +30,7 @@ import appImManager from '../lib/appManagers/appImManager'; @@ -30,6 +30,7 @@ import appImManager from '../lib/appManagers/appImManager';
import { SearchSuperContext } from './appSearchSuper.';
import rootScope from '../lib/rootScope';
import { onVideoLoad } from '../helpers/files';
import { animateSingle } from '../helpers/animation';
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
@ -55,26 +56,24 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -55,26 +56,24 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
let spanTime: HTMLElement;
if(!noInfo) {
if(doc.type !== 'round') {
spanTime = document.createElement('span');
spanTime.classList.add('video-time');
container.append(spanTime);
spanTime = document.createElement('span');
spanTime.classList.add('video-time');
container.append(spanTime);
if(doc.type !== 'gif') {
spanTime.innerText = (doc.duration + '').toHHMMSS(false);
if(doc.type !== 'gif') {
spanTime.innerText = (doc.duration + '').toHHMMSS(false);
if(!noPlayButton) {
if(canAutoplay) {
spanTime.classList.add('tgico', 'can-autoplay');
} else {
const spanPlay = document.createElement('span');
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
container.append(spanPlay);
}
if(!noPlayButton && doc.type !== 'round') {
if(canAutoplay) {
spanTime.classList.add('tgico', 'can-autoplay');
} else {
const spanPlay = document.createElement('span');
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
container.append(spanPlay);
}
} else {
spanTime.innerText = 'GIF';
}
} else {
spanTime.innerText = 'GIF';
}
}
@ -108,80 +107,110 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -108,80 +107,110 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
video.remove();
} */
const video = /* doc.type === 'round' ? appMediaPlaybackController.addMedia(message.peerId, doc, message.mid) as HTMLVideoElement : */document.createElement('video');
const video = document.createElement('video');
video.classList.add('media-video');
video.muted = true;
video.setAttribute('playsinline', 'true');
video.muted = true;
if(doc.type === 'round') {
//video.classList.add('z-depth-1');
const globalVideo = appMediaPlaybackController.addMedia(message.peerId, doc, message.mid);
const globalVideo = appMediaPlaybackController.addMedia(message.peerId, doc, message.mid) as HTMLVideoElement;
const divRound = document.createElement('div');
divRound.classList.add('media-round', 'z-depth-1');
divRound.innerHTML = `<svg class="progress-ring" width="200px" height="200px">
<circle class="progress-ring__circle" stroke="white" stroke-opacity="0.3" stroke-width="3.5" cx="100" cy="100" r="93" fill="transparent" transform="rotate(-90, 100, 100)"/>
</svg>`;
const circle = divRound.querySelector('.progress-ring__circle') as SVGCircleElement;
const radius = circle.r.baseVal.value;
const circumference = 2 * Math.PI * radius;
circle.style.strokeDasharray = circumference + ' ' + circumference;
circle.style.strokeDashoffset = '' + circumference;
spanTime.classList.add('tgico');
video.classList.add('z-depth-1');
const canvas = document.createElement('canvas');
canvas.width = canvas.height = doc.w/* * window.devicePixelRatio */;
onVideoLoad(video).then(() => {
if(globalVideo.currentTime !== globalVideo.duration) {
video.currentTime = globalVideo.currentTime;
}
if(!globalVideo.paused) {
// с закоментированными настройками - хром выключал видео при скролле, для этого нужно было включить видео - выйти из диалога, зайти заново и проскроллить вверх
//video.autoplay = true;
//video.loop = false;
video.play();
}
});
divRound.prepend(canvas, spanTime);
container.append(divRound);
const ctx = canvas.getContext('2d');
/* ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, Math.PI * 2);
ctx.clip(); */
const clear = () => {
//console.log('clearing video');
(appImManager.chat.setPeerPromise || Promise.resolve()).finally(() => {
if(isInDOM(globalVideo)) {
return;
}
globalVideo.removeEventListener('play', onPlay);
globalVideo.removeEventListener('timeupdate', onTimeUpdate);
globalVideo.removeEventListener('pause', onPaused);
});
};
const onFrame = () => {
ctx.drawImage(globalVideo, 0, 0);
globalVideo.removeEventListener('timeupdate', onGlobalTimeUpdate);
globalVideo.removeEventListener('play', onGlobalPlay);
globalVideo.removeEventListener('pause', onGlobalPause);
video.removeEventListener('play', onVideoPlay);
video.removeEventListener('pause', onVideoPause);
const offset = circumference - globalVideo.currentTime / globalVideo.duration * circumference;
circle.style.strokeDashoffset = '' + offset;
return !globalVideo.paused;
};
const onGlobalTimeUpdate = (e: Event) => {
//console.log('video global timeupdate event', e, globalVideo.currentTime, globalVideo.duration);
if(!isInDOM(video)) {
const onTimeUpdate = () => {
if(!globalVideo.duration) return;
if(!isInDOM(globalVideo)) {
clear();
return;
}
spanTime.innerText = (globalVideo.duration - globalVideo.currentTime + '').toHHMMSS(false);
};
const onGlobalPlay = (e: Event) => {
//console.log('video global play event', e);
video.play();
const onPlay = () => {
video.remove();
divRound.classList.remove('is-paused');
animateSingle(onFrame, canvas);
};
const onGlobalPause = (e: Event) => {
//console.trace('video global pause event', e, globalVideo.paused, e.eventPhase);
video.pause();
const onPaused = () => {
if(!isInDOM(globalVideo)) {
clear();
return;
}
divRound.classList.add('is-paused');
};
const onVideoPlay = (e: Event) => {
//console.log('video play event', e);
globalVideo.addEventListener('play', onPlay);
globalVideo.addEventListener('timeupdate', onTimeUpdate);
globalVideo.addEventListener('pause', onPaused);
attachClickEvent(canvas, (e) => {
cancelEvent(e);
if(globalVideo.paused) {
globalVideo.currentTime = video.currentTime;
globalVideo.play();
} else {
globalVideo.pause();
}
};
});
// * this will fire when video unmounts
const onVideoPause = (e: Event) => {
//console.trace('video pause event', e);
if(isInDOM(video)) {
globalVideo.pause();
globalVideo.currentTime = video.currentTime;
if(globalVideo.paused) {
if(globalVideo.duration && globalVideo.currentTime !== globalVideo.duration) {
onFrame();
onTimeUpdate();
} else {
clear();
onPaused();
}
};
globalVideo.addEventListener('timeupdate', onGlobalTimeUpdate);
globalVideo.addEventListener('play', onGlobalPlay);
globalVideo.addEventListener('pause', onGlobalPause);
video.addEventListener('play', onVideoPlay);
video.addEventListener('pause', onVideoPause);
} else {
onPlay();
}
} else {
video.autoplay = true; // для safari
}
@ -276,7 +305,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -276,7 +305,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
/* if(!video.paused) {
video.pause();
} */
if(doc.type !== 'round' && group) {
if(group) {
animationIntersector.addAnimation(video, group);
}
@ -297,24 +326,16 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -297,24 +326,16 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
deferred.resolve();
});
if(doc.type !== 'round') {
video.muted = true;
video.loop = true;
//video.play();
video.autoplay = true;
}
video.muted = true;
video.loop = true;
//video.play();
video.autoplay = true;
renderImageFromUrl(video, doc.url);
return Promise.all([loadPromise, deferred]);
};
if(doc.type === 'round') {
video.dataset.ckin = 'circle';
video.dataset.overlay = '1';
new VideoPlayer(video, undefined, undefined, doc.duration);
}
/* if(doc.size >= 20e6 && !doc.downloaded) {
let downloadDiv = document.createElement('div');
downloadDiv.classList.add('download');

59
src/lib/mediaPlayer.ts

@ -1,9 +1,8 @@ @@ -1,9 +1,8 @@
import { attachClickEvent, cancelEvent } from "../helpers/dom";
import { cancelEvent } from "../helpers/dom";
import appMediaPlaybackController from "../components/appMediaPlaybackController";
import { isAppleMobile } from "../helpers/userAgent";
import { isTouchSupported } from "../helpers/touchSupport";
import RangeSelector from "../components/rangeSelector";
import { animateSingle } from "../helpers/animation";
import { onVideoLoad } from "../helpers/files";
type SUPEREVENT = MouseEvent | TouchEvent;
@ -358,56 +357,6 @@ export default class VideoPlayer { @@ -358,56 +357,6 @@ export default class VideoPlayer {
video.addEventListener('timeupdate', () => {
timeElapsed.innerHTML = String(video.currentTime | 0).toHHMMSS();
});
} else if(skin === 'circle') {
const wrapper = document.createElement('div');
wrapper.classList.add('circle-time-left');
video.parentNode.insertBefore(wrapper, video);
wrapper.innerHTML = '<div class="circle-time"></div><div class="iconVolume tgico-nosound"></div>';
const circle = player.querySelector('.progress-ring__circle') as SVGCircleElement;
const radius = circle.r.baseVal.value;
const circumference = 2 * Math.PI * radius;
timeDuration = wrapper.firstElementChild as HTMLElement;
const iconVolume = wrapper.lastElementChild as HTMLElement;
circle.style.strokeDasharray = circumference + ' ' + circumference;
circle.style.strokeDashoffset = '' + circumference;
attachClickEvent(circle as any, (e) => {
cancelEvent(e);
this.togglePlay();
});
const update = () => {
const offset = circumference - video.currentTime / video.duration * circumference;
circle.style.strokeDashoffset = '' + offset;
return !video.paused;
};
const timeUpdate = () => {
const timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS();
if(timeLeft != '0') timeDuration.innerHTML = timeLeft;
};
video.addEventListener('play', () => {
iconVolume.style.display = 'none';
animateSingle(update, circle);
update();
});
video.addEventListener('pause', () => {
iconVolume.style.display = '';
});
video.addEventListener('timeupdate', () => {
timeUpdate();
});
onVideoLoad(video).then(() => {
if(!video.currentTime || video.currentTime === video.duration) return;
update();
timeUpdate();
});
}
video.addEventListener('play', () => {
@ -465,12 +414,6 @@ export default class VideoPlayer { @@ -465,12 +414,6 @@ export default class VideoPlayer {
</div>
</div>
</div>`;
} else if(skin === 'circle') {
return `
<svg class="progress-ring" width="200px" height="200px">
<circle class="progress-ring__circle" stroke="white" stroke-opacity="0.3" stroke-width="3.5" cx="100" cy="100" r="93" fill="transparent" transform="rotate(-90, 100, 100)"/>
</svg>
`;
}
}

6
src/scss/partials/_chatBubble.scss

@ -520,12 +520,13 @@ $bubble-margin: .25rem; @@ -520,12 +520,13 @@ $bubble-margin: .25rem;
max-width: 200px !important;
max-height: 200px !important;
img {
.media-photo, .media-video {
border-radius: 50%;
pointer-events: none;
}
}
}
&:not(.is-message-empty) .attachment/* ,
&:not(.is-message-empty).is-reply .attachment */ {
border-bottom-left-radius: 0;
@ -1239,6 +1240,7 @@ $bubble-margin: .25rem; @@ -1239,6 +1240,7 @@ $bubble-margin: .25rem;
align-items: center;
cursor: pointer;
user-select: none;
height: 1.125rem;
&.can-autoplay:after {
content: $tgico-nosound;

49
src/scss/partials/_ckin.scss

@ -402,52 +402,3 @@ input[type=range] { @@ -402,52 +402,3 @@ input[type=range] {
justify-content: space-between;
align-items: center;
}
video[data-ckin="circle"] {
border-radius: 50%;
overflow: hidden;
position: relative;
z-index: -1;
}
.progress-ring {
position: absolute;
top: 0;
left: 0;
cursor: pointer;
&__circle {
transition: stroke-dashoffset;
}
}
.ckin__player.circle {
position: relative;
width: 200px;
height: 200px;
.circle-time-left {
color: #fff;
position: absolute;
top: .1875rem;
left: .1875rem;
border-radius: 12px;
background-color: rgba(0, 0, 0, .23);
padding: 1px 6px 2px 7px;
height: 1.125rem;
z-index: 2;
display: flex;
align-items: center;
}
.circle-time {
font-size: .75rem;
//margin-top: 1px;
}
.iconVolume {
padding-left: .25rem;
display: flex;
align-items: center;
font-size: 1.125rem;
}
}

42
src/scss/style.scss

@ -1142,7 +1142,7 @@ middle-ellipsis-element { @@ -1142,7 +1142,7 @@ middle-ellipsis-element {
fill: rgba(0, 0, 0, .08);
}
.media-photo, .media-video, .media-sticker {
.media-photo, .media-video, .media-sticker, .media-round {
position: absolute;
top: 0;
right: 0;
@ -1165,3 +1165,43 @@ middle-ellipsis-element { @@ -1165,3 +1165,43 @@ middle-ellipsis-element {
.media-sticker {
margin: auto;
}
.media-round {
max-width: 200px;
max-height: 200px;
z-index: 1;
canvas {
width: 100%;
height: 100%;
border-radius: 50%;
position: relative;
}
.progress-ring {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
&__circle {
transition: stroke-dashoffset;
stroke-linecap: round;
}
}
.video-time {
padding: 1px 6px 2px 7px;
background-color: rgba(0, 0, 0, .23) !important;
}
&.is-paused .video-time {
&:after {
content: $tgico-nosound;
padding-left: .25rem;
display: flex;
align-items: center;
font-size: 1.125rem;
}
}
}

Loading…
Cancel
Save