前端开发社区中,被抱怨最多的恐怕就是CSS,因为它真心不容易学。但它功能强大,越深入挖掘CSS的潜能,你就越有机会像魔法师一样释放各种神奇魔力。
无疑,学习CSS相关的基础知识只是一方面,你还需要去学习CSS所具有创造力和可视化事物的能力,这就是本期我们规划了推荐CSS这15个功能的原因。
跑题了?开篇先说HTML而非CSS?注意!行内人常说,HTML就像 一个人的骨骼与器官,而CSS就是皮肤,有了这两样再去注入灵魂(JS),所以,我想先强调下骨骼和器官的重要性。
如果你先入手了HTML的所有基础知识,自然CSS会变得更加容易。另外,通常我们要完成所需的样式,就需要在方式上有选择性的去构造HTML;诚然,牺牲样式的结构和语义是走不通的,越好的CSS,理论上你的HTML结构也应是越好的,由此一个出色的Web开发人员应该明白如何写好UI,同时不会引入过多不必要的HTML,损害可访问性或者让内容难以管理。
记得之前Flutter有个说法叫“万物皆Widget”,翻译得不错!对于 CSS,
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model
盒子模型几乎是学好CSS的一个"公开的秘密"——margin是你将box分开的距离(边距),
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/margin
border是边框,
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/border
padding是框内内容和边框之间的间隔,这样你的内容可以是其他框或者文本。
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/padding
谈论盒模型经常遗漏的一件事是轮廓(outline),它是位于边框和边距之间的线条,也是可以样式化的。
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/outline
这个副标题差不多就定义了CSS,CSS又叫级联样式表。
编写CSS时的头号难题是某样式被替换为第三方样式或你自己的样式。虽然现今已经涌现出许多通过隔离样式块来解决类似问题的工具,但是最佳方法是理解和吃透问题本身,包括:浏览器以何种顺序应用样式、如何去覆盖第三方样式、!important标志相关问题等等。
努力遵循CSS流程以及它的工作方式,且你还需要找到一个适合CSS结构,努力坚持使用。
选择器可帮助你定位HTML;组合器可帮助你组合和定位模式;
相关链接:https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors/Combinators
伪类可帮助你确定状态;
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
伪元素可帮助你确定或创建HTML的特定部分。
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements
掌握这些对于成为CSS魔法师来说,至关重要。
变量绝对将CSS带入一个新的高度。虽然CSS具备一些有用的函数,
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions
但离不开变量,因为变量使CSS更加强大......
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
譬如,变量引入了共享和作用域值的思想。了解如何使用变量的方法能够让你的CSS风格更上一层楼。
我在SCSS上使用变量,但是SCSS是预处理器,意味着一旦CSS到达浏览器,它便不复存在;而使用CSS变量,你可以利用JS对其进行操作,例如在网站上切换暗黑和正常模式。
CSS'显示和位置‘属性可以帮助我们控制元素在内容流内部和外部的放置方式。任何CSS开发人员都应该掌握这两个属性以及由它们派生出来的一些知识点。在CSS中,定位事物仍然是最大的挑战之一,因此,你必须着重进行学习。
响应式设计不但能支持网页适配于任何尺寸的屏幕,而且还能调整布局以适应可用空间。
过往,通常我会建议身边的人持续关注CSS媒体查询,
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
但是,想一想,CSS的发展演进很有些年头了,意味着你如果想要打造响应式的网页,可完全搁置使用媒体查询,改用网格布局(或flexbox)外加CSS clamp函数去控制字体。
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions
响应式与流畅设计完美匹配超越了你对display:flex和网格布局的了解,
相关链接:https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Responsive_Design
没有付出怎有收获?利用响应式设计的前提是,开发者需要了解诸如:设备和视口比例等问题、不同tags在可用空间限制下的表现、HTML的meta tags、既定视口的元素之间的合理距离、不同的设备尺寸等。所有这些都需要不断地去学习和积累经验,大魔法师千万不能小觑他们哟~
<img />标签和CSS背景两者是并存的,你必须了解何时使用image标签和CSS背景。尤其是CSS背景功能强大,我们甚至可以使用它来创建文本下划线和一些其他出色的背景效果。对我个人来说,它们是CSS中最有趣的部分之一,选入此15件事中,就是提醒你不要忽略它。
关于字体,开发者往往忽略了一些细节,我感觉它们非常重要——你需要清楚如何为网站优化字体,包括诸如:如何加载它们、有哪些选项、如何控制它们;实际上,很难找到完美的字体,但好的前端必须知道如何就地取材将它们提升到一个新的水平,从细节做起,以帮助到提升页面的整体效果。
颜色无处不在。 你可以将其用在边框、背景、轮廓等等。大多数开发者使用纯hex颜色色值或颜色名称,当然也可以通过使用RGBA控制颜色的alpha透明度;基于alpha值,利用HSLA控制hue-rotate、饱和度和亮度,以便进一步提升页面色彩展示效果。
驾驭页面色彩不仅仅是色值的事,CSS currentColor属性也需要纳入管理范畴——
相关链接:https://www.w3schools.com/colors/colors_currentcolor.asp
我们可以在hex颜色值中再添加2个其它值,以控制RGBA颜色的alpha值, 例如:#29910d82是个不透明的绿色,这其中的‘82’控制的是alpha值,类似使用RGBA时的最后一个值。
过渡效果简单说就是一种控制CSS值从状态A变为状态B的方式,
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions
而动画效果是一种使众多的关键帧实现动态画面的方式。
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations
动画一旦使用得当,任何网页都会美不胜收,而动画和过渡效果相结合有利于增强用户体验,从而增强用户与网页之间的互动性。
另一种被广泛用来增强网页使用体验的方式就是引入3D效果。
相关链接:https://developer.mozilla.org/en-US/docs/Web/CSS/transform
Transform是那些你日常需要进行动画处理和过渡的内容,自不言说,transform属性与CSS动画+过渡是紧密相关的,这是我推荐它的原因之一;而另一个原因是,近些年,海内外运用Transform属性的网站越来越多,大概率你在未来工作中会用到,值得学习和关注。
CSS的预处理器功能强大,可令我们更得心应手地利用上希望在CSS中注入的东西。它使我们能够桥接上诸如mixins、函数和模块化等概念,此外它还有助于导入和撰写样式。
相关链接:https://sass-lang.com/documentation/syntax
上面给你推荐的是SASS,除了SASS,还有其他预处理器,譬如LESS和Stylus,相对来说,它们仅基于一些语法上的更改,但都殊路同归。
无论你会选择哪种方法,在网页样式例程中引入预处理器都是让你收获颇丰,至少可以帮助你以更少的CSS来创建复杂的样式。
CSS Houdini是一个低级的API,通过帮助我们访问到CSS引擎,从而为你提供了一些所需功能去扩展CSS。
相关链接:https://developer.mozilla.org/en-US/docs/Web/Houdini
CSS Houdini不存在等待浏览器的渲染周期的问题,因而解析起来要快得多。再有,它公开了一些被用来对CSS进行模块化的东西,这些称之为worklets,worklets不需要模块化CSS代码的预处理器。 在这样一个简单的API中,应该算是个非常高级的概念了,它让离成为一个魔法师又迈出了一步。
随着你构建的应用体量越来越大,编写CSS时很容易出现冲突,这也是我为什么把CSS架构放到最后单独触及的原因。
在你和你的团队达成意见一致的情况下,采用何种技术方法来帮助你们是当务之急,下面是一些示例:
BEM :是由Yandex团队提出的一种CSS Class 命名方法
相关链接:http://getbem.com/introduction/
OOCSS:面向对象的CSS指南
相关链接:http://oocss.org/
SMACSS :CSS的可扩展模块化体系结构
相关链接:http://smacss.com/
上面这三个都是CSS结构样式指南,你可以按照指南更合理地去构造选择器和样式,甚至还可以根据喜好提出自己的建议。
晋级CSS是一个连贯性的学习过程,需要大量的实践。如果你无法做到坚持尝试上述这15个方法/概念,同时上手各种项目去进行实践,那么你永远成为不了一位魔法师。
我建议你找些小型CSS项目练练手,我近期也会为你找些github上开源的项目练手,敬请期待。
前在相关的元旦活动中,就已透露后面还有更好的活动及奖励,今天,终于是揭晓了,在元旦过后,拳头及英雄联盟,也是准备迎接新的一年,以及鼓励大家迎接新的一个赛季,即S12。
我想一些召唤师也察觉到,新的一年正式开始,新英雄的预告、新年皮肤、新赛季、新活动等各色内容接踵而至,灰呆都觉得最近新闻太多不知道先写哪个了,咱这就来看看这次的活动。
任务统计时间为:2022年1月7日0点—2022年2月7日23:59。
奖励领取截止时间:2022年2月13日23:59。
目标一:进行1场峡谷排位赛
可获得:3胜双倍经验卡×1
?目标二:赢得5场峡谷对局(匹配+排位)
可获得:杰作宝箱+钥匙
?目标三:组队完成1场游戏对局
可获得:随机1个表情
?目标四:组队完成3场游戏对局
可获得:蓝色精萃×500+橙色精萃×500
?目标五:
1.累计12天,当天有完成1场游戏对局
2.累计5天,当天有登录掌上英雄联盟
可获得:随机1个皮肤
?
同样的如果链接挂了的话就去掌盟看看吧~
在的app和pc网站做的越来越花哨,但是有时候用户并不喜欢你给他挑选好的主题颜色,这个时候就需要一个换皮肤的功能了。
那么我们怎么在vue中实现这个换皮肤的功能呢?
项目结构
<!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=""> <!-- 图标 --> <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>
接下来就是具体实现换皮肤功能了,换皮肤一般都是点击一个按钮弹出一些皮肤的选项,选中选项后皮肤生效。
我们将换皮肤功能抽成一个组件theme-switch。pc端使用iview,手机端使用了vant。一共有3套皮肤用于切换。
目录结构
<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: { // 点击切换事件 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留的坑并且设置路径 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=''; // 判断运行环境用于切换地址 if (config.env.indexOf('dev') > -1) { path='./src/views/main-components/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'); } // 根据用户设置主题 if (this.$store.state.app.themeColor !=='b') { let stylesheetPath=path + this.$store.state.app.themeColor + '.css'; // 取到我们在html上给皮肤的css留的坑并且设置路径 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); // 根据用户设置主题 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>
在首页引用该组件,初次渲染时进入该组件的creat方法,如果用户之前选择过皮肤则直接使用之前选择的,否则使用默认皮肤。在store中加入相应方法。
changeMenuTheme (state, theme) { state.menuTheme=theme; }, changeMainTheme (state, mainTheme) { state.themeColor=mainTheme; }
动态切换最关键的是这两行代码,其他的都是将皮肤状态存起来方便下次使用:
let themeLink=document.querySelector('link[name="theme"]')
themeLink.setAttribute('href', stylesheetPath)
但是这个时候我们皮肤相关的css并没有打到代码中,需要我们额外进行配置。
在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' } ])
之前我们可能已经有了这个插件了,现在只是需要把皮肤相关的css额外配置一下。以上工作完成之后已经可以动态的切换html中皮肤相关的css路径了。接下来就需要我们在需要切换css的地方引用具体的class并且写三套样式分别放在theme中的css文件里。
注意在具体的vue文件中不需要引用theme中的css,因为html中已经帮我们引用了
如果报各种与路径有关的错误那就是你的路径真的写错啦。好好对比一下组件中引用的路径,webpack中配置的路径和你的项目路径吧~
当然这只是换皮肤的一种实现思路,也就是动态切换html中的引用路径。也希望大家集思广益提供更多的解决思路~
*请认真填写需求信息,我们会在24小时内与您取得联系。