평가 및 화면 구조 정리

This commit is contained in:
Yun Chan 2026-06-28 21:46:22 +09:00
parent 1248ae8ca4
commit 391639c1de
44 changed files with 5816 additions and 4501 deletions

View file

@ -36,6 +36,7 @@ import { Mouth } from "./Mouth";
import { live2dModel3Path, live2dModelForPersonaCode, live2dMotionForExpression } from "./live2dModel";
import { useExpressionTransition } from "./useExpressionTransition";
import { RasterBust } from "./RasterBust";
import "./client-avatar.css";
/* ( import )
Session.tsx ClientAvatar . */
@ -82,7 +83,6 @@ function prefersReducedMotion(): boolean {
window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true
);
}
/** prefers-reduced-motion 을 반응형으로 구독 */
function useReducedMotion(): boolean {
const [reduced, setReduced] = useState<boolean>(prefersReducedMotion);
@ -299,7 +299,6 @@ export function ClientAvatar({
data-realism={realism} /* 사실성은 데이터로만 유지(시각 표식 비노출, §4.6) */
aria-label={`교육용 가상 내담자: ${persona.label}, ${STATE_TEXT[state]}, ${expressionLabel}`}
>
<style>{AVATAR_CSS}</style>
{/* 상단 라벨 — 실존 인물 오인 차단(상시, §4.1) */}
{showCaption ? (
@ -399,60 +398,3 @@ export function ClientAvatar({
</figure>
);
}
const AVATAR_CSS = `
.vg-avatar{display:flex;flex-direction:column;align-items:center;gap:var(--sp-3);margin:0;}
.vg-avatar__label{
display:inline-flex;align-items:center;gap:7px;
font-family:var(--font-num);font-size:var(--fs-xs);font-weight:600;letter-spacing:0.06em;
color:var(--text-muted);text-transform:none;
}
.vg-avatar__label-dot{width:6px;height:6px;border-radius:50%;background:var(--clay);flex:none;}
.vg-avatar__stage{
position:relative;width:100%;border-radius:50%;
display:flex;align-items:center;justify-content:center;overflow:hidden;
background:
radial-gradient(circle at 50% 38%, rgba(251,250,248,.98) 0%, rgba(246,240,236,.9) 42%, rgba(238,244,242,.76) 64%, rgba(145,200,189,.22) 84%, rgba(30,39,36,.18) 100%);
box-shadow:
inset 0 0 0 1px rgba(251,250,248,.28),
inset 0 -24px 40px rgba(28,42,42,.13);
}
.vg-avatar__aura{
position:absolute;inset:-12%;border-radius:50%;pointer-events:none;
animation:vgAuraBreathe 6s var(--ease-in-out) infinite;
}
.vg-avatar__aura.is-reduced{animation:none;}
@keyframes vgAuraBreathe{0%,100%{opacity:.85;transform:scale(1)}50%{opacity:1;transform:scale(1.04)}}
.vg-avatar__svg{position:relative;z-index:1;display:block;transition:opacity var(--dur-base) var(--ease-out);}
/* ── 래스터(Live2D식) 렌더: 레이어 합성 흉상 ── */
.vg-raster{position:absolute;inset:0;z-index:1;pointer-events:none;will-change:transform;}
.vg-raster__layer{
position:absolute;left:50%;top:var(--vg-raster-top,-15%);
height:var(--vg-raster-h,136%);width:auto;max-width:none;
transform:translateX(-50%);object-fit:contain;
user-select:none;-webkit-user-drag:none;opacity:0;
}
.vg-raster__layer--shoulders{opacity:1;z-index:1;}
.vg-raster__layer--neck{opacity:1;z-index:2;}
.vg-raster__layer--hairback{opacity:1;z-index:3;}
.vg-raster__layer--ear{opacity:1;z-index:4;}
.vg-raster__layer--face{opacity:1;z-index:5;}
.vg-raster__layer--forehead{opacity:1;z-index:6;}
.vg-raster__layer--hair{opacity:1;z-index:7;will-change:transform;}
.vg-raster__layer--bangs{opacity:1;z-index:8;will-change:transform;}
.vg-raster__layer--brow{z-index:9;will-change:opacity,transform;}
.vg-raster__layer--eyes{z-index:10;}
.vg-raster__layer--eyelid{z-index:11;}
.vg-raster__layer--nose{opacity:1;z-index:12;}
.vg-raster__layer--mouth{z-index:13;}
.vg-raster__layer--mouthopen{z-index:14;}
.vg-raster__layer--base{opacity:1;z-index:1;}
.vg-raster__layer--upperface{z-index:2;}
.vg-avatar__meta{display:flex;flex-direction:column;align-items:center;gap:3px;text-align:center;}
.vg-avatar__persona{font-size:var(--fs-sm);font-weight:600;color:var(--text-strong);}
.vg-avatar__state{font-size:var(--fs-xs);color:var(--text-muted);}
.vg-avatar__state b{font-weight:600;color:var(--text-body);}
@media (prefers-reduced-motion: reduce){
.vg-avatar__aura{animation:none;}
}
`;