feat: use local image for wallpaper && change broken image style (#851)

* feat: 新增自定义背景图片和图片error显示

* chore: update broken image icon

* feat: improve customize wallpaper function

* chore: fix ts error

* chore: temporarily disable broken images

* chore: update upload style

* refactor: Add ChangeWallpaper component for customizing wallpaper in SearchPage and globally

* refactor: Update ChangeWallpaper component to handle wallpaper customization in SearchPage and globally

---------

Co-authored-by: pengyunfei <pengyunfei@360.cn>
Co-authored-by: Hakadao <a578457889743@gmail.com>
This commit is contained in:
cloudflypeng
2024-06-21 00:29:09 +08:00
committed by GitHub
parent 34eb136b36
commit 586b503268
7 changed files with 285 additions and 206 deletions

BIN
assets/broken-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -2,14 +2,10 @@
import { useThrottleFn } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import Input from '~/components/Input.vue'
import Radio from '~/components/Radio.vue'
import Select from '~/components/Select.vue'
import Slider from '~/components/Slider.vue'
import Tooltip from '~/components/Tooltip.vue'
import { WALLPAPERS } from '~/constants/imgs'
import { settings } from '~/logic'
import ChangeWallpaper from './ChangeWallpaper.vue'
import SettingsItem from './SettingsItem.vue'
import SettingsItemGroup from './SettingsItemGroup.vue'
@@ -122,99 +118,9 @@ function changeWallpaper(url: string) {
</SettingsItem>
</SettingsItemGroup>
<SettingsItemGroup :title="$t('settings.group_wallpaper')">
<SettingsItem :title="$t('settings.wallpaper_mode')" :desc="$t('settings.wallpaper_mode_desc')">
<div w-full flex rounded="$bew-radius" bg="$bew-fill-1" p-1>
<div
flex-1 py-1 cursor-pointer text-center rounded="$bew-radius"
:style="{
background: settings.wallpaperMode === 'buildIn' ? 'var(--bew-theme-color)' : '',
color: settings.wallpaperMode === 'buildIn' ? 'white' : '',
}"
@click="settings.wallpaperMode = 'buildIn'"
>
{{ $t('settings.wallpaper_mode_opt.build_in') }}
</div>
<div
flex-1 py-1 cursor-pointer text-center rounded="$bew-radius"
:style="{
background: settings.wallpaperMode === 'byUrl' ? 'var(--bew-theme-color)' : '',
color: settings.wallpaperMode === 'byUrl' ? 'white' : '',
}"
@click="settings.wallpaperMode = 'byUrl'"
>
{{ $t('settings.wallpaper_mode_opt.by_url') }}
</div>
</div>
</SettingsItem>
<SettingsItem v-if="settings.wallpaperMode === 'buildIn'" :title="$t('settings.choose_ur_wallpaper')" next-line>
<div grid="~ xl:cols-5 lg:cols-4 cols-3 gap-4">
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" cursor-pointer
grid place-items-center
:class="{ 'selected-wallpaper': settings.wallpaper === '' }"
@click="changeWallpaper('')"
>
<div i-tabler:photo-off text="3xl $bew-text-3" />
</picture>
<Tooltip v-for="item in WALLPAPERS" :key="item.url" placement="top" :content="item.name" aspect-video>
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" w-full
:class="{ 'selected-wallpaper': settings.wallpaper === item.url }"
@click="changeWallpaper(item.url)"
>
<img :src="item.thumbnail" alt="" w-full h-full object-cover>
</picture>
</Tooltip>
</div>
</SettingsItem>
<SettingsItem v-else :title="$t('settings.image_url')" next-line>
<div flex items-center gap-4>
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" cursor-pointer shrink-0
w="xl:1/5 lg:1/4 md:1/3"
>
<img
v-if="settings.wallpaper" :src="settings.wallpaper" alt="" loading="lazy"
w-full h-full object-cover
onerror="this.style.display='none'; this.onerror=null;"
>
</picture>
<div>
<Input v-model="settings.wallpaper" w-full />
<p color="sm $bew-text-3" mt-2>
{{ $t('settings.image_url_hint') }}
</p>
</div>
</div>
</SettingsItem>
<SettingsItem :title="$t('settings.enable_wallpaper_masking')">
<template #desc>
<span color="$bew-warning-color">{{ $t('common.performance_impact_warn') }}</span>
</template>
<Radio v-model="settings.enableWallpaperMasking" />
</SettingsItem>
<SettingsItem v-if="settings.enableWallpaperMasking" :title="$t('settings.wallpaper_mask_opacity')">
<Slider v-model="settings.wallpaperMaskOpacity" :label="`${settings.wallpaperMaskOpacity}%`" />
</SettingsItem>
<SettingsItem v-if="settings.enableWallpaperMasking" :title="$t('settings.wallpaper_blur_intensity')">
<template #desc>
<span color="$bew-warning-color">{{ $t('common.performance_impact_warn') }}</span>
</template>
<Slider v-model="settings.wallpaperBlurIntensity" :min="0" :max="60" :label="`${settings.wallpaperBlurIntensity}px`" />
</SettingsItem>
</SettingsItemGroup>
<ChangeWallpaper type="global" />
</div>
</template>
<style lang="scss" scoped>
.selected-wallpaper {
--uno: "border-$bew-theme-color-60";
}
</style>

View File

@@ -0,0 +1,243 @@
<script setup lang="ts">
import Input from '~/components/Input.vue'
import Radio from '~/components/Radio.vue'
import Slider from '~/components/Slider.vue'
import Tooltip from '~/components/Tooltip.vue'
import { WALLPAPERS } from '~/constants/imgs'
import { settings } from '~/logic'
import SettingsItem from './SettingsItem.vue'
import SettingsItemGroup from './SettingsItemGroup.vue'
const props = defineProps<Props>()
interface Props {
type: 'global' | 'searchPage'
}
const uploadWallpaperRef = ref(null)
const isGlobal = computed(() => props.type === 'global')
const isBuildInWallpaper = computed(() => {
return isGlobal.value ? settings.value.wallpaperMode === 'buildIn' : settings.value.searchPageWallpaperMode === 'buildIn'
})
function changeWallpaperType(type: 'buildIn' | 'byUrl') {
if (isGlobal.value) {
settings.value.wallpaperMode = type
}
else {
settings.value.searchPageWallpaperMode = type
}
}
function changeWallpaper(url: string) {
if (isGlobal.value) {
// If you had already set the wallpaper, it enables the wallpaper masking to prevent text hard to see
if (url)
settings.value.enableWallpaperMasking = true
else
settings.value.enableWallpaperMasking = false
settings.value.wallpaper = url
}
else {
settings.value.searchPageWallpaper = url
}
}
function fileToBase64(inputFile: File) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(inputFile)
reader.onload = () => resolve(reader.result)
reader.onerror = error => reject(error)
})
}
function handleUploadWallpaper(e: Event) {
if (uploadWallpaperRef.value)
(uploadWallpaperRef.value as HTMLInputElement).click()
const file = (e.target as HTMLInputElement)?.files?.[0]
if (!file)
return
// img to base64 on browser
fileToBase64(file).then((base64) => {
// 使用base64字符串
changeWallpaper(base64 as string)
settings.value.customizeWallpaper = {
name: file.name,
url: base64 as string,
}
}).catch(() => {
// 处理错误
})
}
function handleRemoveCustomWallpaper() {
changeWallpaper('')
settings.value.customizeWallpaper = null
}
</script>
<template>
<SettingsItemGroup :title="$t('settings.group_wallpaper')">
<SettingsItem :title="$t('settings.wallpaper_mode')" :desc="$t('settings.wallpaper_mode_desc')">
<div w-full flex rounded="$bew-radius" bg="$bew-fill-1" p-1>
<div
flex-1 py-1 cursor-pointer text-center rounded="$bew-radius"
:style="{
background: isBuildInWallpaper ? 'var(--bew-theme-color)' : '',
color: isBuildInWallpaper ? 'white' : '',
}"
@click="changeWallpaperType('buildIn')"
>
{{ $t('settings.wallpaper_mode_opt.build_in') }}
</div>
<div
flex-1 py-1 cursor-pointer text-center rounded="$bew-radius"
:style="{
background: !isBuildInWallpaper ? 'var(--bew-theme-color)' : '',
color: !isBuildInWallpaper ? 'white' : '',
}"
@click="changeWallpaperType('byUrl')"
>
{{ $t('settings.wallpaper_mode_opt.by_url') }}
</div>
</div>
</SettingsItem>
<SettingsItem v-if="isBuildInWallpaper" :title="$t('settings.choose_ur_wallpaper')" next-line>
<div grid="~ xl:cols-5 lg:cols-4 cols-3 gap-4">
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" cursor-pointer
grid place-items-center
:class="{ 'selected-wallpaper': isGlobal ? settings.wallpaper === '' : settings.searchPageWallpaper === '' }"
@click="changeWallpaper('')"
>
<div i-tabler:photo-off text="3xl $bew-text-3" />
</picture>
<!-- Upload wallpaper input -->
<input
ref="uploadWallpaperRef" type="file" accept="image/*" hidden
@change="handleUploadWallpaper"
>
<Tooltip placement="top" :content="settings.customizeWallpaper?.name || ''" aspect-video>
<picture
class="group"
:class="{ 'selected-wallpaper': isGlobal
? settings.wallpaper === settings.customizeWallpaper?.url
: settings.searchPageWallpaper === settings.customizeWallpaper?.url }"
aspect-video bg="$bew-theme-color-20" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" w-full
flex="~ items-center justify-center"
@click="changeWallpaper(settings.customizeWallpaper?.url || '')"
>
<div
v-if="settings.customizeWallpaper"
class="opacity-0 group-hover:opacity-100" duration-300
pos="absolute top-4px right-4px" z-1 text="14px" flex="~ gap-1"
>
<button
bg="$bew-content-1" rounded-full w-28px h-28px
grid place-items-center
@click="handleUploadWallpaper"
>
<i i-mingcute:edit-2-line />
</button>
<button
bg="$bew-content-1" rounded-full w-28px h-28px
grid place-items-center
@click="handleRemoveCustomWallpaper"
>
<i i-mingcute:delete-2-line />
</button>
</div>
<div
v-if="!settings.customizeWallpaper"
absolute w-full h-full grid place-items-center
@click="handleUploadWallpaper"
>
<div
i-tabler:photo-up
text="3xl $bew-theme-color"
/>
</div>
<img
v-else
:src="settings.customizeWallpaper.thumbnail || settings.customizeWallpaper.url"
:alt="settings.customizeWallpaper.name"
w-full h-full object-cover
>
</picture>
</Tooltip>
<Tooltip v-for="item in WALLPAPERS" :key="item.url" placement="top" :content="item.name" aspect-video>
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" w-full
:class="{ 'selected-wallpaper': isGlobal ? settings.wallpaper === item.url : settings.searchPageWallpaper === item.url }"
@click="changeWallpaper(item.url)"
>
<img
:src="item.thumbnail || item.url"
:alt="item.name"
w-full h-full object-cover
class="img-no-error"
>
</picture>
</Tooltip>
</div>
</SettingsItem>
<SettingsItem v-else :title="$t('settings.image_url')" next-line>
<div flex items-center gap-4>
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" cursor-pointer shrink-0
w="xl:1/5 lg:1/4 md:1/3"
>
<img
v-if="isGlobal ? settings.wallpaper : settings.searchPageWallpaper"
:src="isGlobal ? settings.wallpaper : settings.searchPageWallpaper" alt="" loading="lazy"
w-full h-full object-cover
onerror="this.style.display='none'; this.onerror=null;"
>
</picture>
<div>
<Input v-if="isGlobal" v-model="settings.wallpaper" w-full />
<Input v-else v-model="settings.searchPageWallpaper" w-full />
<p color="sm $bew-text-3" mt-2>
{{ $t('settings.image_url_hint') }}
</p>
</div>
</div>
</SettingsItem>
<SettingsItem :title="$t('settings.enable_wallpaper_masking')">
<template #desc>
<span color="$bew-warning-color">{{ $t('common.performance_impact_warn') }}</span>
</template>
<Radio v-if="isGlobal" v-model="settings.enableWallpaperMasking" />
<Radio v-else v-model="settings.searchPageEnableWallpaperMasking" />
</SettingsItem>
<SettingsItem v-if="isGlobal ? settings.enableWallpaperMasking : settings.searchPageEnableWallpaperMasking" :title="$t('settings.wallpaper_mask_opacity')">
<Slider v-if="isGlobal" v-model="settings.wallpaperMaskOpacity" :label="`${settings.wallpaperMaskOpacity}%`" />
<Slider v-else v-model="settings.searchPageWallpaperMaskOpacity" :label="`${settings.searchPageWallpaperMaskOpacity}%`" />
</SettingsItem>
<SettingsItem v-if="isGlobal ? settings.enableWallpaperMasking : settings.searchPageEnableWallpaperMasking" :title="$t('settings.wallpaper_blur_intensity')">
<template #desc>
<span color="$bew-warning-color">{{ $t('common.performance_impact_warn') }}</span>
</template>
<Slider v-if="isGlobal" v-model="settings.wallpaperBlurIntensity" :min="0" :max="60" :label="`${settings.wallpaperBlurIntensity}px`" />
<Slider v-else v-model="settings.searchPageWallpaperBlurIntensity" :min="0" :max="60" :label="`${settings.searchPageWallpaperBlurIntensity}px`" />
</SettingsItem>
</SettingsItemGroup>
</template>
<style lang="scss" scoped>
.selected-wallpaper {
--uno: "border-$bew-theme-color-60";
}
</style>

View File

@@ -1,11 +1,10 @@
<script lang="ts" setup>
import Input from '~/components/Input.vue'
import Radio from '~/components/Radio.vue'
import Slider from '~/components/Slider.vue'
import Tooltip from '~/components/Tooltip.vue'
import { SEARCH_BAR_CHARACTERS, WALLPAPERS } from '~/constants/imgs'
import { SEARCH_BAR_CHARACTERS } from '~/constants/imgs'
import { settings } from '~/logic'
import ChangeWallpaper from './ChangeWallpaper.vue'
import SettingsItem from './SettingsItem.vue'
import SettingsItemGroup from './SettingsItemGroup.vue'
@@ -19,10 +18,6 @@ watch(() => settings.value.individuallySetSearchPageWallpaper, (newValue) => {
function changeSearchBarFocusCharacter(url: string) {
settings.value.searchPageSearchBarFocusCharacter = url
}
function changeWallpaper(url: string) {
settings.value.searchPageWallpaper = url
}
</script>
<template>
@@ -113,93 +108,7 @@ function changeWallpaper(url: string) {
<Radio v-model="settings.individuallySetSearchPageWallpaper" />
</SettingsItem>
<template v-if="settings.individuallySetSearchPageWallpaper">
<SettingsItem :title="$t('settings.wallpaper_mode')" :desc="$t('settings.wallpaper_mode_desc')">
<div w-full flex rounded="$bew-radius" bg="$bew-fill-1" p-1>
<div
flex-1 py-1 cursor-pointer text-center rounded="$bew-radius"
:style="{
background: settings.searchPageWallpaperMode === 'buildIn' ? 'var(--bew-theme-color)' : '',
color: settings.searchPageWallpaperMode === 'buildIn' ? 'white' : '',
}"
@click="settings.searchPageWallpaperMode = 'buildIn'"
>
{{ $t('settings.wallpaper_mode_opt.build_in') }}
</div>
<div
flex-1 py-1 cursor-pointer text-center rounded="$bew-radius"
:style="{
background: settings.searchPageWallpaperMode === 'byUrl' ? 'var(--bew-theme-color)' : '',
color: settings.searchPageWallpaperMode === 'byUrl' ? 'white' : '',
}"
@click="settings.searchPageWallpaperMode = 'byUrl'"
>
{{ $t('settings.wallpaper_mode_opt.by_url') }}
</div>
</div>
</SettingsItem>
<SettingsItem v-if="settings.searchPageWallpaperMode === 'buildIn'" :title="$t('settings.choose_ur_wallpaper')" next-line>
<div grid="~ xl:cols-5 lg:cols-4 cols-3 gap-4">
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" cursor-pointer
grid place-items-center
:class="{ 'selected-wallpaper': settings.searchPageWallpaper === '' }"
@click="changeWallpaper('')"
>
<div i-tabler:photo-off text="3xl $bew-text-3" />
</picture>
<Tooltip v-for="item in WALLPAPERS" :key="item.url" placement="top" :content="item.name" aspect-video>
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" w-full
:class="{ 'selected-wallpaper': settings.searchPageWallpaper === item.url }"
@click="changeWallpaper(item.url)"
>
<img :src="item.thumbnail" alt="" w-full h-full object-cover>
</picture>
</Tooltip>
</div>
</SettingsItem>
<SettingsItem v-else :title="$t('settings.image_url')" next-line>
<div flex items-center gap-4>
<picture
aspect-video bg="$bew-fill-1" rounded="$bew-radius" overflow-hidden
un-border="4 transparent" cursor-pointer shrink-0
w="xl:1/5 lg:1/4 md:1/3"
>
<img
v-if="settings.searchPageWallpaper" :src="settings.searchPageWallpaper" alt="" w-full h-full
object-cover onerror="this.style.display='none'; this.onerror=null;"
>
</picture>
<div>
<Input v-model="settings.searchPageWallpaper" w-full />
<p color="sm $bew-text-3" mt-2>
{{ $t('settings.image_url_hint') }}
</p>
</div>
</div>
</SettingsItem>
<SettingsItem :title="$t('settings.enable_wallpaper_masking')">
<template #desc>
<span color="$bew-warning-color">{{ $t('common.performance_impact_warn') }}</span>
</template>
<Radio v-model="settings.searchPageEnableWallpaperMasking" />
</SettingsItem>
<SettingsItem v-if="settings.searchPageEnableWallpaperMasking" :title="$t('settings.wallpaper_mask_opacity')">
<Slider v-model="settings.searchPageWallpaperMaskOpacity" :label="`${settings.searchPageWallpaperMaskOpacity ?? 0}%`" />
</SettingsItem>
<SettingsItem v-if="settings.searchPageEnableWallpaperMasking" :title="$t('settings.wallpaper_blur_intensity')">
<template #desc>
<span color="$bew-warning-color">{{ $t('common.performance_impact_warn') }}</span>
</template>
<Slider v-model="settings.searchPageWallpaperBlurIntensity" :min="0" :max="60" :label="`${settings.searchPageWallpaperBlurIntensity ?? 0}px`" />
</SettingsItem>
</template>
<ChangeWallpaper v-if="settings.individuallySetSearchPageWallpaper" type="searchPage" />
</SettingsItemGroup>
</div>
</template>

View File

@@ -5,22 +5,24 @@ export const SEARCH_BAR_CHARACTERS: { name: string, url: string }[] = [
{ name: '33 娘', url: 'https://cdn.jsdelivr.net/gh/BewlyBewly/Imgs/searchBarCharacters/33chan-2.png' },
]
export const WALLPAPERS: { name: string, url: string, thumbnail: string }[] = [
{
name: 'Unsplash Random Nature Image',
url: 'https://source.unsplash.com/1920x1080/?nature',
thumbnail: 'https://source.unsplash.com/1920x1080/?nature',
},
{
name: 'Unsplash Random Building Image',
url: 'https://source.unsplash.com/1920x1080/?building',
thumbnail: 'https://source.unsplash.com/1920x1080/?building',
},
{
name: 'Unsplash Random Night Scene Image',
url: 'https://source.unsplash.com/1920x1080/?night-scene',
thumbnail: 'https://source.unsplash.com/1920x1080/?night-scene',
},
export interface wallpaperItem { name: string, url: string, thumbnail?: string }
export const WALLPAPERS: wallpaperItem[] = [
// {
// name: 'Unsplash Random Nature Image',
// url: 'https://source.unsplash.com/1920x1080/?nature',
// thumbnail: 'https://source.unsplash.com/1920x1080/?nature',
// },
// {
// name: 'Unsplash Random Building Image',
// url: 'https://source.unsplash.com/1920x1080/?building',
// thumbnail: 'https://source.unsplash.com/1920x1080/?building',
// },
// {
// name: 'Unsplash Random Night Scene Image',
// url: 'https://source.unsplash.com/1920x1080/?night-scene',
// thumbnail: 'https://source.unsplash.com/1920x1080/?night-scene',
// },
{
name: 'LoremPicsum Random Image',
url: 'https://picsum.photos/2560/1440/?nature',

View File

@@ -1,4 +1,5 @@
import { useStorageLocal } from '~/composables/useStorageLocal'
import type { wallpaperItem } from '~/constants/imgs'
import type { HomeSubPage } from '~/contentScripts/views/Home/types'
import type { AppPage } from '~/enums/appEnums'
@@ -33,6 +34,7 @@ export interface Settings {
enableWallpaperMasking: boolean
wallpaperMaskOpacity: number
wallpaperBlurIntensity: number
customizeWallpaper: wallpaperItem | null
searchPageDarkenOnSearchFocus: boolean
searchPageBlurredOnSearchFocus: boolean
@@ -84,6 +86,7 @@ export const settings = useStorageLocal('settings', ref<Settings>({
enableWallpaperMasking: false,
wallpaperMaskOpacity: 80,
wallpaperBlurIntensity: 0,
customizeWallpaper: null,
searchPageDarkenOnSearchFocus: true,
searchPageBlurredOnSearchFocus: false,

View File

@@ -70,6 +70,22 @@ html.dark.bewly-design {
}
}
// Adjust broken image icon
img {
position: relative;
&::before {
content: "";
display: block;
width: 100%;
height: 100%;
background-image: url("/assets/broken-image.png");
background-size: 25px;
background-position: center;
background-repeat: no-repeat;
}
}
// Change OverlayScrollbar color
.os-scrollbar .os-scrollbar-handle {
--os-handle-bg: rgba(120, 120, 140, 0.44);