From c6f5ed5b0537db909cdf7d43db2fcb193f3a07ec Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Mon, 30 Oct 2023 02:12:56 -0700 Subject: [PATCH] frame: animate live photo icon (close #898) Signed-off-by: Varun Patil --- CHANGELOG.md | 4 ++ src/components/frame/Photo.vue | 26 ++++++-- src/components/icons/LivePhoto.vue | 102 +++++++++++++++++++++++++++++ src/services/utils/helpers.ts | 13 ++-- 4 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 src/components/icons/LivePhoto.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index b36b7274..feef92c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [Unreleased] + +- Icon animation when playing live photos ([#898](https://github.com/pulsejet/memories/issues/898)) + ## [v6.0.1] - 2023-10-27 - Bug fixes in video streaming. diff --git a/src/components/frame/Photo.vue b/src/components/frame/Photo.vue index b17943da..61c6dbf4 100644 --- a/src/components/frame/Photo.vue +++ b/src/components/frame/Photo.vue @@ -24,7 +24,7 @@
- +
@@ -75,10 +75,10 @@ import { defineComponent, type PropType } from 'vue'; import * as utils from '@services/utils'; +import LivePhotoIcon from '@components/icons/LivePhoto.vue'; import CheckCircleIcon from 'vue-material-design-icons/CheckCircle.vue'; import StarIcon from 'vue-material-design-icons/Star.vue'; import VideoIcon from 'vue-material-design-icons/PlayCircleOutline.vue'; -import LivePhotoIcon from 'vue-material-design-icons/MotionPlayOutline.vue'; import LocalIcon from 'vue-material-design-icons/CloudOff.vue'; import RawIcon from 'vue-material-design-icons/Raw.vue'; @@ -90,10 +90,10 @@ import errorsvg from '@assets/error.svg'; export default defineComponent({ name: 'Photo', components: { + LivePhotoIcon, CheckCircleIcon, VideoIcon, StarIcon, - LivePhotoIcon, LocalIcon, RawIcon, }, @@ -119,7 +119,9 @@ export default defineComponent({ data: () => ({ touchTimer: 0, - playLiveTimer: 0, + livePlayTimer: 0, + liveWaiting: false, + livePlaying: false, faceSrc: null as string | null, }), @@ -139,13 +141,16 @@ export default defineComponent({ 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)); } }, /** Clear timers */ beforeDestroy() { clearTimeout(this.touchTimer); - clearTimeout(this.playLiveTimer); + clearTimeout(this.livePlayTimer); // Clean up blob url if face rect was created if (this.faceSrc) { @@ -272,9 +277,11 @@ export default defineComponent({ /** Start preview video */ playVideo() { + this.liveWaiting = true; + utils.setRenewingTimeout( this, - 'playLiveTimer', + 'livePlayTimer', async () => { const video = this.refs.video; if (!video || this.data.flag & this.c.FLAG_SELECTED) return; @@ -284,6 +291,8 @@ export default defineComponent({ await video.play(); } catch (e) { // ignore, pause was probably called too soon + } finally { + this.liveWaiting = false; } }, 400, @@ -292,8 +301,11 @@ export default defineComponent({ /** Stop preview video */ stopVideo() { - window.clearTimeout(this.playLiveTimer); this.refs.video?.pause(); + window.clearTimeout(this.livePlayTimer); + this.livePlayTimer = 0; + this.liveWaiting = false; + this.livePlaying = false; }, }, }); diff --git a/src/components/icons/LivePhoto.vue b/src/components/icons/LivePhoto.vue new file mode 100644 index 00000000..38744dc9 --- /dev/null +++ b/src/components/icons/LivePhoto.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/src/services/utils/helpers.ts b/src/services/utils/helpers.ts index 3b38fe7f..f44aa32d 100644 --- a/src/services/utils/helpers.ts +++ b/src/services/utils/helpers.ts @@ -165,15 +165,10 @@ export function getLivePhotoVideoUrl(p: IPhoto, transcode: boolean) { */ export function setupLivePhotoHooks(video: HTMLVideoElement) { const div = video.closest('.memories-livephoto') as HTMLDivElement; - video.onplay = () => { - div.classList.add('playing'); - }; - video.oncanplay = () => { - div.classList.add('canplay'); - }; - video.onended = video.onpause = () => { - div.classList.remove('playing'); - }; + 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')); } /**