viewer: animate lp icon
Signed-off-by: Varun Patil <radialapps@gmail.com>pull/900/head
parent
c6f5ed5b05
commit
5ccba14519
|
@ -24,7 +24,7 @@
|
||||||
<VideoIcon :size="22" />
|
<VideoIcon :size="22" />
|
||||||
</div>
|
</div>
|
||||||
<div class="livephoto" v-if="data.liveid" @mouseenter.passive="playVideo" @mouseleave.passive="stopVideo">
|
<div class="livephoto" v-if="data.liveid" @mouseenter.passive="playVideo" @mouseleave.passive="stopVideo">
|
||||||
<LivePhotoIcon :size="22" :spin="liveWaiting" :playing="livePlaying" />
|
<LivePhotoIcon :size="22" :spin="liveState.waiting" :playing="liveState.playing" />
|
||||||
</div>
|
</div>
|
||||||
<RawIcon class="raw" v-if="isRaw" :size="28" />
|
<RawIcon class="raw" v-if="isRaw" :size="28" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -119,9 +119,11 @@ export default defineComponent({
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
touchTimer: 0,
|
touchTimer: 0,
|
||||||
livePlayTimer: 0,
|
liveState: {
|
||||||
liveWaiting: false,
|
playTimer: 0,
|
||||||
livePlaying: false,
|
playing: false,
|
||||||
|
waiting: false,
|
||||||
|
},
|
||||||
faceSrc: null as string | null,
|
faceSrc: null as string | null,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -140,17 +142,14 @@ export default defineComponent({
|
||||||
// Setup video hooks
|
// Setup video hooks
|
||||||
const video = this.refs.video;
|
const video = this.refs.video;
|
||||||
if (video) {
|
if (video) {
|
||||||
utils.setupLivePhotoHooks(video);
|
utils.setupLivePhotoHooks(video, this.liveState);
|
||||||
video.addEventListener('playing', () => (this.livePlaying = true));
|
|
||||||
video.addEventListener('pause', () => (this.livePlaying = false));
|
|
||||||
video.addEventListener('ended', () => (this.livePlaying = false));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Clear timers */
|
/** Clear timers */
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
clearTimeout(this.touchTimer);
|
clearTimeout(this.touchTimer);
|
||||||
clearTimeout(this.livePlayTimer);
|
clearTimeout(this.liveState.playTimer);
|
||||||
|
|
||||||
// Clean up blob url if face rect was created
|
// Clean up blob url if face rect was created
|
||||||
if (this.faceSrc) {
|
if (this.faceSrc) {
|
||||||
|
@ -277,11 +276,11 @@ export default defineComponent({
|
||||||
|
|
||||||
/** Start preview video */
|
/** Start preview video */
|
||||||
playVideo() {
|
playVideo() {
|
||||||
this.liveWaiting = true;
|
this.liveState.waiting = true;
|
||||||
|
|
||||||
utils.setRenewingTimeout(
|
utils.setRenewingTimeout(
|
||||||
this,
|
this.liveState,
|
||||||
'livePlayTimer',
|
'playTimer',
|
||||||
async () => {
|
async () => {
|
||||||
const video = this.refs.video;
|
const video = this.refs.video;
|
||||||
if (!video || this.data.flag & this.c.FLAG_SELECTED) return;
|
if (!video || this.data.flag & this.c.FLAG_SELECTED) return;
|
||||||
|
@ -292,7 +291,7 @@ export default defineComponent({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore, pause was probably called too soon
|
// ignore, pause was probably called too soon
|
||||||
} finally {
|
} finally {
|
||||||
this.liveWaiting = false;
|
this.liveState.waiting = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
400,
|
400,
|
||||||
|
@ -302,10 +301,9 @@ export default defineComponent({
|
||||||
/** Stop preview video */
|
/** Stop preview video */
|
||||||
stopVideo() {
|
stopVideo() {
|
||||||
this.refs.video?.pause();
|
this.refs.video?.pause();
|
||||||
window.clearTimeout(this.livePlayTimer);
|
window.clearTimeout(this.liveState.playTimer);
|
||||||
this.livePlayTimer = 0;
|
this.liveState.playTimer = 0;
|
||||||
this.liveWaiting = false;
|
this.liveState.waiting = false;
|
||||||
this.livePlaying = false;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,6 +20,7 @@ class LivePhotoContentSetup {
|
||||||
constructor(
|
constructor(
|
||||||
lightbox: PhotoSwipe,
|
lightbox: PhotoSwipe,
|
||||||
private psImage: PsImage,
|
private psImage: PsImage,
|
||||||
|
private liveState: { playing: boolean; waiting: boolean },
|
||||||
) {
|
) {
|
||||||
lightbox.on('contentLoad', this.onContentLoad.bind(this));
|
lightbox.on('contentLoad', this.onContentLoad.bind(this));
|
||||||
lightbox.on('contentActivate', this.onContentActivate.bind(this));
|
lightbox.on('contentActivate', this.onContentActivate.bind(this));
|
||||||
|
@ -32,10 +33,13 @@ class LivePhotoContentSetup {
|
||||||
if (!video) return;
|
if (!video) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.liveState.waiting = true;
|
||||||
video.currentTime = 0;
|
video.currentTime = 0;
|
||||||
await video.play();
|
await video.play();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore, pause was probably called too soon
|
// ignore, pause was probably called too soon
|
||||||
|
} finally {
|
||||||
|
this.liveState.waiting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +64,7 @@ class LivePhotoContentSetup {
|
||||||
div.appendChild(video);
|
div.appendChild(video);
|
||||||
content.element = div;
|
content.element = div;
|
||||||
|
|
||||||
utils.setupLivePhotoHooks(video);
|
utils.setupLivePhotoHooks(video, this.liveState);
|
||||||
|
|
||||||
const img = this.psImage.getXImgElem(content, () => content.onLoaded());
|
const img = this.psImage.getXImgElem(content, () => content.onLoaded());
|
||||||
div.appendChild(img);
|
div.appendChild(img);
|
||||||
|
|
|
@ -55,7 +55,9 @@
|
||||||
:close-after-click="true"
|
:close-after-click="true"
|
||||||
>
|
>
|
||||||
{{ t('memories', 'Play Live Photo') }}
|
{{ t('memories', 'Play Live Photo') }}
|
||||||
<template #icon> <LivePhotoIcon :size="24" /> </template>
|
<template #icon>
|
||||||
|
<LivePhotoIcon :size="24" :playing="liveState.playing" :spin="liveState.waiting" />
|
||||||
|
</template>
|
||||||
</NcActionButton>
|
</NcActionButton>
|
||||||
<NcActionButton
|
<NcActionButton
|
||||||
v-if="!routeIsPublic && !isLocal"
|
v-if="!routeIsPublic && !isLocal"
|
||||||
|
@ -194,6 +196,7 @@ import PsLivePhoto from './PsLivePhoto';
|
||||||
import type { IImageInfo, IPhoto, TimelineState } from '@typings';
|
import type { IImageInfo, IPhoto, TimelineState } from '@typings';
|
||||||
import type { PsContent } from './types';
|
import type { PsContent } from './types';
|
||||||
|
|
||||||
|
import LivePhotoIcon from '@components/icons/LivePhoto.vue';
|
||||||
import ShareIcon from 'vue-material-design-icons/ShareVariant.vue';
|
import ShareIcon from 'vue-material-design-icons/ShareVariant.vue';
|
||||||
import DeleteIcon from 'vue-material-design-icons/TrashCanOutline.vue';
|
import DeleteIcon from 'vue-material-design-icons/TrashCanOutline.vue';
|
||||||
import StarIcon from 'vue-material-design-icons/Star.vue';
|
import StarIcon from 'vue-material-design-icons/Star.vue';
|
||||||
|
@ -205,7 +208,6 @@ import TuneIcon from 'vue-material-design-icons/Tune.vue';
|
||||||
import SlideshowIcon from 'vue-material-design-icons/PlayBox.vue';
|
import SlideshowIcon from 'vue-material-design-icons/PlayBox.vue';
|
||||||
import EditFileIcon from 'vue-material-design-icons/FileEdit.vue';
|
import EditFileIcon from 'vue-material-design-icons/FileEdit.vue';
|
||||||
import AlbumRemoveIcon from 'vue-material-design-icons/BookRemove.vue';
|
import AlbumRemoveIcon from 'vue-material-design-icons/BookRemove.vue';
|
||||||
import LivePhotoIcon from 'vue-material-design-icons/MotionPlayOutline.vue';
|
|
||||||
import AlbumIcon from 'vue-material-design-icons/ImageAlbum.vue';
|
import AlbumIcon from 'vue-material-design-icons/ImageAlbum.vue';
|
||||||
|
|
||||||
const SLIDESHOW_MS = 5000;
|
const SLIDESHOW_MS = 5000;
|
||||||
|
@ -220,6 +222,7 @@ export default defineComponent({
|
||||||
NcActions,
|
NcActions,
|
||||||
NcActionButton,
|
NcActionButton,
|
||||||
ImageEditor,
|
ImageEditor,
|
||||||
|
LivePhotoIcon,
|
||||||
ShareIcon,
|
ShareIcon,
|
||||||
DeleteIcon,
|
DeleteIcon,
|
||||||
StarIcon,
|
StarIcon,
|
||||||
|
@ -231,7 +234,6 @@ export default defineComponent({
|
||||||
SlideshowIcon,
|
SlideshowIcon,
|
||||||
EditFileIcon,
|
EditFileIcon,
|
||||||
AlbumRemoveIcon,
|
AlbumRemoveIcon,
|
||||||
LivePhotoIcon,
|
|
||||||
AlbumIcon,
|
AlbumIcon,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -260,6 +262,13 @@ export default defineComponent({
|
||||||
psImage: null as PsImage | null,
|
psImage: null as PsImage | null,
|
||||||
psLivePhoto: null as PsLivePhoto | null,
|
psLivePhoto: null as PsLivePhoto | null,
|
||||||
|
|
||||||
|
/** Live photo state */
|
||||||
|
liveState: {
|
||||||
|
playing: false,
|
||||||
|
waiting: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
/** List globals */
|
||||||
list: [] as IPhoto[],
|
list: [] as IPhoto[],
|
||||||
globalCount: 0,
|
globalCount: 0,
|
||||||
globalAnchor: -1,
|
globalAnchor: -1,
|
||||||
|
@ -590,15 +599,15 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
// Video support
|
// Video support
|
||||||
this.psVideo = new PsVideo(<PhotoSwipe>this.photoswipe, {
|
this.psVideo = new PsVideo(<any>this.photoswipe, {
|
||||||
preventDragOffset: 40,
|
preventDragOffset: 40,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Image support
|
// Image support
|
||||||
this.psImage = new PsImage(<PhotoSwipe>this.photoswipe);
|
this.psImage = new PsImage(<any>this.photoswipe);
|
||||||
|
|
||||||
// Live Photo support
|
// Live Photo support
|
||||||
this.psLivePhoto = new PsLivePhoto(<PhotoSwipe>this.photoswipe, <PsImage>this.psImage);
|
this.psLivePhoto = new PsLivePhoto(<any>this.photoswipe, <any>this.psImage, this.liveState);
|
||||||
|
|
||||||
// Patch the close button to stop the slideshow
|
// Patch the close button to stop the slideshow
|
||||||
const _close = this.photoswipe.close.bind(this.photoswipe);
|
const _close = this.photoswipe.close.bind(this.photoswipe);
|
||||||
|
|
|
@ -162,13 +162,23 @@ export function getLivePhotoVideoUrl(p: IPhoto, transcode: boolean) {
|
||||||
/**
|
/**
|
||||||
* Set up hooks to set classes on parent element for Live Photo
|
* Set up hooks to set classes on parent element for Live Photo
|
||||||
* @param video Video element
|
* @param video Video element
|
||||||
|
* @param parent State object to update (reactivity)
|
||||||
*/
|
*/
|
||||||
export function setupLivePhotoHooks(video: HTMLVideoElement) {
|
export function setupLivePhotoHooks(video: HTMLVideoElement, state: { playing: boolean }) {
|
||||||
const div = video.closest('.memories-livephoto') as HTMLDivElement;
|
const div = video.closest('.memories-livephoto') as HTMLDivElement;
|
||||||
|
|
||||||
|
// Playing state
|
||||||
|
video.addEventListener('playing', () => (state.playing = true));
|
||||||
video.addEventListener('play', () => div.classList.add('playing'));
|
video.addEventListener('play', () => div.classList.add('playing'));
|
||||||
video.addEventListener('canplay', () => div.classList.add('canplay'));
|
video.addEventListener('canplay', () => div.classList.add('canplay'));
|
||||||
video.addEventListener('ended', () => div.classList.remove('playing'));
|
|
||||||
video.addEventListener('pause', () => div.classList.remove('playing'));
|
// Ended or pausing state
|
||||||
|
const ended = () => {
|
||||||
|
state.playing = false;
|
||||||
|
div.classList.remove('playing');
|
||||||
|
};
|
||||||
|
video.addEventListener('ended', ended);
|
||||||
|
video.addEventListener('pause', ended);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue