diff --git a/src/assets/widgets/clothes/collared.svg b/src/assets/widgets/clothes/collared.svg index 672d2e0d4f8f931f2e3d4b9203da10239e9ffb3c..714088601dced390535e1a77f521d9131c7d912c 100644 --- a/src/assets/widgets/clothes/collared.svg +++ b/src/assets/widgets/clothes/collared.svg @@ -9,7 +9,7 @@ clothes - collared @@ -54,4 +54,4 @@ stroke-linejoin="round" /> - \ No newline at end of file + diff --git a/src/assets/widgets/clothes/crew.svg b/src/assets/widgets/clothes/crew.svg index 5cfa1bf63e8829bc8a7b80b0c00e50e97cf78710..208f2806605d77a36fb27cde881d7eeb1002da93 100644 --- a/src/assets/widgets/clothes/crew.svg +++ b/src/assets/widgets/clothes/crew.svg @@ -9,7 +9,7 @@ clothes - crew @@ -20,4 +20,4 @@ stroke-width="4" /> - \ No newline at end of file + diff --git a/src/assets/widgets/clothes/open.svg b/src/assets/widgets/clothes/open.svg index 350499b40862f82df49bc9ff5f9a6308425a6015..725c6c5f7c4782c26d444cc5af5296247543e78a 100644 --- a/src/assets/widgets/clothes/open.svg +++ b/src/assets/widgets/clothes/open.svg @@ -9,9 +9,9 @@ clothes - open - \ No newline at end of file + diff --git a/src/assets/widgets/tops/danny.svg b/src/assets/widgets/tops/danny.svg index 519572d0ee57ad693af64acd238760158e6c723e..0f58839e39bb656ad2e9bbd396754387d4acbc80 100644 --- a/src/assets/widgets/tops/danny.svg +++ b/src/assets/widgets/tops/danny.svg @@ -9,9 +9,9 @@ tops - danny - \ No newline at end of file + diff --git a/src/assets/widgets/tops/fonze.svg b/src/assets/widgets/tops/fonze.svg index a439d463a16a08a4db1fc988388bc1f3d446a61b..ba3c44449df836269e6073af15c27bf3ddb5f0f9 100644 --- a/src/assets/widgets/tops/fonze.svg +++ b/src/assets/widgets/tops/fonze.svg @@ -9,23 +9,23 @@ tops - fonze - \ No newline at end of file + diff --git a/src/assets/widgets/tops/funny.svg b/src/assets/widgets/tops/funny.svg index 4da507b2d0c5c74d63dfdd5d61439ab95592ef38..e13d3f4f65d49504b6e58941f7e329e07a2a5a7e 100644 --- a/src/assets/widgets/tops/funny.svg +++ b/src/assets/widgets/tops/funny.svg @@ -23,4 +23,4 @@ stroke-width="4" /> - \ No newline at end of file + diff --git a/src/assets/widgets/tops/pixie.svg b/src/assets/widgets/tops/pixie.svg index a3420de855df94579b2defd186bb2260b45a39d5..84bbfe793565973e5adcb560bfe4a8660473c1f9 100644 --- a/src/assets/widgets/tops/pixie.svg +++ b/src/assets/widgets/tops/pixie.svg @@ -9,7 +9,7 @@ tops - pixie @@ -19,4 +19,4 @@ stroke-width="4" /> - \ No newline at end of file + diff --git a/src/assets/widgets/tops/punk.svg b/src/assets/widgets/tops/punk.svg index 007555ff1392ea8edc3cdd8629ea99e67fc731d1..ca82b6052309cd18f4cb32a5bc2fc112bfd032e1 100644 --- a/src/assets/widgets/tops/punk.svg +++ b/src/assets/widgets/tops/punk.svg @@ -14,11 +14,11 @@ /> - \ No newline at end of file + diff --git a/src/assets/widgets/tops/wave.svg b/src/assets/widgets/tops/wave.svg index d26cc7775ba642d7a28dbea79820f2e993c3d7d0..462b843ff03a9d11859e883461b2cadf63cb8d64 100644 --- a/src/assets/widgets/tops/wave.svg +++ b/src/assets/widgets/tops/wave.svg @@ -9,9 +9,9 @@ tops - wave - \ No newline at end of file + diff --git a/src/components/Configurator.vue b/src/components/Configurator.vue index 40ee789e5ea038c7f6d213a12e2193a20c5bbeb6..5e69a76883f34fd3780f57d92ac26181ec5ec454 100644 --- a/src/components/Configurator.vue +++ b/src/components/Configurator.vue @@ -22,11 +22,11 @@ -
    +
    • + />
    + +
    + 颜色 +
      +
    • +
      +
    • +
    +
    @@ -70,9 +90,14 @@ import { useI18n } from 'vue-i18n' import PerfectScrollbar from '@/components/PerfectScrollbar.vue' import SectionWrapper from '@/components/SectionWrapper.vue' -import { type WidgetShape, type WrapperShape, WidgetType } from '@/enums' +import { + type WidgetShape, + type WrapperShape, + BeardShape, + WidgetType, +} from '@/enums' import { useAvatarOption } from '@/hooks' -import { SETTINGS } from '@/utils/constant' +import { AVATAR_LAYER, SETTINGS } from '@/utils/constant' import { previewData } from '@/utils/dynamic-data' const { t } = useI18n() @@ -156,6 +181,24 @@ function switchWidget(widgetType: WidgetType, widgetShape: WidgetShape) { [widgetType]: { ...avatarOption.value.widgets?.[widgetType], shape: widgetShape, + ...(widgetShape === BeardShape.Scruff + ? { zIndex: AVATAR_LAYER['mouth'].zIndex - 1 } + : undefined), + }, + }, + }) + } +} + +function setWidgetColor(widgetType: WidgetType, fillColor: string) { + if (avatarOption.value.widgets?.[widgetType]) { + setAvatarOption({ + ...avatarOption.value, + widgets: { + ...avatarOption.value.widgets, + [widgetType]: { + ...avatarOption.value.widgets?.[widgetType], + fillColor, }, }, }) @@ -206,12 +249,23 @@ function switchWidget(widgetType: WidgetType, widgetShape: WidgetShape) { } } - .bg-color-list { + .color-picker { + margin-top: 1rem; + + summary { + color: darken(var.$color-text, 20); + font-size: small; + cursor: pointer; + user-select: none; + } + } + + .color-list { display: flex; flex-wrap: wrap; align-items: center; - .bg-color-list__item { + .color-list__item { position: relative; z-index: 1; width: calc(100% / 7); diff --git a/src/components/VueColorAvatar.vue b/src/components/VueColorAvatar.vue index eeee0a0c5bae98032c449013f696d16b170e4332..0ed44ead2ae4dcd5296955f6e460bfd67d09b601 100644 --- a/src/components/VueColorAvatar.vue +++ b/src/components/VueColorAvatar.vue @@ -26,7 +26,7 @@ import { ref, toRefs, watchEffect } from 'vue' import { WrapperShape } from '@/enums' import { type AvatarOption } from '@/types' import { getRandomAvatarOption } from '@/utils' -import { AVATAR_LAYER, NONE } from '@/utils/constant' +import { AVATAR_LAYER, NONE, SETTINGS } from '@/utils/constant' import { widgetData } from '@/utils/dynamic-data' import Background from './widgets/Background.vue' @@ -62,9 +62,9 @@ const svgContent = ref('') watchEffect(async () => { const sortedList = Object.entries(avatarOption.value.widgets).sort( - (i, ii) => { - const ix = AVATAR_LAYER[i[0]]?.zIndex ?? 0 - const iix = AVATAR_LAYER[ii[0]]?.zIndex ?? 0 + ([prevShape, prev], [nextShape, next]) => { + const ix = prev.zIndex ?? AVATAR_LAYER[prevShape]?.zIndex ?? 0 + const iix = next.zIndex ?? AVATAR_LAYER[nextShape]?.zIndex ?? 0 return ix - iix } ) @@ -86,9 +86,12 @@ watchEffect(async () => { const svgRawList = await Promise.all(promises).then((raw) => { return raw.map((svgRaw, i) => { + const widgetFillColor = sortedList[i][1].fillColor + const content = svgRaw .slice(svgRaw.indexOf('>', svgRaw.indexOf('', '') + .replaceAll('$fillColor', widgetFillColor || 'transparent') return ` diff --git a/src/layouts/Sider.vue b/src/layouts/Sider.vue index 9d59815f633549ad8c895993d2c10f1bd5adb9b0..46ea8f36fb52ecd3d8ac0e6d92b9ed8e95af5100 100644 --- a/src/layouts/Sider.vue +++ b/src/layouts/Sider.vue @@ -41,13 +41,13 @@ const { isCollapsed, openSider, closeSider } = useSider() .trigger { position: absolute; top: 50%; - left: 0; + left: 1px; display: flex; align-items: center; justify-content: center; width: 1.2rem; height: 4rem; - background-color: lighten(var.$color-configurator, 2); + background-color: var.$color-configurator; border-radius: 0.4rem 0 0 0.4rem; transform: translate(-100%, -50%); cursor: pointer; diff --git a/src/types/index.ts b/src/types/index.ts index 211cf3ea7762bdcfd943696479a23b7c7f0c3628..c75bb7f484ee6c652469e27138ba9d5d01ebfc43 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -67,7 +67,7 @@ export interface AvatarSettings { glassesShape: GlassesShape[] clothesShape: ClothesShape[] + commonColors: string[] backgroundColor: string[] skinColor: string[] - clothesColor: string[] } diff --git a/src/utils/constant.ts b/src/utils/constant.ts index 4899fe0b706d2acb92c8c4bf8727422d6e7fc664..efad4ff02dca33a74fc7fe3d54a5574c46bc899c 100644 --- a/src/utils/constant.ts +++ b/src/utils/constant.ts @@ -71,7 +71,7 @@ export const SETTINGS: Readonly = { beardShape: Object.values(BeardShape), clothesShape: Object.values(ClothesShape), - backgroundColor: [ + commonColors: [ '#6BD9E9', '#FC909F', '#F4D150', @@ -84,15 +84,20 @@ export const SETTINGS: Readonly = { '#48A99A', '#C09FFF', '#FD6F5D', - 'linear-gradient(45deg, #E3648C, #D97567)', - 'linear-gradient(62deg, #8EC5FC, #E0C3FC)', - 'linear-gradient(90deg, #ffecd2, #fcb69f)', - 'linear-gradient(120deg, #a1c4fd, #c2e9fb)', - 'linear-gradient(-135deg, #fccb90, #d57eeb)', - 'transparent', ], + + get backgroundColor() { + return [ + ...this.commonColors, + 'linear-gradient(45deg, #E3648C, #D97567)', + 'linear-gradient(62deg, #8EC5FC, #E0C3FC)', + 'linear-gradient(90deg, #ffecd2, #fcb69f)', + 'linear-gradient(120deg, #a1c4fd, #c2e9fb)', + 'linear-gradient(-135deg, #fccb90, #d57eeb)', + 'transparent', + ] + }, skinColor: ['#F9C9B6', '#AC6651'], - clothesColor: ['#9287FF', '#6BD9E9', '#FC909F', '#F4D150', '#77311D'], } export const SCREEN = { @@ -107,7 +112,7 @@ export const SPECIAL_AVATARS: Readonly = [ { wrapperShape: 'squircle', background: { - color: '#E0DDFF', + color: 'linear-gradient(62deg, #8EC5FC, #E0C3FC)', }, widgets: { face: { @@ -115,6 +120,7 @@ export const SPECIAL_AVATARS: Readonly = [ }, tops: { shape: TopsShape.Pixie, + fillColor: '#d2eff3', }, ear: { shape: EarShape.Attached, @@ -142,13 +148,14 @@ export const SPECIAL_AVATARS: Readonly = [ }, clothes: { shape: ClothesShape.Crew, + fillColor: '#e0ddff', }, }, }, { wrapperShape: 'squircle', background: { - color: '#F4D150', + color: '#fd6f5d', }, widgets: { face: { @@ -183,6 +190,7 @@ export const SPECIAL_AVATARS: Readonly = [ }, clothes: { shape: ClothesShape.Crew, + fillColor: '#f4d150', }, }, }, diff --git a/src/utils/index.ts b/src/utils/index.ts index 164cdaff0820747adf114b888f3ba63581ed0fa7..b0cca9e93c24bebd34b64764d193c0b4128ec231 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,7 +7,7 @@ import { } from '@/enums' import { type AvatarOption, type None } from '@/types' -import { NONE, SETTINGS, SPECIAL_AVATARS } from './constant' +import { AVATAR_LAYER, NONE, SETTINGS, SPECIAL_AVATARS } from './constant' /** * get a random value from an array @@ -34,6 +34,12 @@ function getRandomValue( return randomValue } +export function getRandomFillColor() { + return SETTINGS.commonColors[ + Math.floor(Math.random() * SETTINGS.commonColors.length) + ] +} + export function getRandomAvatarOption( presetOption: Partial = {}, useOption: Partial = {} @@ -48,6 +54,10 @@ export function getRandomAvatarOption( topList = SETTINGS.topsShape.filter((shape) => !topList.includes(shape)) } + const beardShape = getRandomValue(beardList, { + usually: [NONE], + }) + const avatarOption: AvatarOption = { gender, @@ -68,6 +78,7 @@ export function getRandomAvatarOption( shape: getRandomValue(topList, { avoid: [useOption.widgets?.tops?.shape], }), + fillColor: getRandomFillColor(), }, ear: { shape: getRandomValue(SETTINGS.earShape, { @@ -105,14 +116,18 @@ export function getRandomAvatarOption( }), }, beard: { - shape: getRandomValue(beardList, { - usually: [NONE], - }), + shape: beardShape, + + // HACK: + ...(beardShape === BeardShape.Scruff + ? { zIndex: AVATAR_LAYER['mouth'].zIndex - 1 } + : undefined), }, clothes: { shape: getRandomValue(SETTINGS.clothesShape, { avoid: [useOption.widgets?.clothes?.shape], }), + fillColor: getRandomFillColor(), }, }, } @@ -141,22 +156,22 @@ export function showConfetti() { const duration = performance.now() + 1 * 1000 - const colors = ['#6967fe', '#85e9f4', '#e16984'] + const confettiColors = ['#6967fe', '#85e9f4', '#e16984'] void (function frame() { myConfetti({ - particleCount: colors.length, + particleCount: confettiColors.length, angle: 60, spread: 55, origin: { x: 0 }, - colors: colors, + colors: confettiColors, }) myConfetti({ - particleCount: colors.length, + particleCount: confettiColors.length, angle: 120, spread: 55, origin: { x: 1 }, - colors: colors, + colors: confettiColors, }) if (performance.now() < duration) {