diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2020-07-19 00:24:07 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2020-07-19 00:24:07 +0900 |
| commit | 3f71b1463719bee476d39b7ceca5a2eea4b5cb67 (patch) | |
| tree | 70003fc0a220785526289fd5b46d8c708a59c21d /src/client | |
| parent | fix(client): プラグインの動作を修正 (diff) | |
| download | sharkey-3f71b1463719bee476d39b7ceca5a2eea4b5cb67.tar.gz sharkey-3f71b1463719bee476d39b7ceca5a2eea4b5cb67.tar.bz2 sharkey-3f71b1463719bee476d39b7ceca5a2eea4b5cb67.zip | |
feat: Blurhash integration
Resolve #6559
Diffstat (limited to 'src/client')
| -rw-r--r-- | src/client/components/avatar.vue | 38 | ||||
| -rw-r--r-- | src/client/components/drive-file-thumbnail.vue | 85 | ||||
| -rw-r--r-- | src/client/components/drive.file.vue | 12 | ||||
| -rw-r--r-- | src/client/components/img-with-blurhash.vue | 78 | ||||
| -rw-r--r-- | src/client/components/media-image.vue | 91 | ||||
| -rw-r--r-- | src/client/components/media-list.vue | 2 | ||||
| -rw-r--r-- | src/client/pages/messaging/messaging-room.message.vue | 3 | ||||
| -rw-r--r-- | src/client/pages/page-editor/els/page-editor.el.image.vue | 2 | ||||
| -rw-r--r-- | src/client/style.scss | 4 |
9 files changed, 154 insertions, 161 deletions
diff --git a/src/client/components/avatar.vue b/src/client/components/avatar.vue index 29b457db87..fd4ab78ce1 100644 --- a/src/client/components/avatar.vue +++ b/src/client/components/avatar.vue @@ -1,15 +1,9 @@ <template> -<span class="eiwwqkts" :class="{ cat }" :title="user | acct" v-if="disableLink && !disablePreview" v-user-preview="user.id" @click="onClick"> - <span class="inner" :style="icon"></span> +<span class="eiwwqkts" :class="{ cat }" :title="user | acct" v-if="disableLink" v-user-preview="disablePreview ? undefined : user.id" @click="onClick"> + <img class="inner" :src="url"/> </span> -<span class="eiwwqkts" :class="{ cat }" :title="user | acct" v-else-if="disableLink && disablePreview" @click="onClick"> - <span class="inner" :style="icon"></span> -</span> -<router-link class="eiwwqkts" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && !disablePreview" v-user-preview="user.id"> - <span class="inner" :style="icon"></span> -</router-link> -<router-link class="eiwwqkts" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else-if="!disableLink && disablePreview"> - <span class="inner" :style="icon"></span> +<router-link class="eiwwqkts" :class="{ cat }" :to="user | userPage" :title="user | acct" :target="target" v-else v-user-preview="disablePreview ? undefined : user.id"> + <img class="inner" :src="url"/> </router-link> </template> @@ -45,22 +39,6 @@ export default Vue.extend({ ? getStaticImageUrl(this.user.avatarUrl) : this.user.avatarUrl; }, - icon(): any { - return { - backgroundColor: this.user.avatarColor, - backgroundImage: `url(${this.url})`, - }; - } - }, - watch: { - 'user.avatarColor'() { - this.$el.style.color = this.user.avatarColor; - } - }, - mounted() { - if (this.user.avatarColor) { - this.$el.style.color = this.user.avatarColor; - } }, methods: { onClick(e) { @@ -102,15 +80,17 @@ export default Vue.extend({ } .inner { - background-position: center center; - background-size: cover; + position: absolute; bottom: 0; left: 0; - position: absolute; right: 0; top: 0; border-radius: 100%; z-index: 1; + overflow: hidden; + object-fit: cover; + width: 100%; + height: 100%; } } </style> diff --git a/src/client/components/drive-file-thumbnail.vue b/src/client/components/drive-file-thumbnail.vue index 3561be0bc5..4bc1e569b7 100644 --- a/src/client/components/drive-file-thumbnail.vue +++ b/src/client/components/drive-file-thumbnail.vue @@ -1,36 +1,15 @@ <template> -<div class="zdjebgpv" :class="{ detail }" ref="thumbnail" :style="`background-color: ${ background }`"> - <img - :src="file.url" - :alt="file.name" - :title="file.name" - @load="onThumbnailLoaded" - v-if="detail && is === 'image'"/> - <video - :src="file.url" - ref="volumectrl" - preload="metadata" - controls - v-else-if="detail && is === 'video'"/> - <img :src="file.thumbnailUrl" @load="onThumbnailLoaded" :style="`object-fit: ${ fit }`" v-else-if="isThumbnailAvailable"/> +<div class="zdjebgpv" ref="thumbnail"> + <img-with-blurhash v-if="isThumbnailAvailable" :hash="file.blurhash" :src="file.thumbnailUrl" :alt="file.name" :title="file.name" :style="`object-fit: ${ fit }`"/> <fa :icon="faFileImage" class="icon" v-else-if="is === 'image'"/> <fa :icon="faFileVideo" class="icon" v-else-if="is === 'video'"/> - - <audio - :src="file.url" - ref="volumectrl" - preload="metadata" - controls - v-else-if="detail && is === 'audio'"/> <fa :icon="faMusic" class="icon" v-else-if="is === 'audio' || is === 'midi'"/> - <fa :icon="faFileCsv" class="icon" v-else-if="is === 'csv'"/> <fa :icon="faFilePdf" class="icon" v-else-if="is === 'pdf'"/> <fa :icon="faFileAlt" class="icon" v-else-if="is === 'textfile'"/> <fa :icon="faFileArchive" class="icon" v-else-if="is === 'archive'"/> <fa :icon="faFile" class="icon" v-else/> - - <fa :icon="faFilm" class="icon-sub" v-if="!detail && isThumbnailAvailable && is === 'video'"/> + <fa :icon="faFilm" class="icon-sub" v-if="isThumbnailAvailable && is === 'video'"/> </div> </template> @@ -47,8 +26,12 @@ import { faFileArchive, faFilm } from '@fortawesome/free-solid-svg-icons'; +import ImgWithBlurhash from './img-with-blurhash.vue'; export default Vue.extend({ + components: { + ImgWithBlurhash + }, props: { file: { type: Object, @@ -59,11 +42,6 @@ export default Vue.extend({ required: false, default: 'cover' }, - detail: { - type: Boolean, - required: false, - default: false - } }, data() { return { @@ -108,20 +86,12 @@ export default Vue.extend({ ? (this.is === 'image' || this.is === 'video') : false; }, - background(): string { - return this.file.properties.avgColor || 'transparent'; - } }, mounted() { const audioTag = this.$refs.volumectrl as HTMLAudioElement; if (audioTag) audioTag.volume = this.$store.state.device.mediaVolume; }, methods: { - onThumbnailLoaded() { - if (this.file.properties.avgColor) { - this.$refs.thumbnail.style.backgroundColor = 'transparent'; - } - }, volumechange() { const audioTag = this.$refs.volumectrl as HTMLAudioElement; this.$store.commit('device/set', { key: 'mediaVolume', value: audioTag.volume }); @@ -132,14 +102,8 @@ export default Vue.extend({ <style lang="scss" scoped> .zdjebgpv { - display: flex; position: relative; - > img, - > .icon { - pointer-events: none; - } - > .icon-sub { position: absolute; width: 30%; @@ -153,37 +117,10 @@ export default Vue.extend({ margin: auto; } - &:not(.detail) { - > img { - height: 100%; - width: 100%; - object-fit: cover; - } - - > .icon { - height: 65%; - width: 65%; - } - - > video, - > audio { - width: 100%; - } - } - - &.detail { - > .icon { - height: 100px; - width: 100px; - margin: 16px; - } - - > *:not(.icon) { - max-height: 300px; - max-width: 100%; - height: 100%; - object-fit: contain; - } + > .icon { + pointer-events: none; + height: 65%; + width: 65%; } } </style> diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue index 1b24c61df5..b31a4e6375 100644 --- a/src/client/components/drive.file.vue +++ b/src/client/components/drive.file.vue @@ -126,17 +126,6 @@ export default Vue.extend({ this.browser.isDragSource = false; }, - onThumbnailLoaded() { - if (this.file.properties.avgColor) { - anime({ - targets: this.$refs.thumbnail, - backgroundColor: 'transparent', // TODO fade - duration: 100, - easing: 'linear' - }); - } - }, - rename() { this.$root.dialog({ title: this.$t('renameFile'), @@ -332,7 +321,6 @@ export default Vue.extend({ width: 128px; height: 128px; margin: auto; - color: var(--driveFileIcon); } > .name { diff --git a/src/client/components/img-with-blurhash.vue b/src/client/components/img-with-blurhash.vue new file mode 100644 index 0000000000..6e6a2a8965 --- /dev/null +++ b/src/client/components/img-with-blurhash.vue @@ -0,0 +1,78 @@ +<template> +<div class="xubzgfgb" :title="title"> + <canvas ref="canvas" :width="size" :height="size" :title="title" v-if="!loaded"/> + <img v-if="src" :src="src" :title="title" :alt="alt" @load="onLoad"/> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import { decode } from 'blurhash'; + +export default Vue.extend({ + props: { + src: { + type: String, + required: false, + default: null + }, + hash: { + type: String, + required: true + }, + alt: { + type: String, + required: false, + default: '', + }, + title: { + type: String, + required: false, + default: null, + }, + size: { + type: Number, + required: false, + default: 64 + }, + }, + + data() { + return { + loaded: false, + }; + }, + + mounted() { + this.draw(); + }, + + methods: { + draw() { + const pixels = decode(this.hash, this.size, this.size); + const ctx = (this.$refs.canvas as HTMLCanvasElement).getContext('2d'); + const imageData = ctx!.createImageData(this.size, this.size); + imageData.data.set(pixels); + ctx!.putImageData(imageData, 0, 0); + }, + + onLoad() { + this.loaded = true; + } + } +}); +</script> + +<style lang="scss" scoped> +.xubzgfgb { + width: 100%; + height: 100%; + + > canvas, + > img { + width: 100%; + height: 100%; + object-fit: cover; + } +} +</style> diff --git a/src/client/components/media-image.vue b/src/client/components/media-image.vue index 6d1b5345de..f6ed45daec 100644 --- a/src/client/components/media-image.vue +++ b/src/client/components/media-image.vue @@ -1,19 +1,22 @@ <template> -<div class="qjewsnkgzzxlxtzncydssfbgjibiehcy" v-if="hide" @click="hide = false"> - <div> - <b><fa :icon="faExclamationTriangle"/> {{ $t('sensitive') }}</b> - <span>{{ $t('clickToShow') }}</span> +<div class="qjewsnkg" v-if="hide" @click="hide = false"> + <img-with-blurhash class="bg" :hash="image.blurhash" :title="image.name"/> + <div class="text"> + <div> + <b><fa :icon="faExclamationTriangle"/> {{ $t('sensitive') }}</b> + <span>{{ $t('clickToShow') }}</span> + </div> </div> </div> -<div class="gqnyydlzavusgskkfvwvjiattxdzsqlf" v-else> +<div class="gqnyydlz" v-else> <i><fa :icon="faEyeSlash" @click="hide = true"/></i> <a :href="image.url" - :style="style" :title="image.name" @click.prevent="onClick" > - <div v-if="image.type === 'image/gif'">GIF</div> + <img-with-blurhash :hash="image.blurhash" :src="url" :alt="image.name" :title="image.name"/> + <div class="gif" v-if="image.type === 'image/gif'">GIF</div> </a> </div> </template> @@ -23,8 +26,12 @@ import Vue from 'vue'; import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons'; import { getStaticImageUrl } from '../scripts/get-static-image-url'; import ImageViewer from './image-viewer.vue'; +import ImgWithBlurhash from './img-with-blurhash.vue'; export default Vue.extend({ + components: { + ImgWithBlurhash + }, props: { image: { type: Object, @@ -42,23 +49,18 @@ export default Vue.extend({ }; }, computed: { - style(): any { - let url = `url(${ - this.$store.state.device.disableShowingAnimatedImages - ? getStaticImageUrl(this.image.thumbnailUrl) - : this.image.thumbnailUrl - })`; + url(): any { + let url = this.$store.state.device.disableShowingAnimatedImages + ? getStaticImageUrl(this.image.thumbnailUrl) + : this.image.thumbnailUrl; if (this.$store.state.device.loadRemoteMedia) { url = null; } else if (this.raw || this.$store.state.device.loadRawImages) { - url = `url(${this.image.url})`; + url = this.image.url; } - return { - 'background-color': this.image.properties.avgColor || 'transparent', - 'background-image': url - }; + return url; } }, created() { @@ -82,7 +84,38 @@ export default Vue.extend({ </script> <style lang="scss" scoped> -.gqnyydlzavusgskkfvwvjiattxdzsqlf { +.qjewsnkg { + position: relative; + + > .bg { + filter: brightness(0.5); + } + + > .text { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + z-index: 1; + display: flex; + justify-content: center; + align-items: center; + + > div { + display: table-cell; + text-align: center; + font-size: 0.8em; + color: #fff; + + > * { + display: block; + } + } + } +} + +.gqnyydlz { position: relative; > i { @@ -110,7 +143,7 @@ export default Vue.extend({ background-size: contain; background-repeat: no-repeat; - > div { + > .gif { background-color: var(--fg); border-radius: 6px; color: var(--accentLighten); @@ -126,22 +159,4 @@ export default Vue.extend({ } } } - -.qjewsnkgzzxlxtzncydssfbgjibiehcy { - display: flex; - justify-content: center; - align-items: center; - background: #111; - color: #fff; - - > div { - display: table-cell; - text-align: center; - font-size: 12px; - - > * { - display: block; - } - } -} </style> diff --git a/src/client/components/media-list.vue b/src/client/components/media-list.vue index c757d80911..fd0035f10c 100644 --- a/src/client/components/media-list.vue +++ b/src/client/components/media-list.vue @@ -114,7 +114,7 @@ export default Vue.extend({ > * { overflow: hidden; - border-radius: 4px; + border-radius: 6px; } &[data-count="1"] { diff --git a/src/client/pages/messaging/messaging-room.message.vue b/src/client/pages/messaging/messaging-room.message.vue index 58e1e54ad8..4461740df7 100644 --- a/src/client/pages/messaging/messaging-room.message.vue +++ b/src/client/pages/messaging/messaging-room.message.vue @@ -10,8 +10,7 @@ <mfm class="text" v-if="message.text" ref="text" :text="message.text" :i="$store.state.i"/> <div class="file" v-if="message.file"> <a :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name"> - <img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name" - :style="{ backgroundColor: message.file.properties.avgColor || 'transparent' }"/> + <img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/> <p v-else>{{ message.file.name }}</p> </a> </div> diff --git a/src/client/pages/page-editor/els/page-editor.el.image.vue b/src/client/pages/page-editor/els/page-editor.el.image.vue index dd690da6f1..d26d7f603f 100644 --- a/src/client/pages/page-editor/els/page-editor.el.image.vue +++ b/src/client/pages/page-editor/els/page-editor.el.image.vue @@ -8,7 +8,7 @@ </template> <section class="oyyftmcf"> - <mk-file-thumbnail class="preview" v-if="file" :file="file" :detail="true" fit="contain" @click="choose()"/> + <mk-file-thumbnail class="preview" v-if="file" :file="file" fit="contain" @click="choose()"/> </section> </x-container> </template> diff --git a/src/client/style.scss b/src/client/style.scss index 972c38338c..c3d3cf2233 100644 --- a/src/client/style.scss +++ b/src/client/style.scss @@ -123,10 +123,6 @@ a { &:hover { text-decoration: underline; } - - * { - cursor: pointer; - } } hr { |