Browse Source

Media viewer fixes

master
Eduard Kuzmenko 3 years ago
parent
commit
13c4b541cd
  1. 124
      src/components/appMediaViewer.ts
  2. 9
      src/components/sidebarLeft/tabs/settings.ts
  3. 10
      src/helpers/dom.ts
  4. 19
      src/lib/appManagers/appPhotosManager.ts
  5. 12
      src/scss/partials/_mediaViewer.scss
  6. 4
      src/scss/partials/_preloader.scss

124
src/components/appMediaViewer.ts

@ -774,8 +774,10 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
protected updateMediaSource(target: HTMLElement, url: string, tagName: 'video' | 'img') { protected updateMediaSource(target: HTMLElement, url: string, tagName: 'video' | 'img') {
//if(target instanceof SVGSVGElement) { //if(target instanceof SVGSVGElement) {
const el = target.querySelector(tagName) as HTMLElement; const el = target.tagName.toLowerCase() === tagName ? target : target.querySelector(tagName) as HTMLElement;
renderImageFromUrl(el, url); if(el) {
renderImageFromUrl(el, url);
}
/* } else { /* } else {
} */ } */
@ -847,7 +849,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
this.buttons.next.classList.toggle('hide', !this.nextTargets.length); this.buttons.next.classList.toggle('hide', !this.nextTargets.length);
const container = this.content.media; const container = this.content.media;
const useContainerAsTarget = !target; const useContainerAsTarget = !target || target === container;
if(useContainerAsTarget) target = container; if(useContainerAsTarget) target = container;
this.lastTarget = target; this.lastTarget = target;
@ -890,11 +892,22 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
//const maxWidth = appPhotosManager.windowW - 16; //const maxWidth = appPhotosManager.windowW - 16;
const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16; const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16;
const maxHeight = appPhotosManager.windowH - 100; const maxHeight = appPhotosManager.windowH - 100;
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(media); let thumbPromise: Promise<any> = Promise.resolve();
if(gotThumb) { if(useContainerAsTarget) {
container.append(gotThumb.image); const cacheContext = appPhotosManager.getCacheContext(media);
if(cacheContext.downloaded) {
const img = new Image();
img.src = cacheContext.url;
container.append(img);
} else {
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(media);
if(gotThumb) {
thumbPromise = gotThumb.loadPromise;
container.append(gotThumb.image);
}
}
} }
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight); const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight, mediaSizes.isMobile ? false : true);
// need after setAttachmentSize // need after setAttachmentSize
/* if(useContainerAsTarget) { /* if(useContainerAsTarget) {
@ -910,55 +923,55 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
// потому что для safari нужно создать элемент из event'а // потому что для safari нужно создать элемент из event'а
const video = document.createElement('video'); const video = document.createElement('video');
setMoverPromise = this.setMoverToTarget(target, false, fromRight).then(({onAnimationEnd}) => { const set = () => this.setMoverToTarget(target, false, fromRight).then(({onAnimationEnd}) => {
//return; // set and don't move //return; // set and don't move
//if(wasActive) return; //if(wasActive) return;
//return; //return;
const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover; const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
//const video = mover.querySelector('video') || document.createElement('video'); //const video = mover.querySelector('video') || document.createElement('video');
const moverVideo = mover.querySelector('video'); const moverVideo = mover.querySelector('video');
if(moverVideo) { if(moverVideo) {
moverVideo.remove(); moverVideo.remove();
} }
//video.src = ''; //video.src = '';
video.setAttribute('playsinline', 'true'); video.setAttribute('playsinline', 'true');
// * fix for playing video if viewer is closed (https://contest.com/javascript-web-bonus/entry1425#issue11629) // * fix for playing video if viewer is closed (https://contest.com/javascript-web-bonus/entry1425#issue11629)
video.addEventListener('timeupdate', () => { video.addEventListener('timeupdate', () => {
if(this.tempId != tempId) { if(this.tempId != tempId) {
video.pause(); video.pause();
} }
}); });
if(isSafari) { if(isSafari) {
// test stream // test stream
// video.controls = true; // video.controls = true;
video.autoplay = true; video.autoplay = true;
} }
if(media.type == 'gif') { if(media.type == 'gif') {
video.muted = true; video.muted = true;
video.autoplay = true; video.autoplay = true;
video.loop = true; video.loop = true;
} }
if(!video.parentElement) { if(!video.parentElement) {
div.append(video); div.append(video);
} }
const canPlayThrough = new Promise((resolve) => { const canPlayThrough = new Promise((resolve) => {
video.addEventListener('canplay', resolve, {once: true}); video.addEventListener('canplay', resolve, {once: true});
}); });
const createPlayer = () => { const createPlayer = () => {
if(media.type != 'gif') { if(media.type != 'gif') {
video.dataset.ckin = 'default'; video.dataset.ckin = 'default';
video.dataset.overlay = '1'; video.dataset.overlay = '1';
// fix for simultaneous play // fix for simultaneous play
appMediaPlaybackController.pause(); appMediaPlaybackController.pause();
appMediaPlaybackController.willBePlayedMedia = null; appMediaPlaybackController.willBePlayedMedia = null;
@ -967,25 +980,25 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
if(this.tempId != tempId) { if(this.tempId != tempId) {
return; return;
} }
const player = new VideoPlayer(video, true, media.supportsStreaming); const player = new VideoPlayer(video, true, media.supportsStreaming);
/* div.append(video); /* div.append(video);
mover.append(player.wrapper); */ mover.append(player.wrapper); */
}); });
} }
}; };
if(media.supportsStreaming) { if(media.supportsStreaming) {
onAnimationEnd.then(() => { onAnimationEnd.then(() => {
if(video.readyState < video.HAVE_FUTURE_DATA) { if(video.readyState < video.HAVE_FUTURE_DATA) {
preloader.attach(mover, true); preloader.attach(mover, true);
} }
/* canPlayThrough.then(() => { /* canPlayThrough.then(() => {
preloader.detach(); preloader.detach();
}); */ }); */
}); });
const attachCanPlay = () => { const attachCanPlay = () => {
video.addEventListener('canplay', () => { video.addEventListener('canplay', () => {
//this.log('video waited and progress loaded'); //this.log('video waited and progress loaded');
@ -993,22 +1006,22 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
video.parentElement.classList.remove('is-buffering'); video.parentElement.classList.remove('is-buffering');
}, {once: true}); }, {once: true});
}; };
video.addEventListener('waiting', (e) => { video.addEventListener('waiting', (e) => {
const loading = video.networkState === video.NETWORK_LOADING; const loading = video.networkState === video.NETWORK_LOADING;
const isntEnoughData = video.readyState < video.HAVE_FUTURE_DATA; const isntEnoughData = video.readyState < video.HAVE_FUTURE_DATA;
//this.log('video waiting for progress', loading, isntEnoughData); //this.log('video waiting for progress', loading, isntEnoughData);
if(loading && isntEnoughData) { if(loading && isntEnoughData) {
attachCanPlay(); attachCanPlay();
preloader.attach(mover, true); preloader.attach(mover, true);
// поставлю класс для плеера, чтобы убрать большую иконку пока прелоадер на месте // поставлю класс для плеера, чтобы убрать большую иконку пока прелоадер на месте
video.parentElement.classList.add('is-buffering'); video.parentElement.classList.add('is-buffering');
} }
}); });
attachCanPlay(); attachCanPlay();
} }
@ -1023,13 +1036,13 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
}); });
} }
(promise as Promise<any>).then(async() => { (promise as Promise<any>).then(async() => {
if(this.tempId != tempId) { if(this.tempId != tempId) {
this.log.warn('media viewer changed video'); this.log.warn('media viewer changed video');
return; return;
} }
const url = media.url; const url = media.url;
if(target instanceof SVGSVGElement/* && (video.parentElement || !isSafari) */) { // if video exists if(target instanceof SVGSVGElement/* && (video.parentElement || !isSafari) */) { // if video exists
//if(!video.parentElement) { //if(!video.parentElement) {
@ -1040,25 +1053,27 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} else { } else {
renderImageFromUrl(video, url); renderImageFromUrl(video, url);
} }
createPlayer(); createPlayer();
}); });
return promise; return promise;
}; };
this.lazyLoadQueue.unshift({load}); this.lazyLoadQueue.unshift({load});
//} else createPlayer(); //} else createPlayer();
}); });
setMoverPromise = thumbPromise.then(set);
} else { } else {
setMoverPromise = this.setMoverToTarget(target, false, fromRight).then(({onAnimationEnd}) => { const set = () => this.setMoverToTarget(target, false, fromRight).then(({onAnimationEnd}) => {
//return; // set and don't move //return; // set and don't move
//if(wasActive) return; //if(wasActive) return;
//return; //return;
const load = () => { const load = () => {
const cancellablePromise = appPhotosManager.preloadPhoto(media.id, size); const cancellablePromise = appPhotosManager.preloadPhoto(media.id, size);
onAnimationEnd.then(() => { onAnimationEnd.then(() => {
if(!media.url) { if(!media.url) {
this.preloader.attach(mover, true, cancellablePromise); this.preloader.attach(mover, true, cancellablePromise);
@ -1072,12 +1087,12 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
///////this.log('indochina', blob); ///////this.log('indochina', blob);
const url = media.url; const url = media.url;
if(target instanceof SVGSVGElement) { if(target instanceof SVGSVGElement) {
this.updateMediaSource(target, url, 'img'); this.updateMediaSource(target, url, 'img');
this.updateMediaSource(mover, url, 'img'); this.updateMediaSource(mover, url, 'img');
if(mediaSizes.isMobile) { if(mediaSizes.isMobile) {
const imgs = mover.querySelectorAll('img'); const imgs = mover.querySelectorAll('img');
if(imgs && imgs.length) { if(imgs && imgs.length) {
@ -1088,29 +1103,38 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
} }
} else { } else {
const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover; const div = mover.firstElementChild && mover.firstElementChild.classList.contains('media-viewer-aspecter') ? mover.firstElementChild : mover;
let image = new Image(); const haveImage = div.firstElementChild?.tagName === 'IMG' ? div.firstElementChild as HTMLImageElement : null;
if(!haveImage || haveImage.src !== url) {
//this.log('will renderImageFromUrl:', image, div, target); let image = new Image();
//this.log('will renderImageFromUrl:', image, div, target);
renderImageFromUrl(image, url, () => {
this.updateMediaSource(target, url, 'img');
renderImageFromUrl(image, url, () => { if(haveImage) {
if(div.firstElementChild?.tagName === 'IMG') { window.requestAnimationFrame(() => {
div.firstElementChild.remove(); haveImage.remove();
} });
}
div.append(image);
}); div.append(image);
});
}
} }
this.preloader.detach(); this.preloader.detach();
}).catch(err => { }).catch(err => {
this.log.error(err); this.log.error(err);
}); });
return cancellablePromise; return cancellablePromise;
}; };
this.lazyLoadQueue.unshift({load}); this.lazyLoadQueue.unshift({load});
}); });
setMoverPromise = thumbPromise.then(set);
} }
return this.setMoverPromise = setMoverPromise.catch(() => { return this.setMoverPromise = setMoverPromise.catch(() => {

9
src/components/sidebarLeft/tabs/settings.ts

@ -5,7 +5,7 @@ import appSidebarLeft, { AppSidebarLeft } from "..";
import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager";
import ButtonMenuToggle from "../../buttonMenuToggle"; import ButtonMenuToggle from "../../buttonMenuToggle";
import Button from "../../button"; import Button from "../../button";
import AppMediaViewer from "../../appMediaViewer"; //import AppMediaViewer from "../../appMediaViewerNew";
export default class AppSettingsTab extends SliderSuperTab { export default class AppSettingsTab extends SliderSuperTab {
private avatarElem: AvatarElement; private avatarElem: AvatarElement;
@ -44,12 +44,15 @@ export default class AppSettingsTab extends SliderSuperTab {
this.avatarElem.classList.add('profile-avatar', 'avatar-120'); this.avatarElem.classList.add('profile-avatar', 'avatar-120');
/* const div = document.createElement('div'); /* const div = document.createElement('div');
div.style.cssText = 'border-radius: 8px; overflow: hidden; width: 396px; height: 264px; flex: 0 0 auto; position: relative; margin: 10rem 0 10rem auto;'; //div.style.cssText = 'border-radius: 8px; overflow: hidden; width: 396px; height: 264px; flex: 0 0 auto; position: relative; margin: 10rem 0 10rem auto;';
//div.style.width = '135px';
//div.style.height = '100px';
div.style.cssText = 'border-radius: 8px; overflow: hidden; width: 396px; height: 264px; flex: 0 0 auto; position: relative; margin: 10rem auto 10rem 0;';
div.style.width = '135px'; div.style.width = '135px';
div.style.height = '100px'; div.style.height = '100px';
const img = document.createElement('img'); const img = document.createElement('img');
img.src = 'assets/img/PinnedMessages.png'; img.src = 'assets/img/pepe.jpg';
img.classList.add('media-photo'); img.classList.add('media-photo');
img.style.cssText = 'max-width: 100%;max-height: 100%;'; img.style.cssText = 'max-width: 100%;max-height: 100%;';

10
src/helpers/dom.ts

@ -410,13 +410,13 @@ export function fillPropertyValue(str: string) {
return splitted; return splitted;
} }
export function calcImageInBox(imageW: number, imageH: number, boxW: number, boxH: number, noZoom?: boolean) { export function calcImageInBox(imageW: number, imageH: number, boxW: number, boxH: number, noZoom = true) {
if(imageW < boxW && imageH < boxH) { if(imageW < boxW && imageH < boxH && noZoom) {
return {w: imageW, h: imageH}; return {w: imageW, h: imageH};
} }
var boxedImageW = boxW; let boxedImageW = boxW;
var boxedImageH = boxH; let boxedImageH = boxH;
if((imageW / imageH) > (boxW / boxH)) { if((imageW / imageH) > (boxW / boxH)) {
boxedImageH = (imageH * boxW / imageW) | 0; boxedImageH = (imageH * boxW / imageW) | 0;
@ -441,6 +441,8 @@ export function calcImageInBox(imageW: number, imageH: number, boxW: number, box
return {w: boxedImageW, h: boxedImageH}; return {w: boxedImageW, h: boxedImageH};
} }
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.calcImageInBox = calcImageInBox);
export function positionElementByIndex(element: HTMLElement, container: HTMLElement, pos: number) { export function positionElementByIndex(element: HTMLElement, container: HTMLElement, pos: number) {
const prevPos = element.parentElement === container ? whichChild(element) : -1; const prevPos = element.parentElement === container ? whichChild(element) : -1;

19
src/lib/appManagers/appPhotosManager.ts

@ -38,13 +38,14 @@ export class AppPhotosManager {
public static Df = bytesFromHex('ffd9'); public static Df = bytesFromHex('ffd9');
constructor() { constructor() {
window.addEventListener('resize', (e) => { // @ts-ignore
this.windowW = document.body.scrollWidth; const w: any = 'visualViewport' in window ? window.visualViewport : window;
this.windowH = document.body.scrollHeight; const set = () => {
}); this.windowW = w.width || w.innerWidth;
this.windowH = w.height || w.innerHeight;
this.windowW = document.body.scrollWidth; };
this.windowH = document.body.scrollHeight; w.addEventListener('resize', set);
set();
} }
public savePhoto(photo: Photo, context?: ReferenceContext) { public savePhoto(photo: Photo, context?: ReferenceContext) {
@ -208,7 +209,7 @@ export class AppPhotosManager {
return {image, loadPromise}; return {image, loadPromise};
} }
public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number) { public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, noZoom = true) {
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight); const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div); //console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
@ -222,7 +223,7 @@ export class AppPhotosManager {
height = 'h' in photoSize ? photoSize.h : 100; height = 'h' in photoSize ? photoSize.h : 100;
} }
const {w, h} = calcImageInBox(width, height, boxWidth, boxHeight); const {w, h} = calcImageInBox(width, height, boxWidth, boxHeight, noZoom);
if(element instanceof SVGForeignObjectElement) { if(element instanceof SVGForeignObjectElement) {
element.setAttributeNS(null, 'width', '' + w); element.setAttributeNS(null, 'width', '' + w);
element.setAttributeNS(null, 'height', '' + h); element.setAttributeNS(null, 'height', '' + h);

12
src/scss/partials/_mediaViewer.scss

@ -218,18 +218,21 @@
transform-origin: top left; transform-origin: top left;
//transform-origin: bottom right; //transform-origin: bottom right;
overflow: hidden; overflow: hidden;
transform: translateZ(0) scale3d(1, 1, 1);
border-radius: 0;
-webkit-mask-image: -webkit-radial-gradient(circle, white 100%, black 100%); // fix border-radius overflow
// эти значения должны быть такими же, как при установке maxWidth и maxHeight в openMedia! // эти значения должны быть такими же, как при установке maxWidth и maxHeight в openMedia!
//max-width: 100%; //max-width: 100%;
max-width: calc(100% - 16px); //max-width: calc(1vw - 16px);
//max-height: 100%; //max-height: 100%;
max-height: calc(100% - 100px); //max-height: calc((var(--vh, 1vh) * 100) - 100px);
// эти значения должны быть такими же, как при установке maxWidth и maxHeight в openMedia! // эти значения должны быть такими же, как при установке maxWidth и maxHeight в openMedia!
@include respond-to(handhelds) { @include respond-to(handhelds) {
overflow: visible; overflow: visible;
//max-height: 100% !important; //max-height: 100% !important;
max-width: 100% !important; //max-width: 100vw !important;
} }
.ckin__player { .ckin__player {
@ -291,10 +294,13 @@
left: 50% !important; left: 50% !important;
top: 50% !important; top: 50% !important;
transform: translate3d(-50%, -50%, 0) !important; transform: translate3d(-50%, -50%, 0) !important;
max-width: calc(100vw - 16px);
max-height: calc((var(--vh, 1vh) * 100) - 100px);
@include respond-to(handhelds) { @include respond-to(handhelds) {
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
max-width: 100vw !important;
//height: calc(100% - 100px) !important; //height: calc(100% - 100px) !important;
/* height: calc(100% - 50px) !important; /* height: calc(100% - 50px) !important;
top: calc(50% + 25px) !important; top: calc(50% + 25px) !important;

4
src/scss/partials/_preloader.scss

@ -89,6 +89,10 @@ $transition: .2s ease-in-out;
//animation: dashNew 1.5s ease-in-out infinite; //animation: dashNew 1.5s ease-in-out infinite;
stroke-dasharray: 112.36, 149.82; stroke-dasharray: 112.36, 149.82;
} }
&.preloader-streamable .preloader-path-new {
stroke-dasharray: 88.95, 118.61;
}
} }
.preloader-close, .preloader-download { .preloader-close, .preloader-download {

Loading…
Cancel
Save