Skip to content

Commit

Permalink
style: mobile responsive
Browse files Browse the repository at this point in the history
  • Loading branch information
nekomeowww committed Dec 3, 2024
1 parent 5546d10 commit bc04dba
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 26 deletions.
1 change: 1 addition & 0 deletions packages/stage/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>アイリ</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
Expand Down
1 change: 1 addition & 0 deletions packages/stage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"devDependencies": {
"@airi-proj/elevenlabs": "workspace:^",
"@iconify-json/carbon": "^1.2.4",
"@iconify-json/eos-icons": "^1.2.1",
"@intlify/unplugin-vue-i18n": "^6.0.0",
"@shikijs/markdown-it": "^1.24.0",
"@types/markdown-it-link-attributes": "^3.0.5",
Expand Down
52 changes: 40 additions & 12 deletions packages/stage/src/components/Live2DViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { Application } from '@pixi/app'
import { extensions } from '@pixi/extensions'
import { Ticker, TickerPlugin } from '@pixi/ticker'
import { useElementBounding, useWindowSize } from '@vueuse/core'
import { breakpointsTailwind, useBreakpoints, useElementBounding, useWindowSize } from '@vueuse/core'
import { Live2DModel, MotionPreloadStrategy, MotionPriority } from 'pixi-live2d-display/cubism4'
import { onMounted, onUnmounted, ref, watch } from 'vue'
Expand All @@ -21,10 +21,37 @@ const mouthOpenSize = computed(() => {
return Math.max(0, Math.min(100, props.mouthOpenSize))
})
const breakpoints = useBreakpoints(breakpointsTailwind)
const { width, height } = useWindowSize()
const containerElementBounding = useElementBounding(containerRef)
const containerParentElementBounding = useElementBounding(containerRef.value?.parentElement)
const isMobile = computed(() => breakpoints.between('sm', 'md').value || breakpoints.smaller('sm').value)
const isTablet = computed(() => breakpoints.between('md', 'lg').value)
const isDesktop = computed(() => breakpoints.greaterOrEqual('lg').value)
const canvasWidth = computed(() => {
if (isDesktop.value)
return containerElementBounding.width.value
else if (isMobile.value)
return (width.value - 16) // padding
else if (isTablet.value)
return (width.value - 16) // padding
else
return containerElementBounding.width.value
})
const canvasHeight = computed(() => {
if (isDesktop.value)
return Math.max(600, containerParentElementBounding.height.value)
else if (isMobile.value)
return 600
else if (isTablet.value)
return 600
else
return Math.max(600, containerParentElementBounding.height.value)
})
function getCoreModel() {
return model.value!.internalModel.coreModel as any
}
Expand All @@ -38,19 +65,20 @@ async function initLive2DPixiStage(parent: HTMLDivElement) {
extensions.add(TickerPlugin)
pixiApp.value = new Application({
width: containerElementBounding.width.value,
height: Math.max(600, containerParentElementBounding.height.value),
width: canvasWidth.value,
height: canvasHeight.value,
backgroundAlpha: 0,
})
pixiAppCanvas.value = pixiApp.value.view
pixiAppCanvas.value.style.objectFit = 'contain'
parent.appendChild(pixiApp.value.view)
model.value = await Live2DModel.from(props.model, { motionPreload: MotionPreloadStrategy.ALL })
pixiApp.value.stage.addChild(model.value as any)
model.value.x = containerElementBounding.width.value / 2
model.value.y = Math.max(600, containerParentElementBounding.height.value)
model.value.x = canvasWidth.value / 2
model.value.y = canvasHeight.value
model.value.rotation = Math.PI
model.value.skew.x = Math.PI
model.value.scale.set(0.3, 0.3)
Expand All @@ -69,18 +97,18 @@ async function setMotion(motionName: string) {
await model.value!.motion(motionName, undefined, MotionPriority.FORCE)
}
watch([width, height], () => {
watch([width, height, canvasWidth, canvasHeight], () => {
if (pixiApp.value)
pixiApp.value.renderer.resize((width.value - 16) / 2, 550)
pixiApp.value.renderer.resize(canvasWidth.value, canvasHeight.value)
if (pixiAppCanvas.value) {
pixiAppCanvas.value.width = (width.value - 16) / 2
pixiAppCanvas.value.height = Math.max(600, containerParentElementBounding.height.value)
pixiAppCanvas.value.width = canvasWidth.value
pixiAppCanvas.value.height = canvasHeight.value
}
if (model.value) {
model.value.x = (width.value - 16) / 4
model.value.y = Math.max(600, containerParentElementBounding.height.value)
model.value.x = canvasWidth.value / 2
model.value.y = canvasHeight.value
}
})
Expand All @@ -105,5 +133,5 @@ defineExpose({
</script>

<template>
<div ref="containerRef" h-full w-full />
<div ref="containerRef" />
</template>
60 changes: 46 additions & 14 deletions packages/stage/src/components/MainStage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -273,15 +273,25 @@ onUnmounted(() => {
<template>
<div max-h="[100vh]" h-full p="2" flex="~ col">
<Settings />
<div flex="~ row 1" w-full items-end space-x-2>
<div w-full min-h="100 sm:100">
<Live2DViewer ref="live2DViewerRef" :mouth-open-size="mouthOpenSize" model="/assets/live2d/models/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json" />
<div flex="~ row 1" relative w-full items-end gap-2>
<div w="50%" min-w="50% <lg:full" min-h="100 sm:100" flex-1>
<Live2DViewer
ref="live2DViewerRef"
:mouth-open-size="mouthOpenSize"
model="/assets/live2d/models/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json"
/>
</div>
<div my="2" w-full space-y-2 max-h="[calc(100vh-117px)]">
<div
class="relative <lg:(absolute bottom-0 from-zinc-800/80 to-zinc-800/0 bg-gradient-to-t p-2)"
px="<sm:2" py="<sm:2" rounded="<sm:lg"
w="50% <lg:full" flex="~ col 1" gap-2 max-h="[calc(100vh-117px)]"
>
<div v-for="(message, index) in messages" :key="index">
<div v-if="message.role === 'assistant'" flex mr="12">
<div
mr-2 h-10 min-h-10 min-w-10 w-10 overflow-hidden rounded-full
mr-2 h-10
min-h-10 min-w-10 w-10
overflow-hidden rounded-full
border="solid 3"
transition="all ease-in-out" duration-100
:style="{
Expand All @@ -290,22 +300,44 @@ onUnmounted(() => {
>
<img :src="Avatar">
</div>
<div flex="~ col" bg="pink-50/50 dark:pink-900/50" p="2" border="2 solid pink/10" rounded-lg>
<div
flex="~ col"
bg="pink-50 dark:pink-900"
p="2"
border="2 solid pink dark:pink-700"
rounded-lg
h="unset <sm:fit"
>
<div>
<span font-semibold>Neuro</span>
<span font-semibold class="inline <sm:hidden">Neuro</span>
</div>
<div v-html="process(message.content as string)" />
<div v-if="message.content" text="base <sm:xs" v-html="process(message.content as string)" />
<div v-else i-eos-icons:three-dots-loading />
</div>
</div>
<div v-else-if="message.role === 'user'" flex="~ row-reverse" ml="12">
<div border="purple solid 3" ml="2" h-10 min-h-10 min-w-10 w-10 overflow-hidden rounded-full>
<div
class="block <sm:hidden"
border="purple solid 3"
ml="2"
h-10 min-h-10 min-w-10 w-10
overflow-hidden rounded-full
>
<div i-carbon:user-avatar-filled text="purple" h-full w-full p="0" m="0" />
</div>
<div flex="~ col" bg="purple-50/50 dark:purple-900/50" p="2" border="2 solid pink/10" rounded-lg>
<div
flex="~ col"
bg="purple-50 dark:purple-900"
p="2"
border="2 solid purple dark:purple-700"
rounded-lg
h="unset <sm:fit"
>
<div>
<span font-semibold>You</span>
<span font-semibold class="inline <sm:hidden">You</span>
</div>
<div v-html="process(message.content as string)" />
<div v-if="message.content" text="base <sm:xs" v-html="process(message.content as string)" />
<div v-else />
</div>
</div>
</div>
Expand All @@ -329,10 +361,10 @@ onUnmounted(() => {
{{ 'name' in m ? `${m.name} (${m.id})` : m.id }}
</option>
</select>
<div absolute bottom="5" left="50%" translate-x="-50%">
<div fixed bottom="5" left="50%" translate-x="-50%">
<button
bg="zinc-100 dark:zinc-700" flex="~ row"
items-center rounded-full px-4 py-2
items-center rounded-full px-4 py-2 border="2 solid zinc-500/50"
transition="all ease-in-out"
@click="listening = !listening"
>
Expand Down
10 changes: 10 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit bc04dba

Please sign in to comment.