原本只是一個小小的假畫面專案,不知哪天心血來潮,就開始了充滿挑戰的 Windows XP 之旅~
1. 從純 JS 轉至 React
面對眼前的 XP 魔王,我下定決心將整個專案以 React 重寫 ,但後來分離至另外一個 repo 了。
2. 元素拖曳、伸縮
一開始決定從 XP 視窗的拖曳以及伸縮著手,經歷 getBoundingClientRect 的 left、top 以及 mousemove 的( clientX, offsetX, pageX, screenX)各種折磨, 才讓我真正了解其中的區別,並實作不受 scroll 影響的元素拖曳和伸縮。
光是這點就能寫一篇啦!
3. 抖動的 Cursor
在元素伸縮時,加入 cursor: resize; 系列是不可或缺的,然而單純的 :hover 會在快速伸縮時因為超過元素範圍讓 cursor 變回 default,想了很久以後決定蓋一個全版 div,讓 cursor 怎麼滑都保持 resize!
4. 無限延伸子目錄
最後的寫法是遍歷一個 JSON 檔,產生相應的目錄結構,較麻煩的是為了符合 XP 的行為,需要自己控制選單反白的狀態。
5. Grid 欄位對齊
每個下拉選單都有四欄,中間的兩欄(選項名稱和快捷鍵)長度不固定,且內容要對齊該欄左側,首先我想到以 grid 解決:
display: grid;
grid-template-columns: 16px auto auto 15px;
但後來發現 hover 時需要反藍整列,就用了 display: contents 這個屬性,作用是包住內容(一整列)但不影響 layout(欄的對齊),如此一來就能輕鬆設定整列樣式了。
6. 踩地雷
為了完全模擬踩地雷的行為,將 mouse event 的 button 和 buttons 屬性組合了一下才完成,複雜的狀態則使用 useReducer 來管理。
MouseEvent.button 代表觸發事件的按鍵(0 左鍵 2 右鍵)
MouseEvent.buttons 代表觸發事件時按鍵的狀態(1 左鍵 2 右鍵 3 雙鍵)
7. 圖片反藍
為了讓圖片變藍真是無所不用其極,一開始用了這種方式:
.img {
filter: hue-rotate(170deg) brightness(60%) saturate(300%);
}
網站中使用了許多好用的 filter 屬性,只有這個比較詭異,色相旋轉、亮度還有飽和度的組合還真的讓圖片變藍了。(大家可以試試看
最後反藍的的方式是給 parent 藍色影子、讓圖案透明,達到變藍的效果:
.container {
filter: drop-shadow(0 0 blue);
}
.img {
opacity: 0.5;
}
8. ReactDom.createPortal
打開關機選單時,為了讓全版畫面變成灰階但保持目錄的顏色,使用了 portal,讓選單 render 時可以插入特定的 DOM 位置又保持事件溝通,時常用在突破 overflow: hidden; 限制。
9. 其他
製作過程中也發現一些小眉角和大家分享:
- 多個 box shadow 會由前到後覆蓋,如下圖影子會是黑色
.dot {
box-shadow: 0 0 black, 0 0 white, 0 0 blue;
}
- 選單使用 filter: invert(100%) 轉負片,看到的藍色其實原本是橘色
.container {
background: #e99f17; -> 橘色
filter: invert(100%);
}
- 用 user select: none、pointer event: none 防止拖曳事件受到影響
後記
完成後真是成就感爆棚,過程中為了完全符合 XP 視覺,不斷微調 layout 和顏色,也花了很多時間在圖示上,其實有許多次「到此為止吧」的念頭。不過也釐清了許多自己在 layout 的錯誤觀念,且只要發現跟原生 XP 不同的地方,不改完就渾身不對勁,為此還特地裝了 XP 虛擬機呢 XD。
感謝您讀到這兒,如果你喜歡這篇文章或是我的作品,請不吝幫我拍拍手或是給我一顆 Star,若內容有任何問題非常歡迎一起討論或協助修正。
Credits:
https://bitsofco.de/how-display-contents-works/
http://www.colorzilla.com/gradient-editor/
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons