viewer: animate lp icon

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/900/head
Varun Patil 2023-10-30 02:33:46 -07:00
parent c6f5ed5b05
commit 5ccba14519
4 changed files with 48 additions and 27 deletions

View File

@ -24,7 +24,7 @@
<VideoIcon :size="22" />
</div>
<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>
<RawIcon class="raw" v-if="isRaw" :size="28" />
</div>
@ -119,9 +119,11 @@ export default defineComponent({
data: () => ({
touchTimer: 0,
livePlayTimer: 0,
liveWaiting: false,
livePlaying: false,
liveState: {
playTimer: 0,
playing: false,
waiting: false,
},
faceSrc: null as string | null,
}),
@ -140,17 +142,14 @@ export default defineComponent({
// Setup video hooks
const video = this.refs.video;
if (video) {
utils.setupLivePhotoHooks(video);
video.addEventListener('playing', () => (this.livePlaying = true));
video.addEventListener('pause', () => (this.livePlaying = false));
video.addEventListener('ended', () => (this.livePlaying = false));
utils.setupLivePhotoHooks(video, this.liveState);
}
},
/** Clear timers */
beforeDestroy() {
clearTimeout(this.touchTimer);
clearTimeout(this.livePlayTimer);
clearTimeout(this.liveState.playTimer);
// Clean up blob url if face rect was created
if (this.faceSrc) {
@ -277,11 +276,11 @@ export default defineComponent({
/** Start preview video */
playVideo() {
this.liveWaiting = true;
this.liveState.waiting = true;
utils.setRenewingTimeout(
this,
'livePlayTimer',
this.liveState,
'playTimer',
async () => {
const video = this.refs.video;
if (!video || this.data.flag & this.c.FLAG_SELECTED) return;
@ -292,7 +291,7 @@ export default defineComponent({
} catch (e) {
// ignore, pause was probably called too soon
} finally {
this.liveWaiting = false;
this.liveState.waiting = false;
}
},
400,
@ -302,10 +301,9 @@ export default defineComponent({
/** Stop preview video */
stopVideo() {
this.refs.video?.pause();
window.clearTimeout(this.livePlayTimer);
this.livePlayTimer = 0;
this.liveWaiting = false;
this.livePlaying = false;
window.clearTimeout(this.liveState.playTimer);
this.liveState.playTimer = 0;
this.liveState.waiting = false;
},
},
});

View File

@ -20,6 +20,7 @@ class LivePhotoContentSetup {
constructor(
lightbox: PhotoSwipe,
private psImage: PsImage,
private liveState: { playing: boolean; waiting: boolean },
) {
lightbox.on('contentLoad', this.onContentLoad.bind(this));
lightbox.on('contentActivate', this.onContentActivate.bind(this));
@ -32,10 +33,13 @@ class LivePhotoContentSetup {
if (!video) return;
try {
this.liveState.waiting = true;
video.currentTime = 0;
await video.play();
} catch (e) {
// ignore, pause was probably called too soon
} finally {
this.liveState.waiting = false;
}
}
@ -60,7 +64,7 @@ class LivePhotoContentSetup {
div.appendChild(video);
content.element = div;
utils.setupLivePhotoHooks(video);
utils.setupLivePhotoHooks(video, this.liveState);
const img = this.psImage.getXImgElem(content, () => content.onLoaded());
div.appendChild(img);

View File

@ -55,7 +55,9 @@
:close-after-click="true"
>
{{ 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
v-if="!routeIsPublic && !isLocal"
@ -194,6 +196,7 @@ import PsLivePhoto from './PsLivePhoto';
import type { IImageInfo, IPhoto, TimelineState } from '@typings';
import type { PsContent } from './types';
import LivePhotoIcon from '@components/icons/LivePhoto.vue';
import ShareIcon from 'vue-material-design-icons/ShareVariant.vue';
import DeleteIcon from 'vue-material-design-icons/TrashCanOutline.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 EditFileIcon from 'vue-material-design-icons/FileEdit.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';
const SLIDESHOW_MS = 5000;
@ -220,6 +222,7 @@ export default defineComponent({
NcActions,
NcActionButton,
ImageEditor,
LivePhotoIcon,
ShareIcon,
DeleteIcon,
StarIcon,
@ -231,7 +234,6 @@ export default defineComponent({
SlideshowIcon,
EditFileIcon,
AlbumRemoveIcon,
LivePhotoIcon,
AlbumIcon,
},
@ -260,6 +262,13 @@ export default defineComponent({
psImage: null as PsImage | null,
psLivePhoto: null as PsLivePhoto | null,
/** Live photo state */
liveState: {
playing: false,
waiting: false,
},
/** List globals */
list: [] as IPhoto[],
globalCount: 0,
globalAnchor: -1,
@ -590,15 +599,15 @@ export default defineComponent({
});
// Video support
this.psVideo = new PsVideo(<PhotoSwipe>this.photoswipe, {
this.psVideo = new PsVideo(<any>this.photoswipe, {
preventDragOffset: 40,
});
// Image support
this.psImage = new PsImage(<PhotoSwipe>this.photoswipe);
this.psImage = new PsImage(<any>this.photoswipe);
// 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
const _close = this.photoswipe.close.bind(this.photoswipe);

View File

@ -162,13 +162,23 @@ export function getLivePhotoVideoUrl(p: IPhoto, transcode: boolean) {
/**
* Set up hooks to set classes on parent element for Live Photo
* @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;
// Playing state
video.addEventListener('playing', () => (state.playing = true));
video.addEventListener('play', () => div.classList.add('playing'));
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);
}
/**