前言
本文主要根據UI
設計需求,講述一套基於scss
封裝方法的網頁響應式佈局,以及不同於傳統引入element UI
主題配色檔案的換膚思路。
需求分析
早期我們前端專案組開發了一個國外業務網站。這周爲了迎合其他國家的喜好,需要在國外業務專案的基礎上,新建多個專案,對之前的主題配色和部分佈局進行修改,並需要適配不同解析度下的螢幕。
UI
提供了包括主題配色和頁面佈局修改在內的一系列專案稿件,這些稿件基於1920px
解析度的螢幕進行處理。前端需要根據UI提供的主題色,修改專案中的顏色變數。介面暫時使用國外業務的那一套介面,後期需要對接這些專案的介面,而我們目前的主要任務就是處理這些專案的靜態頁面改版。主題色修改:
首先,我們前端團隊需要根據
UI
提供的主題色,更新專案中的顏色變數,確保頁面上的所有元素都符合新的配色方案。頁面佈局
提供的修改稿件,如果有不在主題色內的顏色,需要和UI
確認是否需要更換為其他顏色相近的主題配色或者雙方都新增主題配色檢查專案中包括
CSS
和HTML
在內的所有帶#
顏色值的資訊。與UI
確認後,將其更換為其他顏色接近的主題配色,或者雙方共同新增主題配色,以確保配色方案的一致性和協調性。響應式佈局:
前端需要根據
UI
提供的稿件和意見,適配專案在不同螢幕下的樣式。對於頁面上的不同元素,在小於等於1920px
的螢幕上進行縮放時,需要保持橫縱比,並根據頁面大小進行等比例縮放,包括元素的寬高、間距、以及內部元素在內的頁面佈局都需要與UI
確認;在高於1920px
螢幕的裝置上,需要保持和1920px
螢幕的佈局風格,即元素的寬高不變。然而,字型元素在頁面縮放時,需要保持一定的風格。比如:
16px
的文字最小不能低於14px
,18px
、20px
以及24px
的文字最小不能低於16px
,32px
的文字最小不能低於18px
,36px
的文字最小不能低於20px
,44px
的文字最小不能低於28px
,48px
的文字最小不能低於32px
。在移動裝置上,需要保持和800px網頁相同的佈局。
專案現狀
主題色: 早期在與UI團隊合作時,我們為國外業務系統確定了一套配色方案,並將其定義在專案的顏色變數中。然而,後續設計稿中出現了一些不在這套配色方案中的色值。 由於種種原因,我們在開發時沒有與UI確認這些顏色是否需要更換,也沒有將新增的顏色定義到顏色變數中,而是直接在程式碼中使用了這些顏色值。這導致在此次換膚過程中,僅透過修改顏色變數無法實現統一換膚的效果。我們需要逐一檢查程式碼中硬編碼的顏色值,並將其替換為新的顏色變數,以確保換膚的統一性和一致性。
佈局: 以前我們使用
flex
、百分比、最小最大寬度/高度以及element UI
的柵格佈局做了一些簡單的適配,但這些方法不夠靈活。爲了更好地適應不同解析度的螢幕,我們需要採用更為靈活和動態的佈局方案,以確保在各種裝置上的顯示效果都能達到預期。
思路分析
主題色
傳統的解決方案
以前在官網上,我們可以直接編輯並修改一套主題色。點選下載後,會生成一個
css
檔案。將下載後的
css
檔案引入到我們專案中,可以看到編譯後的css
檔案最後在專案中的入口檔案,引入我們下載的
css
檔案。
`main.js` import '@/styles/theme/index.css'
現在的解決方案
由於element UI
官方已不再維護傳統的主題配色下載,我們專案採取官方提供的第二種方式:
原理: 我們專案使用
scss
編寫css
,element UI
的theme-chalk
又恰好使用scss
進行編寫。在官方定義的scss
變數中,使用了!default
語法,用於提供預設值。這也就意味著,我們不用考慮css
的載入順序,直接新建scss
檔案,覆蓋定義在theme-chalk
檔案且在我們系統中常用的scss
變數,達到在css
編譯階段自定義主題scss
變數的效果。
引入變數: 新建
element-variable.scss
檔案,在這個檔案中引入theme-chalk
定義的主題scss
變數,同時需要改變icon
字型路徑變數(使用傳統方法不需要改變路徑變數,是因為我們直接引入了編譯後的css
檔案,裡面已經幫我們做過處理了;而使用現在的解決方案,如果不改變字型路徑變數,專案會提示找不到icon
字型路徑,所以這個配置必填)。此時,將這個檔案引入到我們的入口檔案,那麼系統中已經存在theme-chalk
定義好的scss
變數了
修改變數: 新建
element.scss
檔案,在裡面覆蓋我們需要修改的主題變數,最後在vue.config.js
中sass
配置下的additionalData
裡全域性引入到專案中的每個vue
檔案中(因為是掛載到每個vue
檔案中,所以這個配置下的scss檔案不宜過多),方便在vue
檔案中直接使用變數。
優勢
1. 定製化和靈活性
更改主題色和變數: 輕鬆地改變
Element UI
的主題色、字型、間距等變數,無需過多地覆蓋現有的CSS
樣式。精細控制: 精細控制每個元件的樣式,而必編寫額外的
CSS
來覆蓋預設樣式, 這使得樣式的定義更具備層次性和組織性。
2. 減少冗餘和提高效能
減少 CSS 體積: 透過自定義
SCSS
變數,編譯生成的CSS
檔案只包含我們需要的樣式,避免了在引入編譯後的CSS
檔案時載入不需要的樣式,從而減少了CSS
檔案的體積。提高效能: 較小的
CSS
檔案體積意味著更少的網路傳輸量和更快的頁面載入速度。
3. 避免樣式衝突
避免樣式覆蓋的衝突: 透過直接修改
SCSS
變數來定製樣式,可以避免在使用編譯後的 CSS 檔案時可能出現的樣式覆蓋衝突問題。這樣可以保證樣式的獨立性和一致性。
4. 便於維護
集中管理: 所有的樣式修改都集中在一個地方(變數檔案),這使得維護樣式變得更加方便和清晰。只需要修改檔案中定義的變數,就可以影響整個專案中的樣式,無需逐一查詢以及修改每個元件的樣式。
響應式佈局
思路分析
UI
提供的稿件是1920px
,前端需要對UI
提供的稿件進行一比一還原;網頁在小屏縮放時,需要保持元素的橫縱比。針對這個問題,我們可以用百分比作為佈局單位。 以設計稿寬度
1920px
為基準,建立px
和vw
之間的關係。如果把1920px
視為100vw
,那麼1vw = 19.2px
。 如果設計稿上某個元素的寬度為192px
, 那麼將它換算得到的結果將會是192px / 19.2px * 1vw = 10vw
。因此我們在佈局時,需要嚴格遵循UI
提供的設計稿件,並藉助下文封裝的方法,將設計稿元素的畫素作為第一個形參,傳遞到下文封裝的方法中;實現思路:為等比例縮放網頁元素,先去掉傳入的畫素單位。最後使用前文提到的換算公式,不論寬高,都將其轉換為
vw單位,等比縮放
。字型頁面元素在放大時,需要限制字型元素展現的最大閾值。 那麼我們封裝的方法,第二個形參需要控制字型元素的最大閾值;
實現思路:藉助
scss中的
max方法實現。
字型頁面元素在縮小時,需要限制字型元素展現的最小閾值。 那麼我們封裝的方法,第三個形參需要控制字型元素的最小閾值;
實現思路:藉助
scss中的
min方法實現。
在高於
1920px
螢幕的裝置上,需要保持和1920px
螢幕的佈局風格,即元素的寬高不變。 針對這個問題,我們只需要保證方法中的max
形參和1920px
下的畫素值一致,即保證方法中的第一個形參和第二個形參相同。在移動裝置上,需要使用
800px
的網頁佈局。針對這個問題,我們可以使用meta
標籤進行適配:<meta name="viewport" content="width=800,user-scalable=yes"> <meta name="viewport" content="width=800,user-scalable=yes">
不同螢幕下的元素顯示勢必不會那麼完美。我們可以透過媒體查詢,在不同解析度的螢幕下,按照
UI
給定的反饋意見,對網頁進行適配,這樣就可以解決問題。但是在專案中大量使用媒體查詢語法,會導致整個專案看上去很亂。為此,我們可以基於scss
語法,對媒體查詢語法進行二次封裝。
自適應scss
方法封裝
// 自定義scss函式, 作用是去掉傳入變數的單位 // 之所以要去掉單位,是爲了將傳入的px轉換為vw單位,自適應佈局` @function stripUnits($value) { // 對帶有單位的變數進行特殊處理,返回去掉單位後的結果` // 對於scss來說, 90px和90都是number` // 在scss中,unitless是一個術語,指的是沒有單位的數值,not unitless就是變數帶單位` @if type-of($value) == 'number' and not unitless($value) { // 90px / 1 得到的結果是90px, 90px / 1px得到的結果是90 // 這也是這裏為什麼要用($value * 0 + 1),而不是直接寫1的原因` @return $value / ($value * 0 + 1); } @return $value; } /* 自定義scss函式,提供三個引數: 第一個引數是設計稿提供的元素大小,傳入會自動轉換為vw單位,達到自適應的效果 第二個引數是用來約束這個元素的大小最大不能超過第一個引數和第二個引數的最大值, 必須帶單位 第三個引數是用來約束這個元素的大小最小不能小於第一個引數和第三個引數的最小值,必須帶單位 如果不傳入第二個和第三個引數,則表示元素完全隨螢幕響應式縮放 應用場景: 1. 1920px下標題的字型是48px,當螢幕解析度為960px時,標題字號縮放為24px,起不到突出的作用。 於是我們可以給它設定一個最小閾值,比如最小不能小於32px; 2. 同理,當螢幕解析度為3840px時,標題字號放大為96px,我們不希望字號這麼大。 於是可以給它設定一個最大閾值,比如最大不能超過60px。 */ @function auto($raw, $max:null, $min:null) { $raw: stripUnits($raw); $str: #{$raw / $proportion}vw; @if $max { $str: min(#{$str}, #{$max}); } @if $min { $str: max(#{$str}, #{$min}); } @return $str; } /* 自定義scss函式,auto方法的二次封裝, 提供兩個引數 第一個引數用於設定1920px下的元素大小 第二個引數用於設定這個元素的最小值 應用場景: 1. 1920px下標題的字型是48px,當螢幕解析度為3840px時,標題字號放大為96px,我們希望它保持48px大小, 於是我們可以給它設定一個最大閾值48px。同時,我們可以傳入一個最小閾值,讓它最小不能小於這個引數。 */ @function autoMax($raw, $min:null) { @return auto($raw, $raw, $min) } // 和上面相反 @function autoMin($raw, $max:null) { @return auto($raw, $max, $raw) } //1vw = 1920 / 100 ; $proportion: 19.2; // 根據UI需求,對不同字型大小進行封裝 $wb-font-size-mini: 16px; // $text-mini-1 $wb-font-size-extra-small: 18px; // $text-small-1 $wb-font-size-small: 20px; //$text-sm-md-1 $wb-font-size-base: 24px; //$text-medium-1 $wb-font-size-lesser-medium: 32px; $wb-font-size-medium: 36px; //$text-large-1 $wb-font-size-extra-medium: 44px; $wb-font-size-large: 48px; //$text-title-1 // 根據UI需求,在螢幕解析度縮小時,字型響應式變化,並設定最小閾值 // 並在1920px以上的螢幕,保持和1920px一樣的字型大小 $wb-auto-font-size-mini: autoMax($wb-font-size-mini, 14px); $wb-auto-font-size-extra-small: autoMax($wb-font-size-extra-small, 16px); $wb-auto-font-size-small: autoMax($wb-font-size-small, 16px); $wb-auto-font-size-base: autoMax($wb-font-size-base, 16px); $wb-auto-font-size-lesser-medium: autoMax($wb-font-size-lesser-medium, 18px); $wb-auto-font-size-medium: autoMax($wb-font-size-medium, 20px); $wb-auto-font-size-extra-medium: autoMax($wb-font-size-extra-medium, 28px); $wb-auto-font-size-large: autoMax($wb-font-size-large, 32px);
// 嚴格按照UI稿件提供的元素大小、間距編寫程式碼,以下是示例程式碼 .title { padding: 0 autoMax(180px); font-size: $wb-auto-font-size-large; font-weight: 600; text-align: center; }
媒體查詢語法封裝及使用規範
// 匯入scss的list和map模組,用於處理相關操作。 @use 'sass:list'; @use "sass:map"; /* 媒體查詢對映表,定義各種裝置型別的媒體查詢範圍 key為定義的媒體型別,value為對應的解析度範圍 */ $media-list: ( mobile-begin: (0, null), mobile: (0, 800), mobile-end:(null, 800), tablet-begin: (801, null), tablet: (801, 1023), tablet-end:(null, 1023), mini-desktop-begin: (1024, null), mini-desktop: (1024, 1279), mini-desktop-end: (null, 1279), small-desktop-begin: (1280, null), small-desktop: (1280, 1439), small-desktop-end: (null, 1439), medium-desktop-begin: (1440, 1919), medium-desktop: (1440, 1919), medium-desktop-end: (null, 1919), large-desktop-begin: (1920, null), large-desktop: (1920, 2559), large-desktop-end: (null, 2559), super-desktop-begin: (2560, null), super-desktop: (2560, null), super-desktop-end: (2560, null) ); /* 建立響應式媒體查詢的函式,傳參是媒體查詢對映表中的媒體型別 從$media-list中獲取對應的最小和最大寬度,並返回相應的媒體查詢字串。 */ @function createResponsive($media) { $size-list: map.get($media-list, $media); $min-size: list.nth($size-list, 1); $max-size: list.nth($size-list, 2); @if ($min-size and $max-size) { @return "screen and (min-width:#{$min-size}px) and (max-width: #{$max-size}px)"; } @else if ($max-size) { @return "screen and (max-width: #{$max-size}px)"; } @else { @return "screen and (min-width:#{$min-size}px)"; } } /* 這個混入接受一個或多個媒體型別引數,呼叫createResponsive函式生成媒體查詢 @content是Scss中的一個佔位符,用於在混入中定義塊級內容。 它允許你在呼叫混入時,將實際的樣式程式碼插入到混入定義的樣式規則中。 */ @mixin responsive-to($media...) { @each $item in $media { $media-content: createResponsive($item); @media #{$media-content} { @content; } } } // 以下是針對各種媒體型別定義的混入: @mixin mobile() { @include responsive-to(mobile) { @content; } } @mixin tablet() { @include responsive-to(tablet) { @content; } } @mixin mini-desktop() { @include responsive-to(mini-desktop) { @content; } } @mixin small-desktop() { @include responsive-to(small-desktop) { @content; } } @mixin medium-desktop() { @include responsive-to(medium-desktop) { @content; } } @mixin large-desktop() { @include responsive-to(large-desktop) { @content; } } @mixin super-desktop() { @include responsive-to(super-desktop) { @content; } } @mixin mobile-begin() { @include responsive-to(mobile-begin) { @content; } } @mixin tablet-begin() { @include responsive-to(tablet-begin) { @content; } } @mixin mini-desktop-begin() { @include responsive-to(mini-desktop-begin) { @content; } } @mixin small-desktop-begin() { @include responsive-to(small-desktop-begin) { @content; } } @mixin medium-desktop-begin() { @include responsive-to(medium-desktop-begin) { @content; } } @mixin large-desktop-begin() { @include responsive-to(large-desktop-begin) { @content; } } @mixin super-desktop-begin() { @include responsive-to(super-desktop-begin) { @content; } } @mixin mobile-end() { @include responsive-to(mobile-end) { @content; } } @mixin tablet-end() { @include responsive-to(tablet-end) { @content; } } @mixin mini-desktop-end() { @include responsive-to(mini-desktop-end) { @content; } } @mixin small-desktop-end() { @include responsive-to(small-desktop-end) { @content; } } @mixin medium-desktop-end() { @include responsive-to(medium-desktop-end) { @content; } } @mixin large-desktop-end() { @include responsive-to(large-desktop-end) { @content; } } @mixin super-desktop-end() { @include responsive-to(super-desktop-begin) { @content; } }
需求解決思路:
根據提供的設計稿,使用
autoMax
系列方法,對頁面做初步的響應式佈局適配針對不同螢幕下部分元素佈局需要調整的問題,使用封裝的媒體查詢方法進行處理
書寫規範:
為避免專案中的scss
檔案過多,搞得整個專案看上去很臃腫,現提供一套書寫規範:
在每個路由下的主
index.vue
檔案中,引入同級資料夾scss
下的media.scss
檔案
<style scoped> // dosomething for css </style> // 小屏狀態下,覆蓋前面定義的css樣式 <style src="./scss/media.scss" scoped> </style>
media.css
檔案,以vue
檔案最外層的類進行包裹,以螢幕解析度大小作為排序依據,從大到小書寫媒體查詢樣式,使用deep
穿透
.about-wrapper { @include small-desktop { ::v-deep { } } @include mini-desktop { ::v-deep { } } @include tablet-end { ::v-deep { } } }