æ¦è¦
ããã§ã¯ãç´«å¾®ææ°ã¨ããå ãã®å½ç¤ãä½ãããã®ç¤é¢ã表ã HTML/CSS ã¨ã ããã«ã¢ãã¡ã¼ã·ã§ã³ãå ãã TypeScript ãæ¸ãã¦ã¿ãã
æ¦è¦ã¨äºã®çµç·¯ã«ã¤ãã¦ã¯ã 0. Figma 奮éè¨ã¨ JavaScript ã¨ã®éé ãåç §ã®ãã¨ã
- 0. Figma 奮éè¨ã¨ JavaScript ã¨ã®éé
- 1. æä½éã®ç°å¢æ§ç¯ã¨ã¢ãã¡ã¼ã·ã§ã³
- 2. TypeScript ã®ç°å¢æ§ç¯
- 3. å½ç¤ç¨ã®ç¤é¢ã®ä½æ
- 4. JEST 㨠MVVM ã®å°å ¥
- 5. TestView ã®å®è£ 1
- 6. TestView ã®å®è£ 2
ç¤é¢ã®ä½æ
0ç« ã§å³ç¤ºããããã«ãç´«å¾®ææ°ã¯å½ç¤ã¨å¼ã°ããç¤é¢ã«æãé ç½®ãã¦å ããããããå æè¡ã§ããã æ£æ¹å½¢ã®å¨ä¸ã«ã12åã®å°ããæ£æ¹å½¢ãé ç½®ãã¦ãããã«æãé ããã ãã®12åã®ç®±ããå®®ãã¨å¼ã³ã12åã®å®®ãéã¾ã£ãç¤é¢ã®ãã¨ãå½ç¤ã¨å¼ã¶ã å ãã¯ã HTML/CSS ã§ãã®ç¤é¢ãå®ç¾©ãã¦ãããã
HTML
ç¤é¢ã¯æè¨åãã«å®ç¾©ããã¦ããã
HTML ã§ä¸è¬çã«å®ç¾©ããããããªé層æ§é ã§ã¯ãªããã¾ãã表ã¨ãå°ãéãã
ãã®ããã <div>
è¦ç´ ã綺éºã«12å並ã¹ãå¿
è¦ãããã
ã¨ããããã HTML ãã¡ã¤ã«ã« div è¦ç´ ã 12å並ã¹ã¦ã¿ããã
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>ç·´ç¿</title> <meta name="description" content="HTML ç·´ç¿"> <meta name="keywords" content="HTML"> <link rel="stylesheet" href="style.css"> </head> <body> <div class="background-frame"> <div class="foreground-frame"> ç´«å¾®ææ° </div> <div id="life-board" class="life-board"> <div id="palace-0" class="palace-box palace">å</div> <div id="palace-1" class="palace-box palace">ä¸</div> <div id="palace-2" class="palace-box palace">å¯ </div> <div id="palace-3" class="palace-box palace">å¯</div> <div id="palace-4" class="palace-box palace">è¾°</div> <div id="palace-5" class="palace-box palace">å·³</div> <div id="palace-6" class="palace-box palace">å</div> <div id="palace-7" class="palace-box palace">æª</div> <div id="palace-8" class="palace-box palace">ç³</div> <div id="palace-9" class="palace-box palace">é </div> <div id="palace-10" class="palace-box palace">æ</div> <div id="palace-11" class="palace-box palace">亥</div> <div id="palace-center" class="palace-box palace-center">å½ç¤</div> </div> </div> <script src="dist/script.js"></script> </body> </html>
ã¨ããããã§ã12åã®å®®(palace)ã¨ãä¸å¤®ã®å¤§ããªæ£æ¹å½¢ palace-center ãå®ç¾©ããã ããã13åã® div ããã®ã¾ã¾ãã¼ã¸å ¨ä½ã«è¼ããã®ã§ã¯ãªãã èæ¯ãã¬ã¼ã ã¨ã㦠background-frameã 忝ãã¬ã¼ã ã¨ã㦠life-board ãç¨æãããã¨ã«ãããã
div ã«ã¯ id 㨠class ãæ¢ã«å²ãæ¯ã£ã¦ãããã id ã¯ãã¨ã§ TypeScript ããåç §ããããã®ååã ã class 㯠CSS ã§ã¹ã¿ã¤ã«ãå®ç¾©ããããã®ãã®ã§ããã
CSS (ãã¼ã¹ã®é ç½®)
TypeScript ã¯æå¾ã«è§¦ãã¨ãã¦ãå ã㯠CSS ã触ã£ã¦ãããã
CSS ã§ã夿°ãå®ç¾©ã§ããã®ã§ã å¾ã§ãããããããªãããã«ãäºãããã¤ãã®å¤æ°ãå®ç¾©ãã¦ãããã
:root { --white: rgb(239, 230, 244); --dark: rgb(85, 58, 97);; --background-bg: rgb(226, 208, 233); --foreground-fg: var(--white); --foreground-bg: rgb(85, 58, 97); --palace-fg: var(--dark); --palace-bg: rgb(234, 222, 239); --palace-bd-width: 2px }
VScode ã®æ¡å¼µæ©è½ã§ã«ã©ã¼ããã«ã¼ãå ¥ãã¦ããã¨ãè²é¸æããããããã
次ã«ã body, background-frame, foreground-frame, life-board ãå®ç¾©ãã¦ããã
body { background-color: var(--body-bg); height: 100vh; margin: 0; } .background-frame { background-color: var(--background-bg); height: 95vmin; width: 62vmin; min-width: calc(512px + 32px * 2); min-height: calc(512px + (64px + 32px) * 3); margin: 0 auto; } .foreground-frame { background-color: var(--foreground-bg); color: var(--foreground-fg); font-family: "Zen Maru Gothic"; font-size: 35px; width: 100%; text-align: center; } .life-board { background-color: var(--palace-bg); width: 60vmin; height: 60vmin; min-width: 512px; min-height: 512px; max-width: 1024px; max-height: 1024px; border: 2px solid var(--palace-bd-color); position: relative; margin: 32px auto; }
ã¤ã¡ã¼ã¸ã¨ãã¦ã¯ã
- body: å ¨ä½ã®èæ¯
- background-frame: å½ç¤ãè¼ãããã¬ã¼ã ãå¾ã§æä½ç¨ã®ãã¿ã³çãè¼ããããã«å°ã縦é·ã«ãã¦ããã
- foreground-frame: ãç´«å¾®ææ°ãã¨ããååãæ¸ãããããé¨åã
- life-board: 12å®®ãè¼ãã¦ããããã®æ£æ¹å½¢ã®ãã¬ã¼ã ã
ã¨ãããããªå ·åã ã
ç¾æç¹ã§ã¯ã以ä¸ã®ãããªè¡¨ç¤ºã«ãªãã¯ãã§ããã
CSS (å®®ã®è¨å®)
ããããã CSS ã®æ©è½ãæ´»ç¨ãã¦ã12å®®ãé ç½®ãã¦ãããã
ã©ãããæ¦ç¥ã§12å®®ãé ç½®ãããã¨ããã¨ã % æå®ã§25%ãã¤ã®å¤§ããã§å®®ã®å¤§ããã¨ä½ç½®ãæå®ããã ã¨ããåç´ãªæ¹æ³ãæ¡ããã¨ã«ããã(ã¨ããããä»ã«æãã¤ããªãã£ã)ã
ããããå ´åãåºæ¬çã«ã¯è¦ªè¦ç´ ã« position: relative
ãå
¥ãã¦ããã
åè¦ç´ ã« position: absolute
ãå
¥ããã°ãããããã
å
ãã㦠life-board ã« position: relative
ã¯å
¥ãã¦ãããã®ã§ã
palace-box ã以ä¸ã®ããã«è¨å®ãããã¨ã«ãããã
.palace-box { position: absolute; background-color: var(--palace-bg); border: var(--palace-bd-width) solid var(--palace-bd-color); box-sizing: border-box; text-align: center; }
èæ¯è²ãå¢çç·ãè¨å®ããã¨ã¨ãã«ã box-sizing
ãæå®ãã¦ããã
ããã border-box
ã«æå®ãããã¨ã§ã
å¢çï¼borderï¼ããã³å
é¨ä½ç½ï¼paddingï¼ãã
è¦ç´ ã«æå®ããå¹
ï¼widthï¼ããã³é«ãï¼heightï¼ã®ä¸ã«å«ã¾ããããã«ãªãã
ä»åã¯å¢çç·å«ã綺éºã« 25% ã®ãµã¤ãºã«ãªã£ã¦ã»ããã®ã§ã
border-box
ãæå®ãã¦ããã
次ã«ã palace 㨠palace-center ãè¨å®ãã¦ã¿ããã
.palace { width: 25%; height: 25%; min-width: 128px; min-height: 128px; } .palace-center { width: 50%; height: 50%; min-width: 256px; min-height: 256px; top: 25%; left: 25%; }
大ããã¨æå°ãµã¤ãºãè¨å®ããã
palace-center ã«é¢ãã¦ã¯ãä½ç½®ãè¨å®ãã¦ãããã
top
㨠left
ãããã ã
ä»ã®ã¨ããã index.html ã®è¦ãç®ã¯ä»¥ä¸ã®ããã«ãªã£ã¦ããã¯ãã ã
CSS (å®®ã®é ç½®)
å³ã§ã¯ã12å®®ãä¸ç®æã«éãªã£ã¦ãã¾ã£ã¦ããã æå¾ã«ããããæ£æ¹å½¢ã®å¨ä¸ã«ä¸¦ã¹ã¦ããããã
ãããã palace ã表ã div è¦ç´ ã«ã¯ãå¥ã ã® class åãå²ãå½ã¦ãªãã£ãã ã©ããã£ã¦ 12宮㮠div ãåºå¥ããªããä½ç½®ã調æ´ããã°ããã ãããï¼
ã©ãããæè¿ã® CSS ã«ã¯ nth-of-type()
ãªã©ã¨ããè¨æ³ããããããã
ããã§ããN çªç®ã®è¦ç´ ã«å¯¾ã㦠CSS ãæå®ãããã¨ãããã¨ãã§ããããã ã
å ·ä½çã«ã¯ã以ä¸ã®ããã«æ¸ãã°ããã
.palace:nth-of-type(1) { top: 0%; left: 0%; } .palace:nth-of-type(2) { top: 0%; left: 25%; } .palace:nth-of-type(3) { top: 0%; left: 50%; } [...] .palace:nth-of-type(12) { top: 25%; left: 0%; }
éä¸çç¥ãã¦ãããã 25% ãã¤ãã©ã¡ã¼ã¿ãå¤ããªãããæ£æ¹å½¢ã®å¨ä¸ã«æè¨åãã«é ç½®ãã¦ããã
ããã§ã以ä¸ã®å³ã®ããã«ãããã«å®®ã並ã¶ã¯ãã ã
æè¿ã® HTML/CSS ã®é²åã«ã¯é©ãã°ããã§ããã (ãããããããããæããã§ããã®ãããããªããã»ã»ã»ã)
ã¾ããå ¨ä½çã«çç¥æ°å³ã§æç¸®ã§ããã ã¨ã¯ããã HTML/CSS é¢é£ã¯éä¸èª¬æãã¦ããã¨ãããã¹ãã¼ã¹ããã£ã¦ãè¶³ããªãã
TypeScript ã«ããã¢ãã¡ã¼ã·ã§ã³ã®è¿½å
ããã§ã¯ã以ä¸ã®2ã¤ã®ã¢ãã¡ã¼ã·ã§ã³ã追å ãã¦ããããã
- ã¯ãªãã¯ã§å®®ã鏿ããã 鏿ããå®®ã®å¢çç·ã太ããªãããã«ããã
- ãã¦ã¹ãªã¼ãã¼ã§ä¸æ¹åæ£ããã¤ã©ã¤ãããã ãã¦ã¹ãªã¼ãã¼ãã䏿¹åæ£ã«å½ããå®®ã®è²ãæ¿ããªãããã«ããã
ã䏿¹åæ£ãã«ã¤ãã¦ã¯èª¬æãå¿ è¦ã ããã ç°¡åã«è¨ãã¨ãããå®®ããè¦ã¦ã対è§ç·ä¸ã«ããå®®(対宮)ã¨ããã®å¯¾å®®ã®ä¸¡å´2ã¤é£ã®å®®(ä¸åå®®)ãæå³ããã ä¾ãã°ããåãå®®ããè¦ãã¨ããåãå®®ã対宮ã§ããåãå®®ã»ãè¾°ãå®®ã»ãç³ãå®®ã䏿¹åæ£ã¨ãªãã å ããããä¸ã§ãåå®®ããããã人çéã¨ãééã¨ãææéã¨ãã表ã訳ã ãã ãã®å®®ã ãã§ãªãããã®å®®ã®ä¸æ¹åæ£ã¨ããã3ã¤ã®å®®ãéè¦ã¨ããã¦ããã®ã§ã èªåã§ãã¤ã©ã¤ããã¦ãããã¨å©ããã®ã ã
ã¾ãã䏿ºåã¨ãã¦ã å®®ã表ã div è¦ç´ ãåå¾ãã¦ãããã å¾ã§ä½ãè¨ç®ãç¨ãã¦å®®ã鏿ããªããã°ãªããªããã¨ã¯ç¢ºå®ãªã®ã§ã å®®ãªã¹ã palace_list ãæã£ã¦ããå¿ è¦ãããã ã¾ããé¸æç¶æ ãä¿æãã¦ãã夿°ãªã¹ããå¿ è¦ã ããã
ã¨ãããã¨ã§ãã³ã¼ãã¯ä»¥ä¸ã®ããã«ãªãã
function main() { const palace_list: HTMLElement[] = [] const palace_list_selected: boolean[] = [] const n = 12 for (let i = 0; i < n; i++) { const palace = document.getElementById(`palace-${i}`) if (palace == null) { throw new Error("palace not found."); } palace_list.push(palace) palace_list_selected.push(false) } }
ã¯ã©ã¹ãå°å ¥ãããæ°æã¡ãããããããã¯å¾ã§ãã¹ãç°å¢ã¨ MVVM ãã¿ã¼ã³ãå°å ¥ãã¦ããã«ãããã
å®®ã®é¸æ
ã¾ããå®®ã®é¸æã¢ãã¡ã¼ã·ã§ã³ãå°å ¥ãããã
å³å¯ã«ã¯ãããã¯ãå®®ã®é¸æãã¨ãå®®ã®é¸æè§£é¤ãã®2段éã®ã¢ãã¡ã¼ã·ã§ã³ã«ãªãã
å®®ãã¯ãªãã¯ãããã¨ãã(æªé¸æç¶æ ãªãã°)é¸æç¶æ ã«ãã¦ããå®®ã®é¸æãã¢ãã¡ã¼ã·ã§ã³ãå®è¡ããã
ä»ã®å®®ãã¯ãªãã¯ãããããæ¢ã«é¸æç¶æ ã®å®®ã鏿ãããå ´åã¯ã 鏿æ¸ã¿ã®å®®ã«ã¤ãã¦ãå®®ã®é¸æè§£é¤ãã®ã¢ãã¡ã¼ã·ã§ã³ãå®è¡ããå¿ è¦ãããã
ã¨ã«ãããã«ããå ãã¯ã¢ãã¡ã¼ã·ã§ã³ãå®è¡ãã颿°ãå®ç¾©ãã¦ãããã
function AnimeChangeBorder(element: HTMLElement, width: string) { anime({ targets: element, borderWidth: width, duration: 200, easing: 'linear' }) }
AnimeChangeColor 颿°ã® backgroundColor ã borderWidth ã«ç½®ãæããã ãã®ç°¡åãªãã®ã ã
ããããã¯ãªãã¯æã«é©åã«å®è¡ãããããã«ããã°ããã å ·ä½çã«ã¯ã以ä¸ã®ã³ã¼ãã§åä½ããã
palace.addEventListener('click', () => { let next_state = !palace_list_selected[i] if (next_state) { AnimeChangeBorder(palace, "6px") } for (let j = 0; j < n; j++) { if (palace_list_selected[j]) { palace_list_selected[j] = false AnimeChangeBorder(palace_list[j], "2px") } } palace_list_selected[i] = next_state });
ã¢ã«ã´ãªãºã ã¯ãå°ãã®ããã¯çã ãã以ä¸ã®ããã«ãªã£ã¦ããã
- ã¾ããæ¬¡ç¶æ
ã
next_state
ã«å ¥ãã - æ¬¡ç¶æ
next_state
ãé¸æç¶æ ã«ãªãå ´åã¯ãå¢çç·ã 2px ãã 6px ã«å¤ªãããã - 次ã«ãä»ã®å®®ã®ç¶æ ã確èªãããæ¢ã«é¸æç¶æ ã®ãã®ãããå ´åã¯ããããéé¸æç¶æ ã«ãããå¢çç·ã 6px ãã 2px ã«ç´°ãããã
- æå¾ã«ãã¯ãªãã¯ãããå®®ã®ç¶æ
ã
next_state
ã«æ´æ°ããã
ããã§ãå®®ã®é¸æã»éé¸æç¶æ ãåãæ¿ããã¢ãã¡ã¼ã·ã§ã³ãã§ããã
å®®ã®ãã¤ã©ã¤ã
次ã«ãå®®ã®ãã¤ã©ã¤ããè¡ãã¢ãã¡ã¼ã·ã§ã³ãå°å ¥ãããã
ãã¤ã©ã¤ããã¤ã¾ããè²å¤æ´èªä½ã¯æ¢ã«å¦ãã æè¡ã ã ããããä»åã¯ä¸åº¦ã«4ã¤ã®å®®ã®è²ã夿´ããªããã°ãªããªãã
ã¨ã¯ãããä½ãæ¼ç®ãç¨ããã°è©±ã¯ç°¡åã§ãã³ã¼ãã¯ä»¥ä¸ã®ããã«ããã°ããã
const originalColor = "rgb(234, 222, 239)"; const highlightColor = "rgb(224, 202, 233)"; [...] palace.addEventListener('mouseover', () => { AnimeChangeColor(palace, highlightColor) AnimeChangeColor(palace_list[(i + 4) % n], highlightColor) AnimeChangeColor(palace_list[(i + 6) % n], highlightColor) AnimeChangeColor(palace_list[(i + 8) % n], highlightColor) }); palace.addEventListener('mouseout', () => { AnimeChangeColor(palace, originalColor) AnimeChangeColor(palace_list[(i + 4) % n], originalColor) AnimeChangeColor(palace_list[(i + 6) % n], originalColor) AnimeChangeColor(palace_list[(i + 8) % n], originalColor) });
ãã¦ã¹ãªã¼ãã¼ããã¨ãã«å¯¾è±¡ã®å®®ããã¤ã©ã¤ãã«ã©ã¼ã«å¤æ´ãã ãã¦ã¹ã¢ã¦ãããã¨ãã«å¯¾è±¡ã®å®®ãå ã®è²ã«æ»ãã åç´ãªè©±ã ã è¤éãªã®ã¯ã䏿¹åæ£ã鏿ããããã« palace_list ã«å¯¾ãã¦ä½ãæ¼ç®ãç¨ãã¦ã¢ã¯ã»ã¹ãã¦ããé¨åãããã ãããã
å°ãé·ãããæçµç㪠script.ts ã®å 容ã¯ä»¥ä¸ã®ããã«ãªãã
import anime from "animejs" function AnimeChangeBorder(element: HTMLElement, width: string) { anime({ targets: element, borderWidth: width, duration: 200, easing: 'linear' }) } function AnimeChangeColor(element: HTMLElement, color: string) { anime({ targets: element, backgroundColor: color, duration: 200, easing: 'linear' }) } function main() { const originalColor = "rgb(234, 222, 239)"; const highlightColor = "rgb(224, 202, 233)"; const palace_list: HTMLElement[] = [] const palace_list_selected: boolean[] = [] const n = 12 for (let i = 0; i < n; i++) { const palace = document.getElementById(`palace-${i}`) if (palace == null) { throw new Error("palace not found."); } palace_list.push(palace) palace_list_selected.push(false) palace.addEventListener('click', () => { let next_state = !palace_list_selected[i] if (next_state) { AnimeChangeBorder(palace, "6px") } for (let j = 0; j < n; j++) { if (palace_list_selected[j]) { palace_list_selected[j] = false AnimeChangeBorder(palace_list[j], "2px") } } palace_list_selected[i] = next_state }); palace.addEventListener('mouseover', () => { AnimeChangeColor(palace, highlightColor) AnimeChangeColor(palace_list[(i + 4) % n], highlightColor) AnimeChangeColor(palace_list[(i + 6) % n], highlightColor) AnimeChangeColor(palace_list[(i + 8) % n], highlightColor) }); palace.addEventListener('mouseout', () => { AnimeChangeColor(palace, originalColor) AnimeChangeColor(palace_list[(i + 4) % n], originalColor) AnimeChangeColor(palace_list[(i + 6) % n], originalColor) AnimeChangeColor(palace_list[(i + 8) % n], originalColor) }); } } main()
ã¾ã¨ã
ããã§ã¯ã npm run dev
ã¾ã㯠npm run build
ãå®è¡ãããã
æçµçã«ã以ä¸ã®ãããªã¢ãã¡ã¼ã·ã§ã³ãåã WEB ãã¼ã¸ãã§ãããã£ãã¯ãã§ãã
(å³ã§ã¯ããåãå®®ãã¯ãªãã¯ããã¦é¸æããããå¯ãå®®ããã¦ã¹ãªã¼ãã¼ããã¦ããç¶æ
)ã
ããåããªãå ´åã¯ã Google Chrome çã§ TypeScript ãããã°ãã§ããããã«ã 以ä¸ã®è¨å®ã webpack.config.js ã«å ¥ããã¨ããã
module.exports = { // [ä»ã®è¨å®...] devtool: 'source-map', };
ããããã¨ã .map å½¢å¼ãã¡ã¤ã«ã使ãããã ããã¯ã webpack ã§çæããã js ãã¡ã¤ã«ã¨ã å ã ã® script.ts ãã¡ã¤ã«ã¨ã®éã§ã®ãããã³ã°ããã¦ããããã¡ã¤ã«ã§ã ãã©ã¦ã¶ãããã°ããéã« TypeScript ã³ã¼ãã§ãããã°ããããã¨ãã«å½¹ã«ç«ã¤ã
ãã¦ããã®å½ç¤ã®å®®ã«æã追å ãã¦ãããã訳ã ãã è¤éãªãã¨ãå§ããåã«ã次åã¯ãä»åè¡ã£ããã¨ã MVVM ã®ãã¿ã¼ã³ã«è½ã¨ãè¾¼ãã§ã ã¢ãã«ã³ã¼ãã¨ãã¥ã¼ã³ã¼ããæ··å¨ããªãããã«ãã¦ããããã¨æãã ã¤ãã§ã«ãã¹ããã§ããããã«ãªãã®ã§ããã¹ãç°å¢ãå°å ¥ãã¦ãããã