Merge pull request #1304 from rinsuki/features/implement-media-video
Implement mk-media-video
This commit is contained in:
commit
4189ffd5e4
6 changed files with 179 additions and 1 deletions
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-media-list" :data-count="mediaList.length">
|
<div class="mk-media-list" :data-count="mediaList.length">
|
||||||
<template v-for="media in mediaList">
|
<template v-for="media in mediaList">
|
||||||
<mk-media-image :image="media" :key="media.id"/>
|
<mk-media-video :video="media" :key="media.id" v-if="media.type.startsWith('video')" :inline-playable="mediaList.length === 1"/>
|
||||||
|
<mk-media-image :image="media" :key="media.id" v-else />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import analogClock from './analog-clock.vue';
|
||||||
import ellipsisIcon from './ellipsis-icon.vue';
|
import ellipsisIcon from './ellipsis-icon.vue';
|
||||||
import mediaImage from './media-image.vue';
|
import mediaImage from './media-image.vue';
|
||||||
import mediaImageDialog from './media-image-dialog.vue';
|
import mediaImageDialog from './media-image-dialog.vue';
|
||||||
|
import mediaVideo from './media-video.vue';
|
||||||
import notifications from './notifications.vue';
|
import notifications from './notifications.vue';
|
||||||
import postForm from './post-form.vue';
|
import postForm from './post-form.vue';
|
||||||
import repostForm from './repost-form.vue';
|
import repostForm from './repost-form.vue';
|
||||||
|
@ -42,6 +43,7 @@ Vue.component('mk-analog-clock', analogClock);
|
||||||
Vue.component('mk-ellipsis-icon', ellipsisIcon);
|
Vue.component('mk-ellipsis-icon', ellipsisIcon);
|
||||||
Vue.component('mk-media-image', mediaImage);
|
Vue.component('mk-media-image', mediaImage);
|
||||||
Vue.component('mk-media-image-dialog', mediaImageDialog);
|
Vue.component('mk-media-image-dialog', mediaImageDialog);
|
||||||
|
Vue.component('mk-media-video', mediaVideo);
|
||||||
Vue.component('mk-notifications', notifications);
|
Vue.component('mk-notifications', notifications);
|
||||||
Vue.component('mk-post-form', postForm);
|
Vue.component('mk-post-form', postForm);
|
||||||
Vue.component('mk-repost-form', repostForm);
|
Vue.component('mk-repost-form', repostForm);
|
||||||
|
|
70
src/web/app/desktop/views/components/media-video-dialog.vue
Normal file
70
src/web/app/desktop/views/components/media-video-dialog.vue
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<div class="mk-media-video-dialog">
|
||||||
|
<div class="bg" @click="close"></div>
|
||||||
|
<video :src="video.url" :title="video.name" controls autoplay ref="video"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import * as anime from 'animejs';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['video', 'start'],
|
||||||
|
mounted() {
|
||||||
|
anime({
|
||||||
|
targets: this.$el,
|
||||||
|
opacity: 1,
|
||||||
|
duration: 100,
|
||||||
|
easing: 'linear'
|
||||||
|
});
|
||||||
|
const videoTag = this.$refs.video as HTMLVideoElement
|
||||||
|
if (this.start) videoTag.currentTime = this.start
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
close() {
|
||||||
|
anime({
|
||||||
|
targets: this.$el,
|
||||||
|
opacity: 0,
|
||||||
|
duration: 100,
|
||||||
|
easing: 'linear',
|
||||||
|
complete: () => this.$destroy()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.mk-media-video-dialog
|
||||||
|
display block
|
||||||
|
position fixed
|
||||||
|
z-index 2048
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
opacity 0
|
||||||
|
|
||||||
|
> .bg
|
||||||
|
display block
|
||||||
|
position fixed
|
||||||
|
z-index 1
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
background rgba(0, 0, 0, 0.7)
|
||||||
|
|
||||||
|
> video
|
||||||
|
position fixed
|
||||||
|
z-index 2
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
bottom 0
|
||||||
|
left 0
|
||||||
|
max-width 80vw
|
||||||
|
max-height 80vh
|
||||||
|
margin auto
|
||||||
|
|
||||||
|
</style>
|
67
src/web/app/desktop/views/components/media-video.vue
Normal file
67
src/web/app/desktop/views/components/media-video.vue
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<video class="mk-media-video"
|
||||||
|
:src="video.url"
|
||||||
|
:title="video.name"
|
||||||
|
controls
|
||||||
|
@dblclick.prevent="onClick"
|
||||||
|
ref="video"
|
||||||
|
v-if="inlinePlayable" />
|
||||||
|
<a class="mk-media-video-thumbnail"
|
||||||
|
:href="video.url"
|
||||||
|
:style="imageStyle"
|
||||||
|
@click.prevent="onClick"
|
||||||
|
:title="video.name"
|
||||||
|
v-else>
|
||||||
|
%fa:R play-circle%
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import MkMediaVideoDialog from './media-video-dialog.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['video', 'inlinePlayable'],
|
||||||
|
computed: {
|
||||||
|
imageStyle(): any {
|
||||||
|
return {
|
||||||
|
'background-image': `url(${this.video.url}?thumbnail&size=512)`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick() {
|
||||||
|
const videoTag = this.$refs.video as (HTMLVideoElement | null)
|
||||||
|
var start = 0
|
||||||
|
if (videoTag) {
|
||||||
|
start = videoTag.currentTime
|
||||||
|
videoTag.pause()
|
||||||
|
}
|
||||||
|
(this as any).os.new(MkMediaVideoDialog, {
|
||||||
|
video: this.video,
|
||||||
|
start,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.mk-media-video
|
||||||
|
display block
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
border-radius 4px
|
||||||
|
.mk-media-video-thumbnail
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
|
align-items center
|
||||||
|
font-size 3.5em
|
||||||
|
|
||||||
|
cursor zoom-in
|
||||||
|
overflow hidden
|
||||||
|
background-position center
|
||||||
|
background-size cover
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
</style>
|
|
@ -5,6 +5,7 @@ import timeline from './timeline.vue';
|
||||||
import post from './post.vue';
|
import post from './post.vue';
|
||||||
import posts from './posts.vue';
|
import posts from './posts.vue';
|
||||||
import mediaImage from './media-image.vue';
|
import mediaImage from './media-image.vue';
|
||||||
|
import mediaVideo from './media-video.vue';
|
||||||
import drive from './drive.vue';
|
import drive from './drive.vue';
|
||||||
import postPreview from './post-preview.vue';
|
import postPreview from './post-preview.vue';
|
||||||
import subPostContent from './sub-post-content.vue';
|
import subPostContent from './sub-post-content.vue';
|
||||||
|
@ -27,6 +28,7 @@ Vue.component('mk-timeline', timeline);
|
||||||
Vue.component('mk-post', post);
|
Vue.component('mk-post', post);
|
||||||
Vue.component('mk-posts', posts);
|
Vue.component('mk-posts', posts);
|
||||||
Vue.component('mk-media-image', mediaImage);
|
Vue.component('mk-media-image', mediaImage);
|
||||||
|
Vue.component('mk-media-video', mediaVideo);
|
||||||
Vue.component('mk-drive', drive);
|
Vue.component('mk-drive', drive);
|
||||||
Vue.component('mk-post-preview', postPreview);
|
Vue.component('mk-post-preview', postPreview);
|
||||||
Vue.component('mk-sub-post-content', subPostContent);
|
Vue.component('mk-sub-post-content', subPostContent);
|
||||||
|
|
36
src/web/app/mobile/views/components/media-video.vue
Normal file
36
src/web/app/mobile/views/components/media-video.vue
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<template>
|
||||||
|
<a class="mk-media-video"
|
||||||
|
:href="video.url"
|
||||||
|
target="_blank"
|
||||||
|
:style="imageStyle"
|
||||||
|
:title="video.name">
|
||||||
|
%fa:R play-circle%
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue'
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['video'],
|
||||||
|
computed: {
|
||||||
|
imageStyle(): any {
|
||||||
|
return {
|
||||||
|
'background-image': `url(${this.video.url}?thumbnail&size=512)`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.mk-media-video
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
|
align-items center
|
||||||
|
|
||||||
|
font-size 3.5em
|
||||||
|
overflow hidden
|
||||||
|
background-position center
|
||||||
|
background-size cover
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
</style>
|
Loading…
Reference in a new issue