ð± ããŒã¿ã«å¶çŽä»æ§æž
察象: ã²ã€ãŸã¶ããžæ²èŒããã²ãŒã ïŒHTML5 / JavaScript / iframeïŒã®éçºè ã»ã¯ãªãšã€ã¿ãŒåãã æŠèŠ: ã²ãŒã 㯠PCã»ã¹ããŒããã©ã³ã»ã¿ãã¬ããã»OBS ãªã©å€æ§ãªç°å¢ã®
<iframe>å ã§åããŸãã ããã«æžãããŠããã®ã¯æšå¥šã§ã¯ãªãå¶çŽã§ãå€ããšç¹å®ã®ç°å¢ã§æäœã§ããªãã»è¡šç€ºã厩ãã ãšãã£ã圢ã§å£ããŸããå®ããŠãããã®ç¢ºèªææ®µã¯ æ€èšŒ ã«ãããŸãã
1. ã¿ããã¿ãŒã²ããæå°ãµã€ãºïŒ44 à 44 pxïŒ
èŠä»¶
ãã¹ãŠã®æäœå¯èœãªUIèŠçŽ ïŒãã¿ã³ã»ãªã³ã¯ã»ãã°ã«ã»ã«ãŒãçïŒã¯ã
ã¿ããã¿ãŒã²ããã®æå°ãµã€ãºã 44 à 44 px 以äžã§å®è£
ããŠãã ããã
ãã㯠Apple Human Interface Guidelines ããã³
WCAG 2.5.5 Target Size (AAA) ã®åºæºã«æºæ ããŠããŸãã
å®è£ äŸ
/* â äžé©å: ã¿ããã¿ãŒã²ãããå°ãããã */
.small-btn {
padding: 2px 6px;
font-size: 0.7rem;
}
/* â
é©å: æå° 44Ã44px ãç¢ºä¿ */
.action-btn {
min-width: 44px;
min-height: 44px;
padding: 10px 16px;
display: inline-flex;
align-items: center;
justify-content: center;
}
泚æäºé
- ã¢ã€ã³ã³ã®ã¿ã®ãã¿ã³ïŒéãã
Ããæ¬¡ãžâ¶çïŒã¯èŠèŠãµã€ãºãå°ãããŠãã
paddingã::before/::afterç䌌èŠçŽ ã§ã¿ããé åã 44 px 以äžã«æ¡åŒµããããšã - ã¢ãã€ã«å¹
ïŒ
< 640pxïŒã§ã¯ç¹ã«æ³šæãPC ã§ã¯åé¡ãªãèŠããŠããã¹ããŒããã©ã³å®æ©ã§ã¿ãããã«ããå Žåãããã
äŸå€ïŒå€ã»ã«ã®ã°ãªããç€é¢
NÃNã®ã°ãªããç€é¢ã§ã¯ããã®èŠå®ãå šã»ã«ã«é©çšãããšç€é¢ãç»é¢ã«åãŸããŸããïŒ9Ã9ã§å¹ 396px以äžã15Ã15ã§660pxè¶ ãå¿ èŠã«ãªããå€ãã®ã¹ããŒããã©ã³ã§ç Žç¶»ããŸãïŒã
ãã®ããç€é¢ã®ã»ã«ã«éããAAAïŒ44pxïŒã§ã¯ãªã AAïŒ24pxïŒãæºããããšã蚱容ããŸããç¬èªã«åºæºãç·©ããŠããã®ã§ã¯ãªããé©çšããæšæºã1段äžãããšããéžæã§ãã
| åºæº | ééã«ããäŸå€ | |
|---|---|---|
| WCAG 2.5.5 Target Size (Enhanced) | AAA / 44 à 44 px | ç¡ã |
| WCAG 2.5.8 Target Size (Minimum) | AA / 24 à 24 px | ããïŒSpacingïŒ |
äŸå€ãé©çšãããæ¡ä»¶ïŒãã¹ãŠæºããå Žåã®ã¿ïŒ:
- 察象ããééãªã飿¥ããŠäžŠã¶åçš®ã®ã»ã«ãã§ããããšïŒåç¬ã§ååšãããã¿ã³ã»ã¡ãã¥ãŒã»ãã¥ãŒãçã¯å¯Ÿè±¡å€ã§ã44pxèŠå®ãç¶æããïŒ
- 誀ã¿ããã®çµæãé£ã®ã»ã«ãžã®çæã«ãšã©ãŸããéåºã»èª²éã»ãªãããçã®äžå¯éãªæäœãå«ãŸãªãããš
- ã»ã«éã«èŠèªã§ããå¢çïŒçœ«ç·ã»
gapïŒããããå¢çãã©ããã¿ããåã«å€å¥ã§ããããš
å€å®ã¯ã»ã«1蟺ã§ã¯ãªããããããã§è¡ããŸã
WCAG 2.5.8 ã® Spacing äŸå€ã¯ãåã¿ãŒã²ããã®äžå¿ã«çŽåŸ24pxã®åã眮ããŠãããé£ãšéãªããªãããšãæ±ããŠããŸããééãªã䞊ã¶ã°ãªããã§ã¯ãããã¯æ¬¡ã®å€ã24px以äžã§ããããšãšåãã§ãã
ããã = ã»ã«1蟺 + gap
ã€ãŸãã»ã«ã倧ããããïŒïŒNãæžããïŒä»¥å€ã«ãgap ãåºãããšããæã¡æããããŸãã
ãããïŒã»ã«1蟺 + gapïŒ | æ±ã |
|---|---|
| 44px ä»¥äž | WCAG 2.5.5ïŒAAAïŒãæºãããå¯èœãªéããããç®æã |
| 32px ã 44px | 蚱容ã ç€é¢å šäœãç»é¢ã«åãŸãããšãåªå ããŠãã |
| 24px ã 32px | 蚱容ãããã誀ã¿ããã®åãæ¶ãææ®µãå¿ é ã ã¿ããã§ä»®éžæïŒãã€ã©ã€ãïŒâ 44px以äžã®ã確å®ããã¿ã³ã§ç¢ºå®ãã2段éæäœããŸãã¯çŽåã®çæãåãæ¶ããUIãæäŸãã |
| 24px æªæº | WCAG 2.5.8ïŒAAïŒãæºãããŸããã ç€é¢ã®ã¹ã¯ããŒã«ïŒãã³ããºãŒã ãæäŸããæ¡å€§ããç¶æ ã§ãããã24px以äžã«ãªãããã«ãã |
32px ã®ã©ã€ã³ã ãã¯å¯Ÿå¿ããæšæºããªãããã®ãããžã§ã¯ãã®å€æã§ãïŒåãæ¶ãææ®µãå¿ é ã«ããç®å®ãšããŠçœ®ããŠããŸãïŒã
å®è¡æã«æž¬ã£ãŠå€æããŠãã ãã
iframeã®ãµã€ãºã¯ããŒã¿ã«åŽã§æ±ºãŸãããããããã®å®å¯žã¯ã¬ã³ããªã³ã°ãããŸã§åãããŸãããResizeObserver ã§ç€é¢ã®å®ãµã€ãºã枬ãïŒÂ§2 â£ã®å®è£
äŸïŒãããããæ±ããŠãã ããã
èšèšæã«ææªã±ãŒã¹ãèšç®ãã
ããŒã¿ã«ãä¿èšŒããæå°è«çãµã€ãºã¯ §2 ã® 308 à 480 ã§ããããããèªåã®ã¬ã€ã¢ãŠãã®å·Šå³ã®äœçœãåŒããšãç€é¢ã«äœ¿ããå¹
ãåºãŸãã
ç€é¢ã«äœ¿ããå¹
= 308 â (å·Šå³ã®äœçœã®åèš)
ããã = (ç€é¢ã«äœ¿ããå¹
+ gap) ÷ N
npx hitsumabushi init --template board ã®é圢ããã®ãŸãŸäœ¿ã£ãå Žåã®å
èš³ã§ãã
| åŒããããã® | |
|---|---|
body ã® padding: 8px | 16px |
ã«ãŒãã® padding: 10px | 20px |
ã«ãŒãã® border: 1px | 2px |
| ç€é¢ã«äœ¿ããå¹ | 270px |
ã»ã«éã® gap 㯠2px ãªã®ã§ãããšãã° N=13 ãªã:
ããã = (270 + 2) ÷ 13 â 21px â 24px ãäžåãïŒAAãæºãããªãïŒ
â ïž ãã® 270px ã¯ãã³ãã¬ãŒãã®äœçœã«äŸåããå€ã§ãã ã¬ã€ã¢ãŠããå€ãããèšç®ãçŽããŠãã ãããN ãããã€ã«ãããã¯ã²ãŒã åŽã®å€æã§ãããã§ç€ºããŠããã®ã¯ããã® N ã ãšäœãå¿ é ã«ãªããããå ã«ç¥ãããã®èšç®æ¹æ³ã§ãã
â ïž SDKã¯ããã«é¢äžããŸããã ãã»ã«1蟺ãäœpxããã¯äžèšã®ãšããã²ãŒã åŽã§æž¬ããŸããããã ããã©ãããããã¯ã²ãŒã ãã¶ã€ã³ã®å€æã§ãã2段éã¿ããã«åãæ¿ããïŒç€é¢ããºãŒã å¯èœã«ããïŒããããç€é¢ãµã€ãºãèŠçŽããã®ã©ããæ£è§£ãã¯ã²ãŒã ã«ãã£ãŠéããŸããSDKãèªåã§åãæ¿ãããšå šã²ãŒã ãåãUXã«ãªãããã倿ã¯ã²ãŒã ã«å§ããŠããŸãã
npx hitsumabushi init --template boardãçæããé圢ã«ã¯ã宿ž¬ã»ã«1蟺ãåãåãããã¯ãå ¥ã£ãŠããŸããããããå ã®æåãæžããŠãã ããã
ããããŠå®è£ ããããš:
- ã»ã«ã®ãããé åã¯ã»ã«å¢çãã£ã±ããŸã§åºããïŒ
.cell { width: 100%; height: 100%; }ãå åŽã«marginãä»ããŠå®å¹é åãçããªãïŒ - ç€é¢ã«
touch-action: manipulationãæå®ããããã«ã¿ãããºãŒã ã«ãã誀åäœãææ¢ãã - 2段éæäœã«ããå Žåãã確å®ããã¿ã³èªäœã¯ 44 à 44 px 以äžãç¶æãã
2. ã¢ãã€ã«ã»çå°ç»é¢å¯Ÿå¿ïŒã¬ã¹ãã³ã·ãUIãšæ å ±å¯åºŠå¶åŸ¡ïŒ
èŠä»¶
ããŒã¿ã«å
ã® <iframe> ã¯ãPCã»ã¿ãã¬ããã»ã¹ããŒããã©ã³ã»OBSïŒé
ä¿¡ãœããïŒãªã©å€æ§ãªç°å¢ã§å¯å€ãµã€ãºïŒå¹
ã»é«ãïŒã§åã蟌ãŸããŸãã
åºå®é«ãïŒäŸ: height: 420px;ïŒã®æå®ã¯é¿ããç»é¢ãµã€ãºã«åãããŠæè»ã«äŒžçž®ããã¬ã€ã¢ãŠããèšèšããŠãã ããã
ã²ãŒã ã«äžããããè«çãµã€ãºïŒãã®è¡šãæ£æ¬ã§ãïŒ
ããŒã¿ã«ã®ã¬ã€ã¢ãŠããã宿ž¬ããå€ã§ãã
| ç°å¢ | ç»é¢ãµã€ãº | ã²ãŒã ã«äžããããé å |
|---|---|---|
| iPhone SEïŒçžŠïŒ | 320 à 568 | 308 à 510 |
| iPhone SE2/3ïŒçžŠïŒ | 375 à 667 | 363 à 609 |
| iPhone 13ïŒçžŠïŒ | 390 à 844 | 378 à 786 |
| Pixel 7ïŒçžŠïŒ | 412 à 915 | 400 à 857 |
| iPhone SEïŒæšªïŒ | 568 à 320 | 556 à 480 â» |
| iPhone 13ïŒæšªïŒ | 844 à 390 | 804 à 480 â» |
| ããŒãPC | 1366 à 768 | 1326 à 648 |
| ãã¹ã¯ããã | 1920 à 1080 | 1360 à 960 |
ããŒã¿ã«ãä¿èšŒããæå°è«çãµã€ãºã¯ 308 à 480 ã§ãã ã°ãªããç€é¢ã®ãææªã±ãŒã¹ã§ããããäœpxã«ãªãããã¯ããã®å€ããèšèšæã«èšç®ã§ããŸãïŒÂ§1ãäŸå€ïŒå€ã»ã«ã®ã°ãªããç€é¢ãã«èšç®æ¹æ³ããããŸãïŒã
⻠暪æã¡ã¹ããã¯æ¬æ¥ 262ã270px ããåããŸããããããŒã¿ã«åŽã min-height: 480px ãèšå®ããŠãããã 480px ã確ä¿ãããŸãïŒãã®ã¶ãããŒã¿ã«ã®ããŒãžãã¹ã¯ããŒã«ããŸãïŒã
éçºããŒãã¹ïŒnpx hitsumabushi devïŒã®ãã¬ãã¥ãŒã¯ããã®è¡šã®ãã¡ä»£è¡šçãª3ã€ïŒãã¹ã¯ããã 1400Ã960 / ã¢ãã€ã« 390Ã786 / OBS 1920Ã1080ïŒãåçŸããŠããŸãã
ãŸãããã¿ã³ãææã«ãŒãçã®èŠçŽ ãå€ãã²ãŒã ã§ã¯ãã¢ãã€ã«ç»é¢ã§æŒã朰ãããŠå¯èªæ§ã»æäœæ§ãæãªãããªããããé©åãªæ å ±å¯åºŠå¶åŸ¡ïŒã¬ã¹ãã³ã·ãåïŒãè¡ã£ãŠãã ããã
æšå¥šå®è£ ãã¿ãŒã³
â ãã¿ã³ã®ãããã¹ãçç¥ïŒã¢ã€ã³ã³åãïŒ44 à 44 px ãšã®äž¡ç«ïŒ
PCã§ã¯ãã¢ã€ã³ã³ïŒèª¬æããã¹ããã衚瀺ããã¢ãã€ã«å¹
ïŒ< 640pxïŒãäœç»é¢é«ïŒ< 560pxïŒã§ã¯ããã¹ããé衚瀺ã«ããŠã¢ã€ã³ã³ã®ã¿ãšããããšã§ã暪å¹
ã倧å¹
ã«ç¯çŽã§ããŸãã
â»ã¢ã€ã³ã³åããå Žåã§ãã第2é
ã® ãã¿ããã¿ãŒã²ããæå° 44 à 44 pxã ã¯å¿
ã確ä¿ããŠãã ããã
<!-- HTMLæ§é : ã¢ã€ã³ã³ãšããã¹ããåé¢ -->
<button class="action-btn" title="ã«ãŒããåŒã" aria-label="ã«ãŒããåŒã">
<span class="btn-icon">ðŽ</span>
<span class="btn-label">ã«ãŒããåŒã</span>
</button>
/* PCã»æšæºå¹
*/
.action-btn {
min-width: 44px;
min-height: 44px;
padding: 8px 16px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
}
/* ã¢ãã€ã«å¹
ïŒ640px以äžïŒãŸãã¯äœç»é¢é«ïŒ560px以äžïŒ */
@media (max-width: 640px), (max-height: 560px) {
.action-btn .btn-label {
display: none; /* ããã¹ããé ããŠã¢ã€ã³ã³ã ãã«ãã */
}
.action-btn {
padding: 8px;
width: 44px; /* 44Ã44pxã®æ£æ¹åœ¢ã«ã³ã³ãã¯ãå */
height: 44px;
}
}
â¡ ã«ãŒãã»ããŒã¯ã³ã®å¯å€ã¹ã±ãŒãªã³ã°ïŒclamp() ã®æŽ»çšïŒ
ææãã³ããå€ãã²ãŒã ã§ã¯ãåºå®å¹
ã§ã¯ãªã CSS ã® clamp() ã min() ãæŽ»çšããŠç»é¢å¹
ã«å¿ããŠæ»ããã«çž®å°ãããŸãã
/* ææã«ãŒãã®å¯å€ã¹ã±ãŒã«ïŒæå°36pxãæå€§64pxãç»é¢å¹
9vwã«è¿œåŸïŒ */
.hand-card {
width: clamp(36px, 9vw, 64px);
aspect-ratio: 2 / 3;
flex-shrink: 1;
}
⢠ãã¬ã€ãã£ãŒã«ãã®èªå䌞瞮ïŒflex: 1ïŒ
ããããŒãã¹ã³ã¢ããŒããäžéšã¹ããŒã¿ã¹ããŒã flex-shrink: 0; ãšããã¡ã€ã³ã®ç€é¢ïŒã¢ãªãŒãã»ããŒãïŒã flex: 1; min-height: 150px; ãšããããšã§ãäžäžèŠåããèµ·ãããå®å
šã«åãŸããŸãã
.game-container {
width: 100%;
max-width: 640px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.hud-header { flex-shrink: 0; }
.play-arena { flex: 1; min-height: 160px; width: 100%; } /* èªå䌞瞮 */
.status-bar { flex-shrink: 0; }
⣠NÃN ã°ãªããç€é¢ã®æ£æ¹åœ¢ãã£ããïŒResizeObserver ã«ãã宿ž¬ïŒ
äºç®äžŠã¹ã»ãªã»ãã»äžç®äžŠã¹ã»ã³ãã¯ããã©ãŒãªã©ãNÃNã®æ£æ¹åœ¢ã°ãªããç€é¢ãã¯å®çªãžã£ã³ã«ã§ãããé«ããå¯å€ïŒäºåã«äžæïŒãªflexã³ã³ããã®äžã§ãã¢ã¹ãã¯ãæ¯1:1ãä¿ã£ããŸãŸæå€§ãµã€ãºã«ãã£ãããããå¿ èŠããããCSSã ãã§ã¯å®å®ããŸããã
CSSã ãã§ã¯äžå®å®ã«ãªãçç±:
| ææ®µ | åé¡ |
|---|---|
aspect-ratio: 1 / 1 | ⢠㮠flex: 1 ã³ã³ããå
ã§ã¯å¹
åºæºã§é«ãã解決ãããããã瞊ãè¶³ããªãç»é¢ã§ç€é¢ãäžã«ã¯ã¿åºã |
vh / dvh åäœ | iOS Safariã®URLããŒäŒžçž®ã§å€åããå転çŽåŸãå®å®ããªããiframeå ã§ã¯ããŒã¿ã«åŽã®ããããŒé«ããèæ ®ã§ããªã |
ã³ã³ããã¯ãšãªïŒcqw / cqhïŒ | cqh ã¯èŠªã« container-type: size ãå¿
èŠã§ããã®èŠªã®é«ãã flex: 1 ç±æ¥ã ãšåŸªç°ãããã |
æšå¥š: ResizeObserver ã§èŠªèŠçŽ ã®å®ãµã€ãºã枬ããpx ã§æå®ããã
ãã®3ç¹ã»ããïŒflex: 1 + min-height: 0 ã®å
¥ãç© / ResizeObserver ã«ãã宿ž¬ / ã»ã«1èŸºã®æŽæ°pxäžžãïŒãå®è£
æžã¿ã®é圢ã忢±ãããŠããŸãããŒãããæžããã«ãã¡ãã䜿ã£ãŠãã ããã
npx hitsumabushi init --template board
å®è£ æã®æ³šæ:
ResizeObserverã¯ç»é¢å転ã»OBSãã©ãŠã¶ãœãŒã¹ã®ãµã€ãºå€æŽã»ããŒã¿ã«åŽã®ã¬ã€ã¢ãŠãå€åãã¹ãŠã«è¿œåŸããŸããwindow.resizeã€ãã³ãã ãã§ã¯ã芪èŠçŽ ã®ãµã€ãºã ããå€ããã±ãŒã¹ãåãéããŸãã- åæåçŽåŸãiframeãé衚瀺ã®éã¯
clientWidth/clientHeightã0ã«ãªããŸãã0ã®ãšãã¯äœãããªãã¬ãŒããå¿ ãå ¥ããŠãã ããïŒç€é¢ãå¹ 0ã§æç»ããã以åŸåŸ©åž°ããªããªããŸãïŒã - ã»ã«ã®å
容ïŒç³ã»èšå·ïŒã¯
font-sizeãå®å¯žã«è¿œåŸãããŸããfitBoard()ã®äžã§ãã»ã«1蟺ïŒsnapped / SIZEïŒã® 0.6 åçšåºŠãboard.style.fontSizeã« px æå®ããã®ãç°¡åã§ãã
3. ã¹ã¯ããŒã«ããŠã³ã¹ææ¢ïŒiOS Safari 察çïŒ
èŠä»¶
iframe å
ã®ã³ã³ãã³ãã§ iOS Safari ã®æ
£æ§ã¹ã¯ããŒã«ïŒããŠã³ã¹ïŒ ãæå³ããªã圢ã§çºçãããšã
ã²ãŒã ã®æäœæãèããæãªããŸããã²ãŒã ç»é¢å
šäœã§ã¹ã¯ããŒã«ããŠã³ã¹ãææ¢ããŠãã ããã
å®è£ äŸ
/* ã²ãŒã å
šäœã®ããŠã³ã¹ææ¢ */
html,
body {
overscroll-behavior: none; /* æšæºä»æ§ïŒChrome / Firefox / Safari 16+ïŒ */
overflow: hidden; /* ã²ãŒã ç»é¢ãåºå®ã¬ã€ã¢ãŠãã®å Žå */
}
/* ã¹ã¯ããŒã«å¯èœãªãµãé åïŒã¹ã³ã¢ããŒãã»ãã£ããçïŒã®ã¿ã¹ã¯ããŒã«èš±å¯ */
.scrollable-area {
overflow-y: auto;
overscroll-behavior: contain; /* 芪èŠçŽ ãžã®ã¹ã¯ããŒã«äŒæã鲿¢ */
-webkit-overflow-scrolling: touch;
}
// canvas ã®ã¿ããã¹ã¯ããŒã«å¹²æžã鲿¢
canvas.addEventListener('touchstart', (e) => e.preventDefault(), { passive: false });
canvas.addEventListener('touchmove', (e) => e.preventDefault(), { passive: false });
4. ç»é¢å転察å¿ïŒã¹ããŒããã©ã³åãæ¡å ïŒ
èŠä»¶
ã²ãŒã ãã©ã³ãã¹ã±ãŒãïŒæšªåãïŒå°çšãŸãã¯äžæ¹åã§ã®ã¿æé©ã«åäœããå Žåã
ãŠãŒã¶ãŒã«ç»é¢å転ãä¿ãã¬ã€ãã³ã¹ã衚瀺ããŠãã ããã
æšå¥šå®è£
/* ããŒãã¬ãŒãïŒçžŠåãïŒæã«å転æ¡å
ãªãŒããŒã¬ã€ã衚瀺 */
@media screen and (orientation: portrait) and (max-width: 768px) {
.rotate-prompt { display: flex; }
.game-canvas-wrapper { display: none; }
}
@media screen and (orientation: landscape) {
.rotate-prompt { display: none; }
.game-canvas-wrapper { display: block; }
}
.rotate-prompt {
position: fixed;
inset: 0;
z-index: 9999;
display: none;
align-items: center;
justify-content: center;
background: rgba(10, 11, 16, 0.95);
color: #f8fafc;
text-align: center;
padding: 24px;
}
.rotate-prompt__icon {
font-size: 3rem;
display: block;
margin-bottom: 16px;
animation: rotate-hint 2s ease-in-out infinite;
}
@keyframes rotate-hint {
0%, 100% { transform: rotate(0deg); }
50% { transform: rotate(90deg); }
}
<div class="rotate-prompt" aria-live="polite">
<div>
<span class="rotate-prompt__icon">ð±â©ïž</span>
<p>ãã®ã²ãŒã ã¯ã¹ããŒããã©ã³ã<strong>暪åã</strong>ã«ããŠã楜ãã¿ãã ããã</p>
</div>
</div>
泚æäºé
- 瞊åãã»æšªåãã®äž¡æ¹ã§èªç¶ã«åäœããã²ãŒã ã®å Žåãå転æ¡å ã¯äžèŠã§ãã
screen.orientation.lock()API 㯠iOS Safari ã§ã¯é察å¿ã®ããã匷å¶å転ã§ã¯ãªããŠãŒã¶ãŒãžã®æ¡å ãæšå¥šããŸãã
5. ãªãŒãã£ãªèªååç察å¿
èŠä»¶
ãã©ãŠã¶ã®ãªãŒãã£ãªèªååçããªã·ãŒã«ããããŠãŒã¶ãŒæäœãªãã«é³å£°ãåçããããšã¯ã§ããŸããã
ã²ãŒã ã® BGMã»å¹æé³ã¯å¿
ããŠãŒã¶ãŒæäœã奿©ã«åçãéå§ããŠãã ããã
ãã©ãŠã¶ããªã·ãŒã®æŠèŠ
| ãã©ãŠã¶ | èªååçããªã·ãŒ |
|---|---|
| Chrome 66+ | ãŠãŒã¶ãŒã€ã³ã¿ã©ã¯ã·ã§ã³ãªãã® Audio.play() ã¯ããã㯠|
| Safari (iOS) | ãŠãŒã¶ãŒã€ã³ã¿ã©ã¯ã·ã§ã³ãªãã®åçã¯åžžã«ããã㯠|
| Firefox 66+ | ãµã€ãã®ã¡ãã£ã¢èªååçèšå®ã«äŸåïŒããã©ã«ãèš±å¯ïŒ |
æšå¥šå®è£ ãã¿ãŒã³
// ãã¿ãŒã³1: æåã®ãŠãŒã¶ãŒæäœã§ AudioContext ãè§£é€ãã
let audioContext = null;
function initAudio() {
if (audioContext) return;
audioContext = new (window.AudioContext || window.webkitAudioContext)();
if (audioContext.state === 'suspended') audioContext.resume();
playBGM();
}
document.addEventListener('click', initAudio, { once: true });
document.addEventListener('touchstart', initAudio, { once: true });
document.addEventListener('keydown', initAudio, { once: true });
// ãã¿ãŒã³2: <audio> èŠçŽ ã®éåæã¬ãŒã
async function safePlay(audioElement) {
try {
await audioElement.play();
} catch (err) {
// NotAllowedError: ãŠãŒã¶ãŒæäœåŸã«å詊è¡
console.warn('[Audio] èªååçãããã¯:', err.message);
}
}
ãã¥ãŒããã¿ã³ã®æäŸ
é
ä¿¡è
ã BGM ãªãã§é
ä¿¡ããå Žåãèæ
®ãããã¥ãŒã/é³éèª¿ç¯ UI ãå¿
ãæäŸããŠãã ããã
ãã¥ãŒããã¿ã³ã®ã¿ããã¿ãŒã²ããã¯æå° 44 à 44 px ã確ä¿ããããšã
OBS ã¢ãŒãã§ã® BGM ã®æ±ã㯠§8 OBS ã¢ãŒã ã«ãããŸãã
6. iframe éä¿¡ïŒHitsumabushi SDK ãšã®é£æºïŒ
ããŒã¿ã«ãšã®é信㯠window.postMessage / Hitsumabushi SDK ã䜿ããŸããã€ãã³ãåã»ãã€ããŒã圢åŒã»
éä¿¡ã®äžéå€ïŒãµã€ãºãšã¬ãŒãïŒã¯ãã¹ãŠ API ãªãã¡ã¬ã³ã¹ ãæ£æ¬ã§ããæ¬æžã§ã¯éè€æ²èŒããŸããã
â ïž æ¯ãã¬ãŒã éä¿¡ã¯äžéã«åŒµãä»ããŸãã ã¬ãŒãè¶ éã¯ç¡èšã§ç Žæ£ãããããã åããªãã®ã«çç±ãåãããªãäžå ·åã«ãªããŸããéä¿¡ã¯ç¶æ ãå€ãã£ããšãã ãã«ããŠãã ããã
7. ãµã³ãããã¯ã¹ïŒã¹ãã¬ãŒãžãš CORSïŒ
èŠä»¶
ããŒã¿ã«ã¯ã²ãŒã ã® <iframe> ã« sandbox="allow-scripts" ã ããä»ããŸããã²ãŒã ã¯èªåã®é
ä¿¡å
ã®ãªãªãžã³ã§ã¯ãªããäžéæãªãªãªãžã³ïŒ"null"ïŒã§åããŸãããã®ããæ¬¡ã®2ã€ãå®ã£ãŠãã ããã
- ãã©ãŠã¶ã®ã¹ãã¬ãŒãžãš Cookie ã䜿ããªãã
localStorageã»sessionStorageã»IndexedDBã»document.cookieã¯äŸå€ã«ãªãããä¿åãããŸãããèšå®ãé²è¡ç¶æ³ããã©ãŠã¶ã«æ®ãããšã¯ã§ããŸããã - é
ä¿¡å
ã§
Access-Control-Allow-Originãè¿ãã æ¬¡ã®èªã¿èŸŒã¿ã¯ CORS ã§è¡ãããé ä¿¡å ããã®ããããŒïŒ*ïŒãè¿ããªããšå€±æããŸãã<script type="module">ïŒVite ãªã©ã®ãã«ãããŒã«ãåºåãã圢ïŒcrossorigin屿§ã®ä»ãã<script>/<link rel="stylesheet">fetch()/XMLHttpRequestïŒèªåã®é ä¿¡å ã®ãã¡ã€ã«ã§ãåãïŒ- Web ãã©ã³ãïŒ
@font-faceïŒ
ã³ãã¥ããã£æ ïŒhitsumabushi-community-gamesïŒã«æçš¿ããã²ãŒã ã¯ãé
ä¿¡å
ããã®ããããŒãè¿ãã®ã§å¯Ÿå¿ã¯èŠããŸãããèªåã®ãµãŒããŒã§é
ä¿¡ããå Žåã¯ãã²ãŒã ã®å
šãã¡ã€ã«ã«ãã®ããããŒãä»ããŠãã ããã
ãªã
ã³ãã¥ããã£æ ã®ã²ãŒã ã¯åãé ä¿¡å ïŒåããªãªãžã³ïŒãå ±æããŠããŸãããµã³ãããã¯ã¹ãç¡ããšãããã²ãŒã ãã»ãã®ã²ãŒã ã®ä¿åããŒã¿ã Cookie ãèªã¿æžãã§ããŠããŸããŸãã
ãããã¢ããïŒalert() / confirm() / window.open()ïŒããã©ãŒã ã®éä¿¡ããã€ã³ã¿ãŒããã¯ããããã¬ãã«ã®é·ç§»ãèš±å¯ããŠããŸããã
確èªã®ä»æ¹
éçºããŒãã¹ã® iframe ã«ã¯ãµã³ãããã¯ã¹ããããŸããïŒæ€èšŒ §4ïŒãããŒãã¹ã§åããŠããã¹ãã¬ãŒãžã CORS ã®åé¡ã¯æ¬çªã§åããŠåºãŸãã
8. OBS ã¢ãŒãïŒé ä¿¡ç»é¢ãžã®éãåããïŒ
èŠä»¶
é ä¿¡è ã¯ãããŒã¿ã«ã OBS ã®ãã©ãŠã¶ãœãŒã¹ãšããŠåã蟌ã¿ãã²ãŒã ç»é¢ãèªåã®é ä¿¡ç»é¢ïŒç«ã¡çµµãèæ¯ïŒã®äžã«éããŸãããã®ãšãããŒã¿ã«ã¯ OBS ã¢ãŒãã«ãªããèªåã®èæ¯ã»ããããŒã»åºåãæ¶ããŸããã²ãŒã ã®å¯ŸæŠãã£ãŒã«ãã UI ã ããæµ®ãã³äžããã«ã¯ãã²ãŒã ã®åŽã次ã®2ã€ãããŠãã ããã
- èæ¯ãéæã«ãã â
htmlãbodyãå šç»é¢ãèŠãèŠçŽ ã«ä»ããŠããèæ¯è²ãtransparentã«ããŸããç€é¢ã»ã«ãŒãã»ãã¿ã³ãªã©ãã²ãŒã ã®äžèº«ã®èæ¯ã¯æ®ããŠæ§ããŸããã - BGM ããã¥ãŒãã«ããã
OBS ã¢ãŒãã®åãåãæ¹
OBS ã¢ãŒããã©ãã㯠SDK ãäŒããŸãã
| ã〠| åãåãæ¹ |
|---|---|
| 詊åã®éå§æ | onCreateMatch(config) / onStartMatch(config) ã® config.isObsMode |
| 詊åäžã«åãæ¿ãã£ããšã | onObsModeChange(isObsMode) |
onObsModeChange ã¯æåãã OBS ã¢ãŒãã®ãšãã«åŒã°ãããšã¯éããŸãããconfig.isObsMode ãå¿
ãèŠãŠãã ããïŒAPI ãªãã¡ã¬ã³ã¹ §2ïŒã
function applyObsMode(isObsMode) {
document.body.classList.toggle('transparent', isObsMode);
bgm.muted = isObsMode;
}
Hitsumabushi.init({
onStartMatch: (config) => {
applyObsMode(config.isObsMode);
// ...
},
onObsModeChange: applyObsMode,
});
body.transparent { background-color: transparent !important; }
npx hitsumabushi init ã眮ããã³ãã¬ãŒãã¯ãèæ¯ã®ééã«å¯Ÿå¿ããŠããŸãïŒBGM ã¯ãã³ãã¬ãŒãã«ç¡ãããã鳎ããã²ãŒã ãè¶³ããŠãã ããïŒã
確èªã®ä»æ¹
éçºããŒãã¹ïŒnpx hitsumabushi devïŒã® OBS ã¢ãŒãã®åãæ¿ãã§ãèæ¯ãéã㊠BGM ãæ¢ãŸãããšãç®ã§ç¢ºãããŠãã ãããverify ã¯ãã®é
ç®ãå€å®ããŸããïŒæ€èšŒ §5ïŒã