fix(setup): setup page display timing is incorrect

This commit is contained in:
molvqingtai 2024-09-23 03:16:44 +08:00
parent cc3424d4d8
commit f6277bcc83
7 changed files with 362 additions and 90 deletions

View file

@ -9,6 +9,8 @@ import { stringToHex } from '@/utils'
import { Toaster } from '@/components/ui/Sonner' import { Toaster } from '@/components/ui/Sonner'
import UserInfoDomain from '@/domain/UserInfo' import UserInfoDomain from '@/domain/UserInfo'
import Setup from '@/app/content/views/Setup' import Setup from '@/app/content/views/Setup'
import MessageListDomain from '@/domain/MessageList'
import { useEffect } from 'react'
const hostRoomId = stringToHex(document.location.host) const hostRoomId = stringToHex(document.location.host)
@ -16,15 +18,31 @@ export default function App() {
const send = useRemeshSend() const send = useRemeshSend()
const roomDomain = useRemeshDomain(RoomDomain()) const roomDomain = useRemeshDomain(RoomDomain())
const userInfoDomain = useRemeshDomain(UserInfoDomain()) const userInfoDomain = useRemeshDomain(UserInfoDomain())
send(roomDomain.command.JoinRoomCommand(hostRoomId)) const messageListDomain = useRemeshDomain(MessageListDomain())
const isLogin = useRemeshQuery(userInfoDomain.query.IsLoginQuery()) const joinRoomFinished = useRemeshQuery(roomDomain.query.IsFinishedQuery())
const userInfoFinished = useRemeshQuery(userInfoDomain.query.IsFinishedQuery())
const userInfo = useRemeshQuery(userInfoDomain.query.UserInfoQuery())
const notUserInfo = userInfoFinished && !userInfo
useEffect(() => {
if (userInfoFinished) {
if (userInfo) {
!joinRoomFinished && send(roomDomain.command.JoinRoomCommand(hostRoomId))
} else {
send(messageListDomain.command.ClearListCommand())
}
}
}, [userInfoFinished, userInfo, joinRoomFinished])
return ( return (
<> <>
<AppContainer> <AppContainer>
<Header /> <Header />
<Main /> <Main />
<Footer /> <Footer />
{!isLogin && <Setup />} {notUserInfo && <Setup />}
</AppContainer> </AppContainer>
<AppButton></AppButton> <AppButton></AppButton>
<Toaster richColors offset="104px" position="top-center"></Toaster> <Toaster richColors offset="104px" position="top-center"></Toaster>

View file

@ -18,7 +18,7 @@ export default defineContentScript({
async main(ctx) { async main(ctx) {
const store = Remesh.store({ const store = Remesh.store({
externs: [IndexDBStorageImpl, BrowserSyncStorageImpl, PeerRoomImpl], externs: [IndexDBStorageImpl, BrowserSyncStorageImpl, PeerRoomImpl],
inspectors: [RemeshLogger()] inspectors: __DEV__ ? [RemeshLogger()] : []
}) })
const ui = await createShadowRootUi(ctx, { const ui = await createShadowRootUi(ctx, {

View file

@ -76,17 +76,18 @@ const Setup: FC = () => {
} }
useEffect(() => { useEffect(() => {
send(messageListDomain.command.ClearListCommand())
const timer = new Timer( const timer = new Timer(
async () => { async () => {
const userInfo = await refreshUserInfo() await createMessage(await refreshUserInfo())
await createMessage(userInfo)
}, },
{ delay: 2000, immediate: true, limit: mockTextList.length } { delay: 2000, immediate: true, limit: mockTextList.length }
) )
timer.start() timer.start()
return () => timer.stop() return () => {
timer.stop()
send(messageListDomain.command.ClearListCommand())
}
}, []) }, [])
return ( return (
<div className="absolute inset-0 z-50 flex rounded-xl bg-black/10 shadow-2xl backdrop-blur-sm"> <div className="absolute inset-0 z-50 flex rounded-xl bg-black/10 shadow-2xl backdrop-blur-sm">

View file

@ -1,11 +1,12 @@
import { Remesh } from 'remesh' import { Remesh } from 'remesh'
import { map, merge, tap } from 'rxjs' import { map, merge, switchMap, tap, defer, of, EMPTY, mergeMap } from 'rxjs'
import { type MessageUser } from './MessageList' import { type MessageUser } from './MessageList'
import { PeerRoomExtern } from '@/domain/externs/PeerRoom' import { PeerRoomExtern } from '@/domain/externs/PeerRoom'
import MessageListDomain from '@/domain/MessageList' import MessageListDomain from '@/domain/MessageList'
import UserInfoDomain from '@/domain/UserInfo' import UserInfoDomain from '@/domain/UserInfo'
import { callbackToObservable, desert } from '@/utils' import { callbackToObservable, desert } from '@/utils'
import { nanoid } from 'nanoid' import { nanoid } from 'nanoid'
import StatusModule from './modules/Status'
export enum MessageType { export enum MessageType {
Like = 'like', Like = 'like',
@ -40,6 +41,10 @@ const RoomDomain = Remesh.domain({
const MessageListQuery = messageListDomain.query.ListQuery const MessageListQuery = messageListDomain.query.ListQuery
const RoomStatusState = StatusModule(domain, {
name: 'Room.RoomStatusModule'
})
const PeerListState = domain.state<string[]>({ const PeerListState = domain.state<string[]>({
name: 'Room.PeerListState', name: 'Room.PeerListState',
default: [peerRoom.selfId] default: [peerRoom.selfId]
@ -56,15 +61,15 @@ const RoomDomain = Remesh.domain({
name: 'RoomJoinRoomCommand', name: 'RoomJoinRoomCommand',
impl: (_, roomId: string) => { impl: (_, roomId: string) => {
peerRoom.joinRoom(roomId) peerRoom.joinRoom(roomId)
return [JoinRoomEvent(peerRoom.selfId)] return [JoinRoomEvent(roomId), RoomStatusState.command.SetFinishedCommand()]
} }
}) })
const LeaveRoomCommand = domain.command({ const LeaveRoomCommand = domain.command({
name: 'RoomLeaveRoomCommand', name: 'RoomLeaveRoomCommand',
impl: (_) => { impl: (_, roomId: string) => {
peerRoom.leaveRoom() peerRoom.leaveRoom()
return [LeaveRoomEvent(peerRoom.selfId)] return [LeaveRoomEvent(roomId), RoomStatusState.command.SetInitialCommand()]
} }
}) })
@ -156,6 +161,18 @@ const RoomDomain = Remesh.domain({
name: 'RoomLeaveRoomEvent' name: 'RoomLeaveRoomEvent'
}) })
const OnMessageEvent = domain.event<RoomMessage>({
name: 'RoomOnMessageEvent'
})
const OnJoinRoomEvent = domain.event<string>({
name: 'RoomOnJoinRoomEvent'
})
const OnLeaveRoomEvent = domain.event<string>({
name: 'RoomOnLeaveRoomEvent'
})
const UpdatePeerListCommand = domain.command({ const UpdatePeerListCommand = domain.command({
name: 'RoomUpdatePeerListCommand', name: 'RoomUpdatePeerListCommand',
impl: ({ get }, action: { type: 'create' | 'delete'; peerId: string }) => { impl: ({ get }, action: { type: 'create' | 'delete'; peerId: string }) => {
@ -208,45 +225,37 @@ const RoomDomain = Remesh.domain({
domain.effect({ domain.effect({
name: 'RoomOnMessageEffect', name: 'RoomOnMessageEffect',
impl: ({ get }) => { impl: ({ fromEvent, get }) => {
const onMessage$ = callbackToObservable<RoomMessage>(peerRoom.onMessage.bind(peerRoom)) const onMessage$ = fromEvent(JoinRoomEvent).pipe(
return onMessage$.pipe( switchMap(() => callbackToObservable<RoomMessage>(peerRoom.onMessage.bind(peerRoom))),
map((message) => { mergeMap((message) => {
console.log('onMessage', message)
const messageEvent$ = of(OnMessageEvent(message))
const commandEvent$ = (() => {
switch (message.type) { switch (message.type) {
case 'text': case 'text':
return messageListDomain.command.CreateItemCommand({ return of(
messageListDomain.command.CreateItemCommand({
...message, ...message,
date: Date.now(), date: Date.now(),
likeUsers: [], likeUsers: [],
hateUsers: [] hateUsers: []
}) })
case 'like': {
if (!get(messageListDomain.query.HasItemQuery(message.id))) {
return null
}
const _message = get(messageListDomain.query.ItemQuery(message.id))
return messageListDomain.command.UpdateItemCommand({
..._message,
likeUsers: desert(
_message.likeUsers,
{
userId: message.userId,
username: message.username,
userAvatar: message.userAvatar
},
'userId'
) )
}) case 'like':
}
case 'hate': { case 'hate': {
if (!get(messageListDomain.query.HasItemQuery(message.id))) { if (!get(messageListDomain.query.HasItemQuery(message.id))) {
return null return EMPTY
} }
const _message = get(messageListDomain.query.ItemQuery(message.id)) const _message = get(messageListDomain.query.ItemQuery(message.id))
return messageListDomain.command.UpdateItemCommand({ const users = message.type === 'like' ? 'likeUsers' : 'hateUsers'
return of(
messageListDomain.command.UpdateItemCommand({
..._message, ..._message,
hateUsers: desert( [users]: desert(
_message.hateUsers, _message[users],
{ {
userId: message.userId, userId: message.userId,
username: message.username, username: message.username,
@ -255,59 +264,72 @@ const RoomDomain = Remesh.domain({
'userId' 'userId'
) )
}) })
)
} }
default: default:
console.warn('unknown message type', message) console.warn('未知消息类型', message)
return null return EMPTY
} }
})()
return merge(messageEvent$, commandEvent$)
}) })
) )
return onMessage$
} }
}) })
domain.effect({ domain.effect({
name: 'RoomOnJoinRoomEffect', name: 'RoomOnJoinRoomEffect',
impl: () => { impl: ({ fromEvent }) => {
const onJoinRoom$ = callbackToObservable<string>(peerRoom.onJoinRoom.bind(peerRoom)) const onJoinRoom$ = fromEvent(JoinRoomEvent).pipe(
return onJoinRoom$.pipe( switchMap(() => callbackToObservable<string>(peerRoom.onJoinRoom.bind(peerRoom))),
map((peerId) => { map((peerId) => {
console.log('onJoinRoom', peerId) console.log('onJoinRoom', peerId)
return [UpdatePeerListCommand({ type: 'create', peerId }), JoinRoomEvent(peerId)] return [UpdatePeerListCommand({ type: 'create', peerId }), OnJoinRoomEvent(peerId)]
}) })
) )
return onJoinRoom$
} }
}) })
domain.effect({ domain.effect({
name: 'RoomOnLeaveRoomEffect', name: 'RoomOnLeaveRoomEffect',
impl: () => { impl: ({ fromEvent }) => {
const onLeaveRoom$ = callbackToObservable<string>(peerRoom.onLeaveRoom.bind(peerRoom)) const onLeaveRoom$ = fromEvent(JoinRoomEvent).pipe(
return onLeaveRoom$.pipe( switchMap(() => callbackToObservable<string>(peerRoom.onLeaveRoom.bind(peerRoom))),
map((peerId) => { map((peerId) => {
console.log('onLeaveRoom', peerId) console.log('onLeaveRoom', peerId)
return [UpdatePeerListCommand({ type: 'delete', peerId }), LeaveRoomEvent(peerId)] return [UpdatePeerListCommand({ type: 'delete', peerId }), OnLeaveRoomEvent(peerId)]
}) })
) )
return onLeaveRoom$
} }
}) })
return { return {
query: { query: {
PeerListQuery, PeerListQuery,
MessageListQuery MessageListQuery,
...RoomStatusState.query
}, },
event: { event: {
SendTextMessageEvent, SendTextMessageEvent,
SendLikeMessageEvent, SendLikeMessageEvent,
SendHateMessageEvent, SendHateMessageEvent,
JoinRoomEvent, JoinRoomEvent,
LeaveRoomEvent LeaveRoomEvent,
OnMessageEvent,
OnJoinRoomEvent,
OnLeaveRoomEvent,
...RoomStatusState.event
}, },
command: { command: {
JoinRoomCommand, JoinRoomCommand,
LeaveRoomCommand, LeaveRoomCommand,
SendTextMessageCommand, SendTextMessageCommand,
SendLikeMessageCommand, SendLikeMessageCommand,
SendHateMessageCommand SendHateMessageCommand,
...RoomStatusState.command
} }
} }
} }

View file

@ -1,6 +1,7 @@
import { Remesh } from 'remesh' import { Remesh } from 'remesh'
import { BrowserSyncStorageExtern } from '@/domain/externs/Storage' import { BrowserSyncStorageExtern } from '@/domain/externs/Storage'
import StorageEffect from '@/domain/modules/StorageEffect' import StorageEffect from '@/domain/modules/StorageEffect'
import StatusModule from './modules/Status'
export interface UserInfo { export interface UserInfo {
id: string id: string
@ -26,6 +27,10 @@ const UserInfoDomain = Remesh.domain({
default: null default: null
}) })
const UserInfoStatusModule = StatusModule(domain, {
name: 'UserInfo.StatusModule'
})
const UserInfoQuery = domain.query({ const UserInfoQuery = domain.query({
name: 'UserInfo.UserInfoQuery', name: 'UserInfo.UserInfoQuery',
impl: ({ get }) => { impl: ({ get }) => {
@ -33,17 +38,17 @@ const UserInfoDomain = Remesh.domain({
} }
}) })
const IsLoginQuery = domain.query({
name: 'UserInfo.IsLoginQuery',
impl: ({ get }) => {
return !!get(UserInfoState())?.id
}
})
const UpdateUserInfoCommand = domain.command({ const UpdateUserInfoCommand = domain.command({
name: 'UserInfo.UpdateUserInfoCommand', name: 'UserInfo.UpdateUserInfoCommand',
impl: (_, userInfo: UserInfo | null) => { impl: (_, userInfo: UserInfo | null) => {
return [UserInfoState().new(userInfo), UpdateUserInfoEvent(), SyncToStorageEvent()] return [
UserInfoState().new(userInfo),
UpdateUserInfoEvent(),
SyncToStorageEvent(),
userInfo
? UserInfoStatusModule.command.SetFinishedCommand()
: UserInfoStatusModule.command.SetInitialCommand()
]
} }
}) })
@ -74,21 +79,25 @@ const UserInfoDomain = Remesh.domain({
storageEffect storageEffect
.set(SyncToStorageEvent) .set(SyncToStorageEvent)
.get<UserInfo>((value) => SyncToStateCommand(value)) .get<UserInfo>((value) => {
.watch<UserInfo>((value) => SyncToStateCommand(value!)) return [SyncToStateCommand(value), UserInfoStatusModule.command.SetFinishedCommand()]
})
.watch<UserInfo>((value) => [SyncToStateCommand(value)])
return { return {
query: { query: {
UserInfoQuery, UserInfoQuery,
IsLoginQuery ...UserInfoStatusModule.query
}, },
command: { command: {
UpdateUserInfoCommand UpdateUserInfoCommand,
...UserInfoStatusModule.command
}, },
event: { event: {
SyncToStateEvent, SyncToStateEvent,
SyncToStorageEvent, SyncToStorageEvent,
UpdateUserInfoEvent UpdateUserInfoEvent,
...UserInfoStatusModule.event
} }
} }
} }

View file

@ -0,0 +1,102 @@
import { DomainConceptName, RemeshDomainContext } from 'remesh'
export enum Status {
Initial = 0b001, // 1
Loading = 0b010, // 2
Finished = 0b100 // 4
}
export interface StatusOptions {
name: DomainConceptName<'StatusModule'>
default?: Status
}
const StatusModule = (domain: RemeshDomainContext, options: StatusOptions) => {
const StatusState = domain.state({
name: `${options.name}.StatusState`,
default: options.default ?? Status.Initial
})
const StatusQuery = domain.query({
name: `${options.name}.StatusQuery`,
impl: ({ get }) => {
return get(StatusState())
}
})
const IsInitialQuery = domain.query({
name: `${options.name}.IsInitialQuery`,
impl: ({ get }) => {
const state = get(StatusState())
return (state & Status.Initial) !== 0
}
})
const IsLoadingQuery = domain.query({
name: `${options.name}.IsLoadingQuery`,
impl: ({ get }) => {
const state = get(StatusState())
return (state & Status.Loading) !== 0
}
})
const IsFinishedQuery = domain.query({
name: `${options.name}.IsFinishedQuery`,
impl: ({ get }) => {
const state = get(StatusState())
return (state & Status.Finished) !== 0
}
})
const UpdateStatusEvent = domain.event<Status>({
name: `${options.name}.UpdateStatusEvent`
})
const SetInitialCommand = domain.command({
name: `${options.name}.SetInitialCommand`,
impl: () => {
return [StatusState().new(Status.Initial), UpdateStatusEvent(Status.Initial)]
}
})
const SetLoadingCommand = domain.command({
name: `${options.name}.SetLoadingCommand`,
impl: () => {
return [StatusState().new(Status.Loading), UpdateStatusEvent(Status.Loading)]
}
})
const SetFinishedCommand = domain.command({
name: `${options.name}.SetFinishedCommand`,
impl: () => {
return [StatusState().new(Status.Finished), UpdateStatusEvent(Status.Finished)]
}
})
const UpdateStatusCommand = domain.command({
name: `${options.name}.UpdateStatusCommand`,
impl: (_, status: Status) => {
return [StatusState().new(status), UpdateStatusEvent(status)]
}
})
return {
query: {
StatusQuery,
IsInitialQuery,
IsLoadingQuery,
IsFinishedQuery
},
command: {
SetInitialCommand,
SetLoadingCommand,
SetFinishedCommand,
UpdateStatusCommand
},
event: {
UpdateStatusEvent
}
}
}
export default StatusModule

View file

@ -57,8 +57,83 @@ function createAvatarSvg() {
'rgb(165, 42, 42)', // Red 'rgb(165, 42, 42)', // Red
'rgb(145, 85, 61)', // Auburn 'rgb(145, 85, 61)', // Auburn
'rgb(128, 128, 128)', // Grey 'rgb(128, 128, 128)', // Grey
'rgb(185, 55, 55)' // Fire 'rgb(185, 55, 55)', // Fire
// ... 其他颜色 'rgb(255, 192, 203)', // Pastel Pink
'rgb(255, 105, 180)', // Bright Pink
'rgb(230, 230, 250)', // Lavender
'rgb(64, 224, 208)', // Turquoise
'rgb(0, 191, 255)', // Bright Blue
'rgb(148, 0, 211)', // Deep Purple
'rgb(50, 205, 50)', // Lime Green
'rgb(255, 165, 0)', // Vivid Orange
'rgb(220, 20, 60)', // Crimson Red
'rgb(192, 192, 192)', // Silver
'rgb(255, 215, 0)', // Gold
'rgb(255, 255, 255)', // White
'rgb(124, 252, 0)', // Lawn Green
'rgb(127, 255, 0)', // Chartreuse
'rgb(0, 255, 127)', // Spring Green
'rgb(72, 209, 204)', // Medium Turquoise
'rgb(0, 255, 255)', // Cyan
'rgb(0, 206, 209)', // Dark Turquoise
'rgb(32, 178, 170)', // Light Sea Green
'rgb(95, 158, 160)', // Cadet Blue
'rgb(70, 130, 180)', // Steel Blue
'rgb(176, 196, 222)', // Light Steel Blue
'rgb(30, 144, 255)', // Dodger Blue
'rgb(135, 206, 235)', // Sky Blue
'rgb(0, 0, 139)', // Dark Blue
'rgb(138, 43, 226)', // Blue Violet
'rgb(75, 0, 130)', // Indigo
'rgb(139, 0, 139)', // Dark Magenta
'rgb(153, 50, 204)', // Dark Orchid
'rgb(186, 85, 211)', // Medium Orchid
'rgb(218, 112, 214)', // Orchid
'rgb(221, 160, 221)', // Plum
'rgb(238, 130, 238)', // Violet
'rgb(255, 0, 255)', // Magenta
'rgb(216, 191, 216)', // Thistle
'rgb(255, 20, 147)', // Deep Pink
'rgb(255, 69, 0)', // Orange Red
'rgb(255, 140, 0)', // Dark Orange
'rgb(255, 165, 0)', // Orange
'rgb(250, 128, 114)', // Salmon
'rgb(233, 150, 122)', // Dark Salmon
'rgb(240, 128, 128)', // Light Coral
'rgb(205, 92, 92)', // Indian Red
'rgb(255, 99, 71)', // Tomato
'rgb(255, 160, 122)', // Light Salmon
'rgb(220, 20, 60)', // Crimson
'rgb(139, 0, 0)', // Dark Red
'rgb(178, 34, 34)', // Fire Brick
'rgb(250, 235, 215)', // Antique White
'rgb(255, 239, 213)', // Papaya Whip
'rgb(255, 235, 205)', // Blanched Almond
'rgb(255, 222, 173)', // Navajo White
'rgb(245, 245, 220)', // Beige
'rgb(255, 228, 196)', // Bisque
'rgb(255, 218, 185)', // Peach Puff
'rgb(244, 164, 96)', // Sandy Brown
'rgb(210, 180, 140)', // Tan
'rgb(222, 184, 135)', // Burly Wood
'rgb(250, 250, 210)', // Light Goldenrod Yellow
'rgb(255, 250, 205)', // Lemon Chiffon
'rgb(255, 245, 238)', // Sea Shell
'rgb(253, 245, 230)', // Old Lace
'rgb(255, 228, 225)', // Misty Rose
'rgb(255, 240, 245)', // Lavender Blush
'rgb(250, 240, 230)', // Linen
'rgb(255, 228, 181)', // Moccasin
'rgb(255, 250, 250)', // Snow
'rgb(240, 255, 255)', // Azure
'rgb(240, 255, 240)', // Honeydew
'rgb(245, 245, 245)', // White Smoke
'rgb(245, 255, 250)', // Mint Cream
'rgb(240, 248, 255)', // Alice Blue
'rgb(240, 248, 255)', // Ghost White
'rgb(248, 248, 255)', // Ghost White
'rgb(255, 250, 240)', // Floral White
'rgb(253, 245, 230)' // Old Lace
], ],
hairColor: 'black', hairColor: 'black',
dyeColorOffset: '50%', dyeColorOffset: '50%',
@ -72,8 +147,54 @@ function createAvatarSvg() {
'rgb(188, 143, 143)', // Dusty Rose 'rgb(188, 143, 143)', // Dusty Rose
'rgb(135, 206, 235)', // Sky Blue 'rgb(135, 206, 235)', // Sky Blue
'rgb(245, 255, 250)', // Mint Cream 'rgb(245, 255, 250)', // Mint Cream
'rgb(245, 222, 179)' // Wheat 'rgb(245, 222, 179)', // Wheat
// ... 其他颜色 'rgb(47, 79, 79)', // Dark Slate Gray
'rgb(72, 61, 139)', // Dark Slate Blue
'rgb(60, 20, 20)', // Dark Brown
'rgb(25, 25, 112)', // Midnight Blue
'rgb(139, 0, 0)', // Dark Red
'rgb(85, 107, 47)', // Olive Drab
'rgb(128, 0, 128)', // Purple
'rgb(0, 100, 0)', // Dark Green
'rgb(0, 0, 139)', // Dark Blue
'rgb(105, 105, 105)', // Dim Gray
'rgb(240, 128, 128)', // Light Coral
'rgb(255, 160, 122)', // Light Salmon
'rgb(255, 218, 185)', // Peach Puff
'rgb(255, 228, 196)', // Bisque
'rgb(255, 222, 173)', // Navajo White
'rgb(255, 250, 205)', // Lemon Chiffon
'rgb(250, 250, 210)', // Light Goldenrod Yellow
'rgb(255, 239, 213)', // Papaya Whip
'rgb(255, 245, 238)', // Sea Shell
'rgb(255, 248, 220)', // Cornsilk
'rgb(255, 255, 240)', // Ivory
'rgb(240, 255, 240)', // Honeydew
'rgb(240, 255, 255)', // Azure
'rgb(240, 248, 255)', // Alice Blue
'rgb(248, 248, 255)', // Ghost White
'rgb(255, 250, 250)', // Snow
'rgb(255, 240, 245)', // Lavender Blush
'rgb(255, 228, 225)', // Misty Rose
'rgb(230, 230, 250)', // Lavender
'rgb(216, 191, 216)', // Thistle
'rgb(221, 160, 221)', // Plum
'rgb(238, 130, 238)', // Violet
'rgb(218, 112, 214)', // Orchid
'rgb(186, 85, 211)', // Medium Orchid
'rgb(147, 112, 219)', // Medium Purple
'rgb(138, 43, 226)', // Blue Violet
'rgb(148, 0, 211)', // Dark Violet
'rgb(153, 50, 204)', // Dark Orchid
'rgb(139, 69, 19)', // Saddle Brown
'rgb(160, 82, 45)', // Sienna
'rgb(210, 105, 30)', // Chocolate
'rgb(205, 133, 63)', // Peru
'rgb(244, 164, 96)', // Sandy Brown
'rgb(222, 184, 135)', // Burly Wood
'rgb(255, 250, 240)', // Floral White
'rgb(253, 245, 230)', // Old Lace
'rgb(250, 240, 230)' // Linen
], ],
mouthPoints: [] mouthPoints: []
} }
@ -250,7 +371,6 @@ function createAvatarSvg() {
return createElement(svgTemplate) return createElement(svgTemplate)
} }
// 导出函数
export default function generateUglyAvatar() { export default function generateUglyAvatar() {
const svgElement = createAvatarSvg() const svgElement = createAvatarSvg()
const serializer = new XMLSerializer() const serializer = new XMLSerializer()