于 CSS 阴影,之前已经有写过一篇,box-shadow 与 filter:drop-shadow 详解及奇技淫巧,介绍了一些关于 box-shadow 的用法。
最近一个新的项目,CSS-Inspiration,挖掘了其他很多有关 CSS 阴影的点子,是之前的文章没有覆盖到的新内容,而且有一些很有意思,遂打算再起一篇。
本文的题目是 CSS 阴影技巧与细节。CSS 阴影,却不一定是 box-shadow 与 filter:drop-shadow,为啥?因为使用其他属性也可以模拟阴影,而且是各种各样的阴影。下面且听我娓娓道来~
单侧投影
先说单侧投影,关于 box-shadow,大部分时候,我们使用它都是用来生成一个两侧的投影,或者一个四侧的投影。如下:
OK,那如果要生成一个单侧的投影呢?
我们来看看 box-shadow 的用法定义:
{ box-shadow: none | [inset? && [ <offset-x> <offset-y> <blur-radius>? <spread-radius>? <color>? ] ]#}
以 box-shadow: 1px 2px 3px 4px #333 为例,4 个数值的含义分别是,x 方向偏移值、y 方向偏移值 、模糊半径、扩张半径。
这里有一个小技巧,扩张半径可以为负值。
继续,如果阴影的模糊半径,与负的扩张半径一致,那么我们将看不到任何阴影,因为生成的阴影将被包含在原来的元素之下,除非给它设定一个方向的偏移量。所以这个时候,我们给定一个方向的偏移值,即可实现单侧投影:
CodePen Demo — css单侧投影(https://codepen.io/Chokcoco/pen/pergRb)
投影背景 / 背景动画
接着上面的说。
很明显,0 = -0,所以当 box-shadow 的模糊半径和扩张半径都为 0 的时候,我们也可以得到一个和元素大小一样的阴影,只不过被元素本身遮挡住了,我们尝试将其偏移出来。
CSS代码如下:
div { width: 80px; height: 80px; border: 1px solid #333; box-sizing: border-box; box-shadow: 80px 80px 0 0 #000;}
得到如下结果:
有什么用呢?好像没什么意义啊。
额,确实好像没什么用。不过我们注意到,box-shadow 是可以设置多层的,也就是多层阴影,而且可以进行过渡变换动画(补间动画)。但是 background-image: linear-gradient(),也就是渐变背景是不能进行补间动画的。
这又扯到哪里去了。好我们回来,利用上面的特性,我们可以利用 box-shadow 实现原本只能利用渐变才能实现的背景图:
用 box-shadow,实现它的 CSS 代码如下(可以更简化):
.shadow { position: relative; width: 250px; height: 250px;}.shadow::before { content: ""; position: absolute; width: 50px; height: 50px; top: -50px; left: -50px; box-shadow: 50px 50px #000, 150px 50px #000, 250px 50px #000, 50px 100px #000, 150px 100px #000, 250px 100px #000, 50px 150px #000, 150px 150px #000, 250px 150px #000, 50px 200px #000, 150px 200px #000, 250px 200px #000, 50px 250px #000, 150px 250px #000, 250px 250px #000;}
用渐变来实现的话,只需要这样:
.gradient { width: 250px; height: 250px; background-image: linear-gradient(90deg, #000 0%, #000 50%, #fff 50%, #fff 100%); background-size: 100px 100px;}
为什么选择更为复杂的 box-shadow 呢?因为它可以进行补间动画,像这样,这是使用渐变做不到的:
CodePen Demo — box-shadow实现背景动画(https://codepen.io/Chokcoco/pen/WaBYZL)
当然,这只是个示例 Demo,运用点想象力还有很多有意思的效果,再贴一个:
CodePen Demo — CSS Checker Illusion( By David Khourshid )(https://codepen.io/davidkpiano/pen/LVzxPV)
嗯,很有意思,就是实际用途可能不大。
立体投影
好,我们继续。下一个主题是立体投影。
这个说法很奇怪,阴影的出现,本就是为了让原本的元素看起来更加的立体,那这里所谓的立体投影,是个怎么立体法?
这里所谓的立体投影,并不一定是使用了 box-shadow、text-shadow 或者 drop-shadow,而是我们使用其他元素或者属性模拟元素的阴影。而这样做的目的,是为了能够突破 box-shadow 这类元素的一些定位局限。让阴影的位置、大小、模糊度可以更加的灵活。
OK,让我们来看看,这样一个元素,我们希望通过自定义阴影的位置,让它更加立体:
上图 div 只是带了一个非常浅的 bos-shadow ,看上去和立体没什么关系,接下来,我们通过 div 的伪元素,给它生成一个和原图边角形状类似的图形,再通过 transform 位移一下,可能是这样
OK,最后对这个用伪元素生成的元素进行一些虚化效果(filter或者box-shadow都可以),就可以实现一个边角看起来像被撕开的立体效果:
代码非常简单,伪 CSS 代码示意如下:
div { position: relative; width: 600px; height: 100px; background: hsl(48, 100%, 50%); border-radius: 20px;}div::before { content: ""; position: absolute; top: 50%; left: 5%; right: 5%; bottom: 0; border-radius: 10px; background: hsl(48, 100%, 20%); transform: translate(0, -15%) rotate(-4deg); transform-origin: center center; box-shadow: 0 0 20px 15px hsl(48, 100%, 20%);}
所以总结一下:
还有其他很多场景:
CodePen Demo — 立体投影(https://codepen.io/Chokcoco/pen/LgdRKE?editors=1100)
文字立体投影 / 文字长阴影
上面的立体效果在文字上就完全不适用了,所以对待文字的立体阴影效果,还需要另辟蹊径。
正常而言,我们使用 text-shadow 来生成文字阴影,像这样:
<div> Txt Shadow</div>-----div { text-shadow: 6px 6px 3px hsla(14, 100%, 30%, 1);}
嗯,挺好的,就是不够立体。那么要做到立体文字阴影,最常见的方法就是使用多层文字阴影叠加。
Tips:和 box-shadow 一样,text-shadow 是可以叠加多层的!但是对于单个元素而言, drop-shadow的话就只能是一层。
好,上面的文字,我们试着叠加个 50 层文字阴影试一下。额,50 层手写,其实很快的~
好吧,手写真的太慢了,还容易出错,所以这里我们需要借助一下 SASS/LESS 帮忙,写一个生成 50 层阴影的 function 就好,我们每向右和向下偏移 1px,生成一层 text-shadow:
@function makeLongShadow($color) { $val: 0px 0px $color; @for $i from 1 through 50 { $val: #{$val}, #{$i}px #{$i}px #{$color}; } @return $val;} div { text-shadow: makeLongShadow(hsl(14, 100%, 30%));}
上面的 SCSS 代码。经过编译后,就会生成如下 CSS:
div { text-shadow: 0px 0px #992400, 1px 1px #992400, 2px 2px #992400, 3px 3px #992400, 4px 4px#992400, 5px 5px #992400, 6px 6px #992400, 7px 7px #992400, 8px 8px #992400, 9px 9px #992400, 10px10px #992400, 11px 11px #992400, 12px 12px #992400, 13px 13px #992400, 14px 14px #992400, 15px15px #992400, 16px 16px #992400, 17px 17px #992400, 18px 18px #992400, 19px 19px #992400, 20px20px #992400, 21px 21px #992400, 22px 22px #992400, 23px 23px #992400, 24px 24px #992400, 25px25px #992400, 26px 26px #992400, 27px 27px #992400, 28px 28px #992400, 29px 29px #992400, 30px30px #992400, 31px 31px #992400, 32px 32px #992400, 33px 33px #992400, 34px 34px #992400, 35px35px #992400, 36px 36px #992400, 37px 37px #992400, 38px 38px #992400, 39px 39px #992400, 40px40px #992400, 41px 41px #992400, 42px 42px #992400, 43px 43px #992400, 44px 44px #992400, 45px45px #992400, 46px 46px #992400, 47px 47px #992400, 48px 48px #992400, 49px 49px #992400, 50px50px #992400;}
看看效果:
额,很不错,很立体。但是,就是丑,而且说不上来的奇怪。
问题出在哪里呢,阴影其实是存在明暗度和透明度的变化的,所以,对于渐进的每一层文字阴影,明暗度和透明度应该都是不断变化的。这个需求,SASS 可以很好的实现,下面是两个 SASS 颜色函数:
关于 SASS 颜色函数,可以看看这里:Sass基础—颜色函数
我们使用上面两个 SASS 颜色函数修改一下我们的 CSS 代码,主要是修改上面的 makeLongShadow function 函数:
@function makelongrightshadow($color) { $val: 0px 0px $color; @for $i from 1 through 50 { $color: fade-out(desaturate($color, 1%), .02); $val: #{$val}, #{$i}px #{$i}px #{$color}; } @return $val;}
好,看看最终效果:
嗯,大功告成,这次顺眼了很多~
CodePen Demo — 立体文字阴影(https://codepen.io/Chokcoco/pen/JmgNNa)
当然,使用 CSS 生成立体文字阴影的方法还有很多,下面再贴出一例,使用了透明色叠加底色的多重线性渐变实现的文字立体阴影,感兴趣的同学可以去看看具体实现:
线性渐变配合阴影实现条纹立体阴影条纹字(https://codepen.io/Chokcoco/pen/XxQJEB?editors=1100)
长投影
上面提到了通过多层阴影叠加实现文字的立体阴影。运用在 div 这些容器上也是可以的。当然这里还有一种挺有意思的方法。假设我们,有一个矩形元素,希望给他添加一个长投影,像下面这样:
要生成这种长投影,刚刚说的叠加多层阴影可以,再就是借助元素的两个伪元素,其实上面的图是这样的:
关键点在于,我们通过对两个伪元素的 transform: skew() 变换以及从实色到透明色的背景色变化,实现了长投影的效果:
CodePen Demo — 线性渐变模拟长阴影(https://codepen.io/Chokcoco/pen/qJvVGy)
彩色投影
通常而言,我们生成阴影的方式大多是 box-shadow 、filter: drop-shadow() 、text-shadow 。但是,使用它们生成的阴影通常只能是单色或者同色系的。
你这么说,难道还可以生成渐变色的阴影不成?
额,当然不行。
这个真不行,但是通过巧妙的利用 filter: blur 模糊滤镜,我们可以假装生成渐变色或者说是颜色丰富的阴影效果。
当然,关于 CSS 阴影还有很多有意思的技巧和细节,本文限于篇幅不再一一罗列。
写响应式布局时,发现border所占据空间的问题让人很头疼。下面举例说说遇到的问题和解决方法!
上例中的盒子box1和box2,因为宽度和刚好等于父元素盒子宽度,可以完美的排在同一行。
但我们给box2加一个边框,就会发现,box2的宽度增加了,box3被挤到了下面一行去。虽然我们只是添加了1px的边框,但它对布局的影响确实很大。在我们需要使用伪类显示边框时,突然出现的边框常常会对我们的布局造成影响,严重拉低体验。我尝试了两个方法消除边框影响。一是outline代替border来展示边框
以上的代码,box2四周也会出现边框,并且不会布局。但也会看到,box2部分边的边框线会被遮住而看不到,所以这种方法有局限性,不是很推荐,用在四周没有紧贴的其他元素的情况下比较好。第二种方法是给box2设置属性box-sizing:border-box;
可以看到,这次既显示了边框线,有没有影响我们的布局,除了降低了内容区大小,这个方法接近完美。box-sizing是css3的属性,它有三个值,content-box,border-box,inherit。content-box,宽度和高度分别应用到元素的内容框,在宽度和高度之外绘制元素的内边距和边框。这也是box-sizing的默认值。border-box,为元素设定的宽度和高度决定了元素的边框盒,就是说,为元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制,通过从已设定的宽度和高度分别减去边框和内边距才能得到内容的宽度和高度。inherit,规定应从父元素继承 box-sizing 属性的值。
文:https://blog.csdn.net/TriDiamond6/article/details/105222289
CSS是一个很独特的语言。看起来非常简单,但是某种特殊效果看似简单,实现起来就颇有难度。这篇文章主要是给在学习前端的童鞋分享一些新的CSS技巧,一些在前端教程和培训课堂中不会讲到的知识。第二就是让还在前端开发这条道路上的童鞋们,重新燃起对前端排版和特效的热爱和热情!
1、固定底部内容
这种是一个非常常见的布局方式,但是对于新手来说是比较常见的难题。
这种布局方式在后台管理系统中比较常见,当我们内容不足浏览器窗口高度时,底部内容需要固定在底部。当内容超出了浏览器窗口高度,就会随着内容往后推。
在有CSS3之前,实现这个效果是颇有难度的。浏览器窗口高度是会根据不同用户打开浏览器的情况,屏幕大小的差异和浏览器的缩放比例而变。我们需要借助JavaScript来实时获取浏览器高度进行运算才能实现。虽然说标题是说“固定”底部,但是我们想要的效果不是position: fixed。使用固定定位,在内容高于窗口高度时,就会挡住我们的内容。
随着CSS3的来临,最完美的实现方式是使用Flexbox。实现的关键就是使用不太被关注的flex-grow属性,可以在我们的内容标签元素(比如div)中使用。在我们下面的例子里使用了main标签。
我来讲解一下实现原理吧。
flew-grow是用来控制一个flex元素相对它同等级flex元素的自身可扩充的空间。如果我们使用flex-grow: 0,那这个flex元素就完全不会扩展了。所以我们需要把头部和底部之间的内容标签元素设置为flex-grow: 1或者flex-grow: auto,这样内容部分就会自动填充满头部和底部之外的所有空间。
为了避免底部内容受内容部分扩充空间的影响,我们给footer底部元素flex-shrink: 0属性。flex-shrink的作用与flex-grow是恰恰相反,用来控制flex元素收缩的空间,这里我们给了flex-shrink: 0就是为了底部footer的大小不受影响。
我们直接上HTML和CSS代码看看是怎么实现的。
<div id="document">
<nav>
<h1>头部内容</h1>
</nav>
<main>
<p>可以添加更多内容看看底部的变化哦!</p>
</main>
<footer>
<h1>底部内容</h1>
</footer>
</div>
CSS
#document {
height: 100vh;
display: flex;
flex-direction: column;
background: #202020;
font-family: microsoft yahei,wenquanyi micro hei,sans-serif !important;
}
nav, footer {
background: #494949;
display: flex;
justify-content: center;
}
main {
color: #bdbdbd;
flex: auto;
}
footer {
flex-shrink: 0;
}
* {
margin: 0;
}
h1,p {
padding: 15px;
}
nav > h1 {
color: #82FCFD;
text-shadow: 1px 1px 4px #00000080;
}
footer > h1 {
color: #82FCFD;
text-shadow: 1px 1px 4px #00000080;
}
知识点总结:
2. 悬停放大图片特效
悬停放大图片是一个特别吸引眼球的特效,比较常用于可点击的图片。当用户悬停鼠标在图片上,图片会稍微的放大。
其实实现这个特效是非常简单的。首先我们需要一个div包裹这img标签,这个包裹层是用来遮挡住图片,当图片放大时不会出现图片超出我们规定的宽高以外。
首先我们来讲讲div包裹的属性,我们需要给它一个固定的width宽和height高。然后我们必须给予这个元素overflow: hidden属性。让图片放大的时候不会超出这个div元素的宽高。有了这个包裹层,我们就可以编写img的各种效果了。
我的例子里面用了transform: scale(1,1)作为悬停时的图片特效,这个transform是用于改变任何元素的属性的,然后scale是用于放大(整数就会放大)或者缩小(负数就会缩小)元素的。
上代码让大家看看:
html body中放入
<div class="img-wrapper">
<img src="https://img-blog.csdnimg.cn/2020032122230564.png"/>
</div>
CSS
.img-wrapper {
width: 400px;
height: 400px;
overflow: hidden;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}
.img-wrapper img {
height: 400px;
-webkit-transition: 0.3s linear;
transition: 0.3s linear;
}
.img-wrapper img:hover {
transform: scale(1.1);
}
.img-wrapper {
display: inline-block;
box-sizing: border-box;
border: 3px solid #000;
}
如果你们想让图片更加炫酷可以加上图片过滤属性filter,让图片变灰或者变深褐色,然后悬停时候出现更加炫酷的颜色变幻。灰化的属性是filter: grayscale(100%);,然后深褐色化的属性是filter: sepia(100%)。其实图片还有很多过滤属性的,大家有兴趣也可以去尝试一下哦!
加入特殊效果的代码如下:
HTML
<!-- 灰度过滤 -->
<div class="img-wrapper">
<img class="grayscale-img" src="https://img-blog.csdnimg.cn/2020032211021728.png" />
</div>
<!-- 深褐色过滤 -->
<div class="img-wrapper">
<img class="sepia-img" src="https://img-blog.csdnimg.cn/2020032122230564.png" />
</div>
CSS
/* ============== * 灰度过滤 * ==============*/
.grayscale-img {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
}
.grayscale-img:hover {
-webkit-filter: grayscale(0);
filter: grayscale(0);
}
/* ============== * 深褐色过滤* ==============*/
.sepia-img {
-webkit-filter: sepia(100%);
filter: sepia(100%);
}
.sepia-img:hover {
-webkit-filter: sepia(0);
filter: sepia(0);
}
3. 瞬间黑暗模式
最近微信也逃脱不了黑暗时代的到来,网页也很多都做了黑暗模式的兼容和主题。如果我们在做的一个网站想瞬间实现黑暗模式可以怎么实现呢?
其实有一个很快的方式,我们可以使用invert和hue-rotate两个CSS3过滤器来实现。
filter: invert() — 是从0到1的刻度,1是从白变黑。
filter: hue-rotate() — 用于改变你元素的颜色,同时或多或少保持原本相同的色系。这个属性的值可以从0deg到360deg。
在我们页面的body标签上添加这两个属性,我们就可以快速尝试把我们的网站变成"黑暗模式"。这里需要注意的是,如果body和html上没有设置background背景颜色,这个过滤就会不起效了哦。
CSS的代码如下:
html {
background: #fff;
}
body {
background: #fff; filter: invert(1) hue-rotate(270deg);
}
实现效果
这里我们会发现图片的颜色会受影响,并不是很美观,使用css过滤器是无法完美切换黑暗模式的。不过使用JavaScript辅助就可以完美的切换黑暗模式。
最近出了一个JavaScript辅助插件叫Darkmode.js。
Darkmode.js
其实Darkmode.js运用的也是css里面的一个特性叫mix-blend-mode — “CSS 属性描述了元素的内容应该与元素的直系父元素的内容和元素的背景如何混合“。加上Javascript的辅助判断哪些页面上的元素需要黑化的,哪些是不需要黑化的。就会想我们之前那种做法,导致其他不需要黑化的元素,比如图片,受到影响导致颜色出现问题。
使用Darkmode.js非常简单,只要在脚本里面添加以下代码就可以马上加入一个插件,
<script src="https://cdn.jsdelivr.net/npm/darkmode-js@1.5.5/lib/darkmode-js.min.js">
</script>
<script>
// 这些是这个插件的可配置项:
var options = {
bottom: "32px", // 定位底部距离 - 默认: '32px'
right: "32px", // 定位右边距离 - 默认: '32px'
left: "unset", // 定位左边距离 - 默认: 'unset'
time: "0.5s", // 默认动画时间: '0.3s'
mixColor: "#fff", // 需要改变的颜色 - 默认: '#fff'
backgroundColor: "#fff", // 背景颜色 - 默认: '#fff'
buttonColorDark: "#262728", // 黑暗模式下按钮颜色 - 默认: '#100f2c'
buttonColorLight: "#fff", // 日间模式下按钮颜色 - 默认: '#fff'
saveInCookies: true, // 是否在cookie保存当前模式 - 默认: true,
label: "", // 切换模式按钮图标 - 默认: ''
autoMatchOsTheme: true // 是否自动根据系统适应模式 - 默认: true
};
let darkmode = new Darkmode(options);
darkmode.showWidget();
</script>
如果你不希望用这个插件的默认按钮,你可以在你的JavaScript代码中自主控制。我们可以通过.toggle()方法来切换模式,同时可以使用.isActivated()来检测是否已经进入黑暗模式。
const darkmode = new Darkmode();darkmode.toggle();
console.log(darkmode.isActivated())
// 如果已经进入黑暗模式会返回 true
知识总结
4. 自定义列表符号
ul,li的无序列表有默认的符号·,但是在很多情况下我们希望可以给这个符号加入自己的样式和颜色,甚至是换成自定义的符号。默认的符号我们是无法做任何的样式处理,而且默认的符号在CSS属性里面只有几个选择可以使用,很多情况下都是无法满足我们的设计。
其实自定义无序列表符号不难,我们只需要使用伪元素::before加content属性就可以实现。
在我这个例子里面我做了两个任务列表,一个是待处理任务,一个是已完成任务,各自给了不一样的列表符号和颜色。
实现原理
一、首先我们禁用了ul的默认符号样式list-style: none
二、在li的:before伪元素上给予content内容值,待处理任务使用,已完成任务li.completed:before使用✔
三、为了展示效果更加好看我分别给了li和li .completed两个不同的颜色
上代码看看是怎么实现的吧:
HTML
<div>
<h2>待处理</h2>
<ul>
<li>待办任务1</li>
<li>待办任务2</li>
<li>待办任务3</li>
<li>待办任务4</li>
<li>待办任务5</li>
</ul>
</div>
<div>
<h2>已完成</h2>
<ul>
<li class="completed">完成任务1</li>
<li class="completed">完成任务2</li>
<li class="completed">完成任务3</li>
</ul>
</div>
CSS
ul {
list-style: none; color: #fff;
font-size: 20px;
border: 3px solid #000;
padding: 1rem 2rem;
min-height: 200px;
margin: 15px 2rem 0 0;
background: #323232;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
border-radius: 8px;
}
li {
padding: 0.3rem 0;
}
li::before {
content: " ";
color: aqua;
}
li.completed::before {
content: "✔ ";
text-decoration: none;
color: greenyellow;
}
li.completed {
text-decoration: line-through;
color: #bdbdbd;
}
::before和::after伪元素加content属性可以用来做很多特殊的效果,也是当代前端排版比较常用的“魔法”。说到伪元素的使用,我再给大家说一个比较常用的使用场景。
在管理后台或者是文章展示中,我们经常可以见到的“面包屑导航”也是用伪元素来插入每个目录中间的符号的。
实现逻辑
一、这个导航含有3个a标签,首先给每个a标签加入一个伪元素::after,然后在content属性插入/符号。
二、然后使用a:first-child,这个伪类会选择到第一个a标签,然后使用content属性加入»符号。
三、因为我们第一步在每个a标签的后面插入了/符号, 所以我们需要在最后一个a标签清除掉。这里我们使用:last-child选择到最后一个a标签,然后用content: " "属性把伪元素的内容清楚掉。
HTML
<div class="breadcrumb">
<a>三钻</a>
<a>前端</a>
<a>教程</a>
</div>
CSS
.breadcrumb {
font-size: 1.6rem;
color: #fff;
}
.breadcrumb a:first-child {
color: #82fcfd;
}
.breadcrumb a:first-child::before {
content: " » ";
}
.breadcrumb a::after {
content: " /";
color: #ef6eae;
}
.breadcrumb a:last-child::after {
content: "";
}
知识总结
::before | ::after — 伪元素用于向某些选择器添加特殊的效果。
content — CSS 属性用于在元素的 ::before 和 ::after 伪元素中插入内容。使用content 属性插入的内容都是匿名的可替换元素。
:first-child — CSS伪类表示在一组兄弟元素中的第一个元素。
:last-child — CSS伪类代表父元素的最后一个子元素。
*请认真填写需求信息,我们会在24小时内与您取得联系。