前端開發(fā)社區(qū)中,被抱怨最多的恐怕就是CSS,因為它真心不容易學(xué)。但它功能強(qiáng)大,越深入挖掘CSS的潛能,你就越有機(jī)會像魔法師一樣釋放各種神奇魔力。
無疑,學(xué)習(xí)CSS相關(guān)的基礎(chǔ)知識只是一方面,你還需要去學(xué)習(xí)CSS所具有創(chuàng)造力和可視化事物的能力,這就是本期我們規(guī)劃了推薦CSS這15個功能的原因。
跑題了?開篇先說HTML而非CSS?注意!行內(nèi)人常說,HTML就像 一個人的骨骼與器官,而CSS就是皮膚,有了這兩樣再去注入靈魂(JS),所以,我想先強(qiáng)調(diào)下骨骼和器官的重要性。
如果你先入手了HTML的所有基礎(chǔ)知識,自然CSS會變得更加容易。另外,通常我們要完成所需的樣式,就需要在方式上有選擇性的去構(gòu)造HTML;誠然,犧牲樣式的結(jié)構(gòu)和語義是走不通的,越好的CSS,理論上你的HTML結(jié)構(gòu)也應(yīng)是越好的,由此一個出色的Web開發(fā)人員應(yīng)該明白如何寫好UI,同時不會引入過多不必要的HTML,損害可訪問性或者讓內(nèi)容難以管理。
記得之前Flutter有個說法叫“萬物皆Widget”,翻譯得不錯!對于 CSS,
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model
盒子模型幾乎是學(xué)好CSS的一個"公開的秘密"——margin是你將box分開的距離(邊距),
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/margin
border是邊框,
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/border
padding是框內(nèi)內(nèi)容和邊框之間的間隔,這樣你的內(nèi)容可以是其他框或者文本。
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/padding
談?wù)摵心P徒?jīng)常遺漏的一件事是輪廓(outline),它是位于邊框和邊距之間的線條,也是可以樣式化的。
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/outline
這個副標(biāo)題差不多就定義了CSS,CSS又叫級聯(lián)樣式表。
編寫CSS時的頭號難題是某樣式被替換為第三方樣式或你自己的樣式。雖然現(xiàn)今已經(jīng)涌現(xiàn)出許多通過隔離樣式塊來解決類似問題的工具,但是最佳方法是理解和吃透問題本身,包括:瀏覽器以何種順序應(yīng)用樣式、如何去覆蓋第三方樣式、!important標(biāo)志相關(guān)問題等等。
努力遵循CSS流程以及它的工作方式,且你還需要找到一個適合CSS結(jié)構(gòu),努力堅持使用。
選擇器可幫助你定位HTML;組合器可幫助你組合和定位模式;
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors/Combinators
偽類可幫助你確定狀態(tài);
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
偽元素可幫助你確定或創(chuàng)建HTML的特定部分。
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
掌握這些對于成為CSS魔法師來說,至關(guān)重要。
變量絕對將CSS帶入一個新的高度。雖然CSS具備一些有用的函數(shù),
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions
但離不開變量,因為變量使CSS更加強(qiáng)大......
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
譬如,變量引入了共享和作用域值的思想。了解如何使用變量的方法能夠讓你的CSS風(fēng)格更上一層樓。
我在SCSS上使用變量,但是SCSS是預(yù)處理器,意味著一旦CSS到達(dá)瀏覽器,它便不復(fù)存在;而使用CSS變量,你可以利用JS對其進(jìn)行操作,例如在網(wǎng)站上切換暗黑和正常模式。
CSS'顯示和位置‘屬性可以幫助我們控制元素在內(nèi)容流內(nèi)部和外部的放置方式。任何CSS開發(fā)人員都應(yīng)該掌握這兩個屬性以及由它們派生出來的一些知識點(diǎn)。在CSS中,定位事物仍然是最大的挑戰(zhàn)之一,因此,你必須著重進(jìn)行學(xué)習(xí)。
響應(yīng)式設(shè)計不但能支持網(wǎng)頁適配于任何尺寸的屏幕,而且還能調(diào)整布局以適應(yīng)可用空間。
過往,通常我會建議身邊的人持續(xù)關(guān)注CSS媒體查詢,
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
但是,想一想,CSS的發(fā)展演進(jìn)很有些年頭了,意味著你如果想要打造響應(yīng)式的網(wǎng)頁,可完全擱置使用媒體查詢,改用網(wǎng)格布局(或flexbox)外加CSS clamp函數(shù)去控制字體。
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions
響應(yīng)式與流暢設(shè)計完美匹配超越了你對display:flex和網(wǎng)格布局的了解,
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design
沒有付出怎有收獲?利用響應(yīng)式設(shè)計的前提是,開發(fā)者需要了解諸如:設(shè)備和視口比例等問題、不同tags在可用空間限制下的表現(xiàn)、HTML的meta tags、既定視口的元素之間的合理距離、不同的設(shè)備尺寸等。所有這些都需要不斷地去學(xué)習(xí)和積累經(jīng)驗,大魔法師千萬不能小覷他們喲~
<img />標(biāo)簽和CSS背景兩者是并存的,你必須了解何時使用image標(biāo)簽和CSS背景。尤其是CSS背景功能強(qiáng)大,我們甚至可以使用它來創(chuàng)建文本下劃線和一些其他出色的背景效果。對我個人來說,它們是CSS中最有趣的部分之一,選入此15件事中,就是提醒你不要忽略它。
關(guān)于字體,開發(fā)者往往忽略了一些細(xì)節(jié),我感覺它們非常重要——你需要清楚如何為網(wǎng)站優(yōu)化字體,包括諸如:如何加載它們、有哪些選項、如何控制它們;實際上,很難找到完美的字體,但好的前端必須知道如何就地取材將它們提升到一個新的水平,從細(xì)節(jié)做起,以幫助到提升頁面的整體效果。
顏色無處不在。 你可以將其用在邊框、背景、輪廓等等。大多數(shù)開發(fā)者使用純hex顏色色值或顏色名稱,當(dāng)然也可以通過使用RGBA控制顏色的alpha透明度;基于alpha值,利用HSLA控制hue-rotate、飽和度和亮度,以便進(jìn)一步提升頁面色彩展示效果。
駕馭頁面色彩不僅僅是色值的事,CSS currentColor屬性也需要納入管理范疇——
相關(guān)鏈接:https://www.w3schools.com/colors/colors_currentcolor.asp
我們可以在hex顏色值中再添加2個其它值,以控制RGBA顏色的alpha值, 例如:#29910d82是個不透明的綠色,這其中的‘82’控制的是alpha值,類似使用RGBA時的最后一個值。
過渡效果簡單說就是一種控制CSS值從狀態(tài)A變?yōu)闋顟B(tài)B的方式,
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions
而動畫效果是一種使眾多的關(guān)鍵幀實現(xiàn)動態(tài)畫面的方式。
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations
動畫一旦使用得當(dāng),任何網(wǎng)頁都會美不勝收,而動畫和過渡效果相結(jié)合有利于增強(qiáng)用戶體驗,從而增強(qiáng)用戶與網(wǎng)頁之間的互動性。
另一種被廣泛用來增強(qiáng)網(wǎng)頁使用體驗的方式就是引入3D效果。
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/CSS/transform
Transform是那些你日常需要進(jìn)行動畫處理和過渡的內(nèi)容,自不言說,transform屬性與CSS動畫+過渡是緊密相關(guān)的,這是我推薦它的原因之一;而另一個原因是,近些年,海內(nèi)外運(yùn)用Transform屬性的網(wǎng)站越來越多,大概率你在未來工作中會用到,值得學(xué)習(xí)和關(guān)注。
CSS的預(yù)處理器功能強(qiáng)大,可令我們更得心應(yīng)手地利用上希望在CSS中注入的東西。它使我們能夠橋接上諸如mixins、函數(shù)和模塊化等概念,此外它還有助于導(dǎo)入和撰寫樣式。
相關(guān)鏈接:https://sass-lang.com/documentation/syntax
上面給你推薦的是SASS,除了SASS,還有其他預(yù)處理器,譬如LESS和Stylus,相對來說,它們僅基于一些語法上的更改,但都殊路同歸。
無論你會選擇哪種方法,在網(wǎng)頁樣式例程中引入預(yù)處理器都是讓你收獲頗豐,至少可以幫助你以更少的CSS來創(chuàng)建復(fù)雜的樣式。
CSS Houdini是一個低級的API,通過幫助我們訪問到CSS引擎,從而為你提供了一些所需功能去擴(kuò)展CSS。
相關(guān)鏈接:https://developer.mozilla.org/en-US/docs/Web/Houdini
CSS Houdini不存在等待瀏覽器的渲染周期的問題,因而解析起來要快得多。再有,它公開了一些被用來對CSS進(jìn)行模塊化的東西,這些稱之為worklets,worklets不需要模塊化CSS代碼的預(yù)處理器。 在這樣一個簡單的API中,應(yīng)該算是個非常高級的概念了,它讓離成為一個魔法師又邁出了一步。
隨著你構(gòu)建的應(yīng)用體量越來越大,編寫CSS時很容易出現(xiàn)沖突,這也是我為什么把CSS架構(gòu)放到最后單獨(dú)觸及的原因。
在你和你的團(tuán)隊達(dá)成意見一致的情況下,采用何種技術(shù)方法來幫助你們是當(dāng)務(wù)之急,下面是一些示例:
BEM :是由Yandex團(tuán)隊提出的一種CSS Class 命名方法
相關(guān)鏈接:http://getbem.com/introduction/
OOCSS:面向?qū)ο蟮腃SS指南
相關(guān)鏈接:http://oocss.org/
SMACSS :CSS的可擴(kuò)展模塊化體系結(jié)構(gòu)
相關(guān)鏈接:http://smacss.com/
上面這三個都是CSS結(jié)構(gòu)樣式指南,你可以按照指南更合理地去構(gòu)造選擇器和樣式,甚至還可以根據(jù)喜好提出自己的建議。
晉級CSS是一個連貫性的學(xué)習(xí)過程,需要大量的實踐。如果你無法做到堅持嘗試上述這15個方法/概念,同時上手各種項目去進(jìn)行實踐,那么你永遠(yuǎn)成為不了一位魔法師。
我建議你找些小型CSS項目練練手,我近期也會為你找些github上開源的項目練手,敬請期待。
前在相關(guān)的元旦活動中,就已透露后面還有更好的活動及獎勵,今天,終于是揭曉了,在元旦過后,拳頭及英雄聯(lián)盟,也是準(zhǔn)備迎接新的一年,以及鼓勵大家迎接新的一個賽季,即S12。
我想一些召喚師也察覺到,新的一年正式開始,新英雄的預(yù)告、新年皮膚、新賽季、新活動等各色內(nèi)容接踵而至,灰呆都覺得最近新聞太多不知道先寫哪個了,咱這就來看看這次的活動。
任務(wù)統(tǒng)計時間為:2022年1月7日0點(diǎn)—2022年2月7日23:59。
獎勵領(lǐng)取截止時間:2022年2月13日23:59。
目標(biāo)一:進(jìn)行1場峽谷排位賽
可獲得:3勝雙倍經(jīng)驗卡×1
?目標(biāo)二:贏得5場峽谷對局(匹配+排位)
可獲得:杰作寶箱+鑰匙
?目標(biāo)三:組隊完成1場游戲?qū)?/p>
可獲得:隨機(jī)1個表情
?目標(biāo)四:組隊完成3場游戲?qū)?/p>
可獲得:藍(lán)色精萃×500+橙色精萃×500
?目標(biāo)五:
1.累計12天,當(dāng)天有完成1場游戲?qū)?/p>
2.累計5天,當(dāng)天有登錄掌上英雄聯(lián)盟
可獲得:隨機(jī)1個皮膚
?
同樣的如果鏈接掛了的話就去掌盟看看吧~
在的app和pc網(wǎng)站做的越來越花哨,但是有時候用戶并不喜歡你給他挑選好的主題顏色,這個時候就需要一個換皮膚的功能了。
那么我們怎么在vue中實現(xiàn)這個換皮膚的功能呢?
項目結(jié)構(gòu)
<!DOCTYPE html> <html lang="zh-CN"> <head> <title>iView admin</title> <meta charset="UTF-8"> <!-- --> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"> <!-- 引入的css --> <link rel="stylesheet" href="/dist/main.css"> <!-- 注意這是我們換皮膚需要的css --> <link rel="stylesheet" name="theme" href=""> <!-- 圖標(biāo) --> <link rel="icon" href="./td_icon.ico" type="image/x-icon"/> </head> <body> <div id="app"></div> <!-- 用到的js --> <script type="text/javascript" src="/dist/vender-base.js"></script> <script type="text/javascript" src="/dist/vender-exten.js"></script> <script type="text/javascript" src="/dist/main.js"></script> </body> </html>
接下來就是具體實現(xiàn)換皮膚功能了,換皮膚一般都是點(diǎn)擊一個按鈕彈出一些皮膚的選項,選中選項后皮膚生效。
我們將換皮膚功能抽成一個組件theme-switch。pc端使用iview,手機(jī)端使用了vant。一共有3套皮膚用于切換。
目錄結(jié)構(gòu)
<template> <div style="display:inline-block;padding:0 6px;"> <Dropdown trigger="click" @on-click="setTheme"> <a href="javascript:void(0)"> <Icon :style="{marginTop: '-2px', verticalAlign: 'middle'}" color="#495060" :size="18" type="paintbucket"></Icon> <Icon type="arrow-down-b"></Icon> </a> <DropdownMenu slot="list"> <DropdownItem v-for="(item, index) in themeList" :key="index" :name="item.name"> <Row type="flex" justify="center" align="middle"> <span style="margin-right:10px;"><Icon :size="20" :type="item.name.substr(0, 1) !=='b' ? 'happy-outline' : 'happy'" :color="item.menu"/></span> <span><Icon :size="22" type="record" :color="item.element"/></span> </Row> </DropdownItem> </DropdownMenu> </Dropdown> </div> </template> <script> import Cookies from 'js-cookie'; import config from '../../../../build/config.js'; export default { name: 'themeSwitch', data () { return { themeList: [ { name: 'black_b', menu: '#495060', element: '#2d8cf0' }, { name: 'black_g', menu: '#495060', element: '#00a854' }, { name: 'black_y', menu: '#495060', element: '#e96500' } ] }; }, methods: { // 點(diǎn)擊切換事件 setTheme (themeFile) { let menuTheme=themeFile.substr(0, 1); let mainTheme=themeFile.substr(-1, 1); if (menuTheme==='b') { // 黑色菜單 this.$store.commit('changeMenuTheme', 'dark'); menuTheme='dark'; } else { this.$store.commit('changeMenuTheme', 'light'); menuTheme='light'; } let path=''; // 取到我們在html上給皮膚的css留的坑并且設(shè)置路徑 let themeLink=document.querySelector('link[name="theme"]'); let userName=Cookies.get('user'); if (localStorage.theme) { let themeList=JSON.parse(localStorage.theme); let index=0; let hasThisUser=themeList.some((item, i)=> { if (item.userName===userName) { index=i; return true; } else { return false; } }); if (hasThisUser) { themeList[index].mainTheme=mainTheme; themeList[index].menuTheme=menuTheme; } else { themeList.push({ userName: userName, mainTheme: mainTheme, menuTheme: menuTheme }); } localStorage.theme=JSON.stringify(themeList); } else { localStorage.theme=JSON.stringify([{ userName: userName, mainTheme: mainTheme, menuTheme: menuTheme }]); } let stylePath=''; if (config.env.indexOf('dev') > -1) { stylePath='./src/views/main-components/theme-switch/theme/'; } else { stylePath='dist/'; } if (mainTheme !=='b') { path=stylePath + mainTheme + '.css'; } else { path=''; } themeLink.setAttribute('href', path); } }, created () { let path=''; // 判斷運(yùn)行環(huán)境用于切換地址 if (config.env.indexOf('dev') > -1) { path='./src/views/main-components/theme-switch/theme/'; } else { path='dist/'; } let name=Cookies.get('user'); // 如果用戶之前選擇過皮膚則直接使用之前選擇的,否則使用默認(rèn)皮膚 if (localStorage.theme) { let hasThisUser=JSON.parse(localStorage.theme).some(item=> { if (item.userName===name) { this.$store.commit('changeMenuTheme', item.menuTheme); this.$store.commit('changeMainTheme', item.mainTheme); return true; } else { return false; } }); if (!hasThisUser) { this.$store.commit('changeMenuTheme', 'dark'); this.$store.commit('changeMainTheme', 'b'); } } else { this.$store.commit('changeMenuTheme', 'dark'); this.$store.commit('changeMainTheme', 'b'); } // 根據(jù)用戶設(shè)置主題 if (this.$store.state.app.themeColor !=='b') { let stylesheetPath=path + this.$store.state.app.themeColor + '.css'; // 取到我們在html上給皮膚的css留的坑并且設(shè)置路徑 let themeLink=document.querySelector('link[name="theme"]'); themeLink.setAttribute('href', stylesheetPath); } } }; </script>
<template> <div style="display:inline-block;padding:0 6px;"> <div @click="showBtn">換皮膚</div> <van-actionsheet v-model="show" :actions="themeList" @select="setTheme"/> </div> </template> <script> import Cookies from "js-cookie"; import { Actionsheet } from "vant"; // import config from "../../../../build/config.js"; export default { name: "themeSwitch", components: { [Actionsheet.name]: Actionsheet }, data() { return { show: false, themeList: [ { name: "黑色", data: "black_b" }, { name: "綠色", data: "black_g" }, { name: "黃色", data: "black_y" } ] }; }, methods: { showBtn() { this.show=true; }, setTheme(themeFile) { themeFile=themeFile.data; let menuTheme=themeFile.substr(0, 1); let mainTheme=themeFile.substr(-1, 1); if (menuTheme==="b") { // 黑色菜單 this.$store.commit("changeMenuTheme", "dark"); menuTheme="dark"; } else { this.$store.commit("changeMenuTheme", "light"); menuTheme="light"; } let path=""; let themeLink=document.querySelector('link[name="theme"]'); let userName=Cookies.get("user"); if (localStorage.theme) { let themeList=JSON.parse(localStorage.theme); let index=0; let hasThisUser=themeList.some((item, i)=> { if (item.userName===userName) { index=i; return true; } else { return false; } }); if (hasThisUser) { themeList[index].mainTheme=mainTheme; themeList[index].menuTheme=menuTheme; } else { themeList.push({ userName: userName, mainTheme: mainTheme, menuTheme: menuTheme }); } localStorage.theme=JSON.stringify(themeList); } else { localStorage.theme=JSON.stringify([ { userName: userName, mainTheme: mainTheme, menuTheme: menuTheme } ]); } let stylePath='css/'; // stylePath="./src/view/component/theme-switch/theme/"; // if (config.env.indexOf('dev') > -1) { // stylePath='src/view/component/theme-switch/theme'; // } else { // stylePath='dist/'; // } if (mainTheme !=="b") { path=stylePath + mainTheme + ".css"; } else { path=""; } themeLink.setAttribute("href", path); this.show=false; } }, created() { let path=""; path="css/"; // if (config.env.indexOf("dev") > -1) { // path="src/view/component/theme-switch/theme"; // } else { // path="dist/"; // } let name=Cookies.get("user"); if (localStorage.theme) { let hasThisUser=JSON.parse(localStorage.theme).some(item=> { if (item.userName===name) { this.$store.commit("changeMenuTheme", item.menuTheme); this.$store.commit("changeMainTheme", item.mainTheme); return true; } else { return false; } }); if (!hasThisUser) { this.$store.commit("changeMenuTheme", "dark"); this.$store.commit("changeMainTheme", "b"); } } else { this.$store.commit("changeMenuTheme", "dark"); this.$store.commit("changeMainTheme", "b"); } console.log(path); // 根據(jù)用戶設(shè)置主題 if (this.$store.state.app.themeColor !=="b") { let stylesheetPath=path + this.$store.state.app.themeColor + ".css"; let themeLink=document.querySelector('link[name="theme"]'); themeLink.setAttribute("href", stylesheetPath); } } }; </script>
在首頁引用該組件,初次渲染時進(jìn)入該組件的creat方法,如果用戶之前選擇過皮膚則直接使用之前選擇的,否則使用默認(rèn)皮膚。在store中加入相應(yīng)方法。
changeMenuTheme (state, theme) { state.menuTheme=theme; }, changeMainTheme (state, mainTheme) { state.themeColor=mainTheme; }
動態(tài)切換最關(guān)鍵的是這兩行代碼,其他的都是將皮膚狀態(tài)存起來方便下次使用:
let themeLink=document.querySelector('link[name="theme"]')
themeLink.setAttribute('href', stylesheetPath)
但是這個時候我們皮膚相關(guān)的css并沒有打到代碼中,需要我們額外進(jìn)行配置。
在webpack的配置文件中找到plugins,加入以下插件:
new CopyWebpackPlugin([ { from: 'td_icon.ico' }, { from: 'src/styles/fonts', to: 'fonts' }, { from: 'src/views/main-components/theme-switch/theme' } ],
new CopyWebpackPlugin([ { from: 'static', to: 'static' }, { from: 'src/view/component/theme-switch/theme', to: './css' } ])
之前我們可能已經(jīng)有了這個插件了,現(xiàn)在只是需要把皮膚相關(guān)的css額外配置一下。以上工作完成之后已經(jīng)可以動態(tài)的切換html中皮膚相關(guān)的css路徑了。接下來就需要我們在需要切換css的地方引用具體的class并且寫三套樣式分別放在theme中的css文件里。
注意在具體的vue文件中不需要引用theme中的css,因為html中已經(jīng)幫我們引用了
如果報各種與路徑有關(guān)的錯誤那就是你的路徑真的寫錯啦。好好對比一下組件中引用的路徑,webpack中配置的路徑和你的項目路徑吧~
當(dāng)然這只是換皮膚的一種實現(xiàn)思路,也就是動態(tài)切換html中的引用路徑。也希望大家集思廣益提供更多的解決思路~
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。