css揭秘笔记——视觉效果
投影
知识点
box-shadow: <offset-x> <offset-y> <blur-radius> <spread-radius> <color> [inset]?
注意:
在元素正下方的投影被裁切掉了,是没有的;而text-shadow不同,文字下方的投影不会被裁切。
box-shadow的第三个参数是模糊半径,假如设置4px,则投影尺寸比元素本身大8px.
box-shadow第四个参数是扩张半径,指定整数是扩大,负数为缩小。
单侧投影
box-shadow: 0 10px 8px -8px black;
邻边投影
box-shadow: 6px 6px 12px -6px black;
双侧投影
box-shadow无法单独指定水平或垂直方向上的放大或缩小,所以只能用两层投影达到目的。
box-shadow: 6px 0 12px -6px black, -6px 0 12px -6px black;
不规则投影
box-shadow
给矩形或用border-radius生成的形状加投影很完美,但给除此之外的形状加投影,就不太行了。比如:
border:10px dashed orange; box-shadow: 2px 2px 3px rgba(0,0,0,.5);
可以使用css新属性滤镜filter
,使用一些函数,如blur()、grayscale()、drop-shadow()等等,就可以达到想要的效果。drop-shadow()
的参数除了没有扩张半径和关键字inset,其他和box-shadow一样,但它不支持逗号分隔的多层投影语法。
border:10px dashed orange; filter: drop-shadow(2px 2px 3px rgba(0,0,0,.5));
background: linear-gradient(45deg, transparent 20px, orange 0) left bottom /50%, linear-gradient(-45deg, transparent 20px, orange 0) right bottom /50%, linear-gradient(135deg, transparent 20px, orange 0) left top /50%, linear-gradient(-135deg, transparent 20px, orange 0) right top /50%; background-repeat: no-repeat; filter: drop-shadow(2px 2px 5px rgba(0,0,0,.5));
.talkbox{ background-color: orange; border-radius: 20px; position: relative; filter: drop-shadow(2px 2px 3px rgba(0,0,0,.5)); } .talkbox::after{ content: ''; display: block; box-sizing: border-box; width: 50px; height: 50px; border-left: 25px solid orange; border-top: 25px solid transparent; border-bottom: 25px solid transparent; position: absolute; top: 25%; left: 100%; }
需要注意的是,任何非透明的部分都会被drop-shadow()打上投影。(这并不是一种很好的体验)
color: orange; border: 10px solid; text-shadow: 2px 2px yellowgreen; filter: drop-shadow(2px 2px 2px black);
染色效果
使用filter
给图片添加滤镜效果。主要用到了sepia()
、saturate()
、hue-rotate()
这些函数。
原图
sepia() 降饱和度的橙黄色染色效果
saturate() 提升饱和度
hue-rotate() 把每个像素的色相进行偏移,偏到粉色
还可以加过度动画,最终的代码是
.filter-img{ transition: .5s filter; filter: sepia(.5) saturate(4) hue-rotate(295deg); } .filter-img:hover, .filter-img:focus{ filter: none; }
混合模式的方案
为了避免上面那种滤镜方式的褪色和不自然的效果,还可以使用“混合模式”,这种方式控制了上层元素的颜色和下层颜色进行混合的方式。实现染色效果的混合模式是luminosity
,它会保留上层元素的hsl高亮信息,并从它的下层吸取色相和饱和度信息。
具体还可以分为两种方式。
方式一:
<div style="background: hsl(335, 100%, 50%);"> <img style="mix-blend-mode: luminosity;" src="./image/flower.jpg" alt=""> </div>
方式二:
<div class="tinted-img"></div> .tinted-img{ background: url(./image/flower.jpg); width: 300px; height: 300px; background-size: cover; background-color: hsl(335, 100%, 50%); background-blend-mode: luminosity; transition: .5s background-color; } .tinted-img:hover{ background-color: transparent; }
这里需要说明一下,混合模式不像滤镜一样可动画。使用background-blend-mode
属性可以让每层背景跟它的下层背景进行混合。当我们只有一个背景图像及一个透明背景色时,就不会有任何混合效果。所以动画可以在这个原理上进行设置。
毛玻璃效果
场景:背景是花哨的图片,在文字下面使用半透明颜色保证文本的可读性。
<div class="wrapper"> <div class="main"> <p> Lorem ipsum dolor sit amet...</p> </div> </div>
失败案例一:
.wrapper{ background: url(./image/flower.jpg) 0 / cover; } .main{ background: hsla(0, 0%, 100%, .3); }
字体不清晰,调高透明度的值:
不太美观。。。
失败案例二:
使用blur()
滤镜。
.main{ filter: blur(2px); }
(⊙o⊙)… 连字体都模糊了。
解决方案:
按照之前提到过的“平行四边形”的方案,为伪元素设置模糊滤镜,而不会影响文本。
.wrapper{ width: 600px; height:300px; background: url(./image/flower.jpg) 0 / cover; display: flex; justify-content: center; align-items: center; } .main{ width: 70%; height: 65%; padding: 1em; font-size: 1.2em; background: hsla(0, 0%, 100%, .3); position: relative; z-index: 1; /*定位元素z-index不为auto时,会创建层叠上下文*/ overflow: hidden; } .main::before{ content: ''; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background: url(./image/flower.jpg) 0 / cover; filter: blur(20px); z-index: -1;/*负的z-index会下降到其所在的层级上下文的背景之上,在这里也就是.main元素的背景之上,文本以下。*/ margin:-30px; /*由于边缘的模糊效果会按模糊半径的长度削减,所以要将模糊元素扩展出去,同时父元素要对延伸出去的部分hidden*/ }
效果出来了,但是需要说明一点,就是层叠顺序的问题,如果直接在伪元素设置z-index
为0的话,它会直接跑到最外层背景颜色下方,所以这个层叠顺序很复杂,可以参考下文:
深入理解CSS中的层叠上下文和层叠顺序
看了文章发现,很多属性都可以创建层叠上下文,如display:flex
、filter
、mix-blend-mode
等等。
折角效果
45°折角
background: #58a; /*回退样式*/ background: linear-gradient(-135deg, transparent 50%, rgba(0,0,0,.4) 0) no-repeat 100% 0 / 2em 2em, linear-gradient(-135deg, transparent 1.5em, #58a 0);
注意,1.5em是通过三角形计算得到,√2em取整为1.5。
其他角度折角
每个角度都需要复杂的计算,这里以30°为例。非45°折角和45°折角不同的是,反折过来的角是不一样的。所以实现思路就不同了。使用伪元素模拟折角,计算得到的长宽和背景切角相反;以右下角为中心逆时针旋转30°;再向上移动一段距离(要调动初中几何的功底了),就可以了;最后加点装饰更真实。
.note{ background: #58a; background:linear-gradient(-150deg, transparent 1.5em, #58a 0); position: relative; /*装饰*/ border-radius: .5em; } .note::before{ content: ""; width: 1.73em; height: 3em; position: absolute; top: 0; right: 0; background: linear-gradient(to left bottom, transparent 50%, rgba(0,0,0,.2) 0, rgba(0,0,0,.4)); transform-origin: bottom right; transform: translateY(-1.3em) rotate(-30deg); /*注意:先平移再旋转和先旋转再平移效果是不一样的!*/ /*装饰*/ border-bottom-left-radius: inherit; box-shadow: -.2em .2em .3em -.1em rgba(0,0,0,.15); }
代码不够DRY,只能使用预处理器的mixin了。