CSS基础---了解CSS2.1以后的知识(4)

87 阅读53分钟

十一、高级的视觉表现

(1)CSS滤镜filter

==> 滤镜函数 <==

滤镜函数作用
blur(5px)模糊
brightness(2.4)亮度
contrast(200%)对比度
drop-shadow(4px 4px 8px blue)投影
grayscale(50%)灰度
hue-rotate(90deg)色调旋转
invert(75%)反相
opacity(25%)透明度
saturate(230%)饱和度
sepia(60%)褐色

<== 模糊滤镜 blur() ==>

说明: 可以让元素或者图像产生高斯模糊的效果,函数参数支持长度值,但是不支持百分比值,参数值表示屏幕上互相融合的像素数量,因此,参数值越大,其模糊程度越明显

img {
    width: 200px;
}

.img_5px {
    filter: blur(5px);
}

.img_10px {
    filter: blur(10px);
}
<p>模糊5px</p>
<img src="D:1.jpg" class="img_5px" />

<p>模糊10px</p>
<img src="D:1.jpg" class="img_10px" />

image.png

注意: 由于图像的边缘区域的像素点数量不足,因此,图像边缘的模糊效果是半透明的,如果背景是黑色的,那么就会看见边缘被染成黑色,为了解决这一现象,可以适当放大图片

<body style="background: black">
    <div class="box">
        <img src="D:1.jpg" width="256" height="192" />
    </div>
</body>
.box {
    width: 256px;
    height: 192px;
    overflow: hidden;
}

.box img {
    filter: blur(5px);
}
.box {
    width: 256px;
    height: 192px;
    overflow: hidden;
}

.box img {
    /* 适当放大图片,让模糊的边缘隐藏掉 */
    transform: scale(1.1);
    filter: blur(5px);
}

image.png

image.png

<== 亮度滤镜 brightness() ==>

说明: 可以用来调节元素的亮度,它的参数值支持数值和百分比值,范围是0 - ∞;0或者0%都是表示纯黑色,1或者100%都是表示正常亮度,0 - 1或者0% - 100%的亮度是线性变化的,如果逐渐大于1或者100,则亮度会越来越亮,也就是逐渐变成白色;如果不传参数值,则使用默认值1

举例: 所以这个在黑白两色之间切换很有用,比如某个图标在白色按钮中是黑色,而在黑色按钮中是白色

.settings-icon {
    position: relative;
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 2px solid #333;
    border-radius: 50%;
}

.settings-icon::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 6px;
    height: 6px;
    border-top: 2px solid #333;
    border-right: 2px solid #333;
    transform-origin: 50% 50%;
}
<body style="background: black">
    <div class="settings-icon"></div>
</body>

image.png

image.png

<== 对比度滤镜 contrast() ==>

说明: 可以用来调节元素的对比度,函数的参数值支持数值和百分比值,范围是0到无穷大。参数值0或0%表示毫无对比度,表现为纯灰色,色值是#808080,使用RGB色值表示为rgb(128,128,128),也就是gray颜色关键字对应的色值;参数值1或100%表示正常的对比度。随着参数值逐渐大于1,元素的对比度也会逐渐提升;参数可以不传,此时使用默认值1

注意: 这里说的是纯灰色,图像会直接变成一个灰色色块,而不是图像灰度

<p>对比度为0</p>
<img src="D:1.jpg" class="img_contrast_0" />

<p>对比度为50%</p>
<img src="D:1.jpg" class="img_contrast_50" />
img {
    width: 100px;
}

.img_contrast_0 {
    filter: contrast(0);
}

.img_contrast_50 {
    filter: contrast(50%);
}

image.png

<== 投影滤镜 drop-shadow() ==>

说明: 可以给元素设置符合真实世界阴影规则的投影效果,从而不支持扩展、内投影效果以及投影叠加,但是它能在凡是透明镂空的地方,就会留下相应的阴影轮廓,这是常规的盒阴影做不到的地方,比如一张图片,盒阴影只能在两个方向产生阴影,而投影滤镜函数则能够在图片的四周都产生阴影,只不过是颜色深浅的问题

语法:

filter: drop-shadow(x偏移, y偏移, 模糊大小, 色值);

举例:

img {
    width: 100px;
}

.box-shadow {
    box-shadow: 5px 5px 8px;
}

.drop-shadow {
    filter: drop-shadow(5px 5px 8px);
}
<p>box-shadow</p>
<img src="D:1.jpg" class="box-shadow" />

<p>drop-shadow</p>
<img src="D:1.jpg" class="drop-shadow" />

image.png

<== 灰度滤镜 grayscale() ==>

说明: 可以实现元素的去色效果,让所有彩色值变成灰度值;函数的参数值支持数值和百分比值,范围是0到无穷大。参数值为1或100%的时候表现为完全灰度;参数值大于1或100%的时候也表现为完全灰度,因此,通常没有必要设置大于1的数值或百分比值;0或0%表示正常的图像表现。在0~1或0%~100%范围区间的灰度是线性变化的;也可以选择不传参数,此时使用默认值0

举例: 比较经典的应用是在特殊的节日让网页变灰

image.png

<== 色调旋转滤镜 hue-rotate() ==>

说明: 可以调整元素的色调,但饱和度和亮度保持不变;函数的参数值支持角度值,例如90deg或0.5turn等,角度值的范围没有限制,每360度就是一个循环;由于函数不会改变任意灰度色值,因此,可以利用该函数非常方便地复制出包含众多色彩的小组件,如按钮元素

image.png

注意: 由于所有filter属性支持的滤镜函数都支持animation动画效果,因此使用hue-rotate()函数可以轻松实现元素的色彩无限变化效果,这个动画可以用在任何元素上,尤其是那些色彩丰富的元素,这样可以得到非常精彩的色彩流动效果

.flow-slogan {
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    font-size: 100px;
    background-image: linear-gradient(
        to right,
        red,
        yellow,
        lime,
        aqua,
        blue,
        fuchsia
    );
    animation: hue 6s linear infinite;
}

@keyframes hue {
    from {
        filter: hue-rotate(0deg);
    }
    to {
        filter: hue-rotate(360deg);
    }
}

<== 反相滤镜 invert() ==>

说明: 可以让元素的亮度和色调同时反转;函数的参数值支持数值和百分比值,范围是0到无穷大。参数值为1或100%的时候图像表现为完全反相。单纯从语法上来说,值可以大于1或者大于100%,但是效果不会再进一步变化。0或0%表示正常的图像表现

img {
    width: 100px;
}

.img_invert_0 {
    filter: invert(0);
}

.img_invert_75 {
    filter: invert(75%);
}
<p>filter: invert(0)</p>
<img src="D:1.jpg" class="img_invert_0" />

<p>filter: invert(75%)</p>
<img src="D:1.jpg" class="img_invert_75" />

image.png

<== 透明度滤镜 opacity() ==>

说明: 可以改变元素的透明度,效果和opacity属性类似;函数的参数值支持数值和百分比值,范围是0到无穷大。参数值为0或0%的时候图像表现为完全透明;参数值为1或100%或者更大的值时,图像均是正常表现

注意: 有些浏览器的opacity()函数可以启用硬件加速,使其性能更好;此外,opacity()函数和opacity属性的作用效果会叠加

img {
    width: 100px;
}

.img_opacity_0 {
    filter: opacity(0);
}

.img_opacity_75 {
    filter: opacity(75%);
}
<p>filter: opacity(0);</p>
<img src="D:1.jpg" class="img_opacity_0" />

<p>filter: opacity(75%);</p>
<img src="D:1.jpg" class="img_opacity_75" />

image.png

<== 饱和度滤镜 saturate() ==>

说明: 可以调整元素的饱和度;函数的参数值支持数值和百分比值,范围是0到无 穷大。参数值0或0%表示毫无饱和度,表现为灰度效果,等同于grayscale(1);参数值1或100%表示正常的饱和度;随着参数值逐渐大于1,元素的饱和度也会逐渐提升;函数参数也可以不传,则使用默认值1

img {
    width: 100px;
}

.img_saturate_0 {
    filter: saturate(0);
}

.img_saturate_75 {
    filter: saturate(75%);
}
<p>filter: saturate(0);</p>
<img src="D:1.jpg" class="img_saturate_0" />

<p>filter: saturate(75%);</p>
<img src="D:1.jpg" class="img_saturate_75" />

image.png

<== 褐色滤镜 sepia() ==>

说明: 可以让元素的视觉效果向褐色靠拢;函数的参数值支持数值和百分比值,范围是0到无穷大。当参数值为1或100%,或者大于1或100%时,图像均表现为深褐色;当参数值为0或0%时,图像还是原始效果;函数的参数值可以为空,此时等同于使用参数值0,这个函数一般用来实现老照片效果

img {
    width: 100px;
}

.img_sepia_0 {
    filter: sepia(0);
}

.img_sepia_75 {
    filter: sepia(75%);
}
<p>filter: sepia(0);</p>
<img src="D:1.jpg" class="img_sepia_0" />

<p>filter: sepia(75%);</p>
<img src="D:1.jpg" class="img_sepia_75" />

image.png

==> 滤镜函数的叠加 <==

说明: 也就是上面的10个函数可以随意叠加产生不一样的效果,比如使用模糊和对比度函数就能够实现元素融合的效果

.container {
    filter: blur(10px) contrast(5);
}

.box {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    position: absolute;
    left: calc(50% - 50px);
    top: calc(50% - 50px);
}

.box-1 {
    background-color: deepskyblue;
    animation: fly-r 5s infinite alternate;
}

.box-2 {
    width: 60px;
    height: 60px;
    margin-top: 20px;
    background-color: white;
    animation: fly-l 5s infinite alternate;
}

@keyframes fly-r {
    from {
        transform: translateX(0);
    }
    to {
        transform: translateX(120px);
    }
}

@keyframes fly-l {
    from {
        transform: translateX(120px);
    }
    to {
        transform: translateX(0);
    }
}

<body style="background: black">
    <div class="container">
        <div class="box box-1"></div>
        <div class="box box-2"></div>
    </div>
</body>

image.png

==> SVG滤镜 <==

<== 融合粘滞效果 ==>

说明: 这里与上面的融合效果区别在于这里的融合不会导致内部的文字消失,但是上面哪一种就不一定了

<!-- 主要是这段svg起作用 -->
<svg width="0" height="0" style="position: absolute">
    <defs>
        <filter id="goo">
            <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
            <feColorMatrix
                in="blur"
                mode="matrix"
                values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9"
                result="goo"
            />
            <feComposite in="SourceGraphic" in2="goo" operator="atop" />
        </filter>
    </defs>
</svg>

<input type="checkbox" id="share" />
<div class="target">
    <label class="share" for="share">分享</label>
    <span class="icon-share-weibo">
        <img src="data:image/png;base64,..."/>
    </span>
    <span class="icon-share-wechat">
        <img src="data:image/png;base64,..."/>
    </span>
    <span class="icon-share-qq">
        <img src="data:image/png;base64,..." />
    </span>
</div>
.target {
    height: 120px;
    max-width: 200px;
    filter: url("#goo");
    text-align: center;
    margin: auto;
    position: relative;
}

.share {
    display: block;
    width: 64px;
    line-height: 64px;
    background-color: #cd0000;
    color: #fff;
    border-radius: 50%;
    margin: auto;
    position: relative;
    z-index: 1;
}

[type="checkbox"] {
    position: absolute;
    clip: rect(0 0 0 0);
}

[class*="icon-share-"] {
    position: absolute;
    width: 48px;
    height: 48px;
    background-color: #cd0000;
    border-radius: 50%;
    transition: transform 0.5s;
    top: 8px;
    left: 0;
    right: 0;
    margin: auto;
    -ms-transform: scale(0.5);
    transform: scale(0.5);
}

[class*="icon-share-"] > img {
    display: block;
    width: 20px;
    height: 20px;
    margin: 14px auto;
}

:checked + .target .icon-share-weibo {
    -ms-transform: scale(1) translate(-70px, 60px);
    transform: scale(1) translate(-70px, 60px);
}

:checked + .target .icon-share-wechat {
    -ms-transform: scale(1) translate(0, 75px);
    transform: scale(1) translate(0, 75px);
}

:checked + .target .icon-share-qq {
    -ms-transform: scale(1) translate(70px, 60px);
    transform: scale(1) translate(70px, 60px);
}

:checked + .target .share {
    animation: jello 1s;
}

@keyframes jello {
    from,
        11.1%,
    to {
        transform: none;
    }
    22.2% {
        transform: skewX(-12.5deg) skewY(-12.5deg);
    }
    33.3% {
        transform: skewX(6.25deg) skewY(6.25deg);
    }
    44.4% {
        transform: skewX(-3.125deg) skewY(-3.125deg);
    }
    55.5% {
        transform: skewX(1.5625deg) skewY(1.5625deg);
    }
    66.6% {
        transform: skewX(-0.78125deg) skewY(-0.78125deg);
    }
    77.7% {
        transform: skewX(0.390625deg) skewY(0.390625deg);
    }
    88.8% {
        transform: skewX(-0.1953125deg) skewY(-0.1953125deg);
    }
}

image.png

(2)CSS背景滤镜backdrop-filter

==> 与滤镜filter的区别 <==

说明: 区别在于背景滤镜属性是让当前元素所在区域后面的内容应用滤镜效果,因此要想看到滤镜效果,需要当前元素本身是半透明或者完全透明的;而filter属性是让当前元素自身应用滤镜效果

<body style="background: black">
    <!-- 图像的四周会有柔化效果 -->
    <div class="container filter">
        <img src="D:1.jpg" width="256" />
    </div>

    <!-- 图像的四周不会有柔化效果 -->
    <div class="container backdrop-filter">
        <img src="D:1.jpg" width="256" />
    </div>
</body>
.container {
    width: 256px;
    height: 192px;
    margin: auto;
    position: relative;
    overflow: hidden;
}

.filter img {
    filter: blur(5px);
}

.backdrop-filter::before {
    content: "";
    position: absolute;
    left: 0;
    width: inherit;
    height: inherit;
    -webkit-backdrop-filter: blur(5px);
    backdrop-filter: blur(5px);
}

image.png

==> 毛玻璃效果 <==

说明: 毛玻璃效果不是让当前元素模糊,而是让当前元素所在的区域后面的内容模糊,因此背景滤镜这个属性才能以大展身手

<== 弹框毛玻璃 ==>

说明: 就是使用模糊将弹窗后面的内容模糊掉,形成毛玻璃的效果

<!-- 来自《CSS新世界》 -->
<dialog
    class="ui-dialog-container ui-dialog-container-alert"
    tabindex="-1"
    open=""
>
    <div class="ui-dialog" style="width: auto">
        <button
            class="ui-dialog-close ESC"
            aria-label="关闭"
            id="lulu_9790540485925694"
            data-target="lulu_9790540485925694"
        >
            <svg
                version="1.1"
                xmlns="http://www.w3.org/2000/svg"
                width="200"
                height="200"
                viewBox="0 0 200 200"
            >
                <path
                    d="M116.152,99.999l36.482-36.486c2.881-2.881,2.881-7.54,0-10.42 l-5.215-5.215c-2.871-2.881-7.539-2.881-10.42,0l-36.484,36.484L64.031,47.877c-2.881-2.881-7.549-2.881-10.43,0l-5.205,5.215 c-2.881,2.881-2.881,7.54,0,10.42l36.482,36.486l-36.482,36.482c-2.881,2.881-2.881,7.549,0,10.43l5.205,5.215 c2.881,2.871,7.549,2.871,10.43,0l36.484-36.488L137,152.126c2.881,2.871,7.549,2.871,10.42,0l5.215-5.215 c2.881-2.881,2.881-7.549,0-10.43L116.152,99.999z"
                ></path>
            </svg>
        </button>
        <h4 class="ui-dialog-title"></h4>
        <div class="ui-dialog-body">
            <div class="ui-dialog-remind ui-dialog-alert">
                <p>弹框出现啦!</p>
            </div>
        </div>
        <div class="ui-dialog-footer">
            <button class="ui-button" data-type="primary" style="outline: none">
                确定
            </button>
        </div>
    </div>
</dialog>
.ui-dialog-container {
    position: fixed;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    padding: 0;
    border: 0;
    background-color: rgba(25, 28, 34, 0.88);
    text-align: center;
    color: #4c5161;
    font-size: 14px;
    white-space: nowrap;
    overflow: auto;
    z-index: 19;
}

.ui-dialog-container:after {
    content: "";
    display: inline-block;
    height: 95%;
    vertical-align: middle;
}

.ui-dialog {
    display: inline-block;
    margin-top: 20px;
    margin-bottom: 30px;
    text-align: left;
    min-width: 400px;
    border-radius: 4px;
    background-color: #f7f9fa;
    vertical-align: middle;
    white-space: normal;
    outline: none;
    position: relative;
}

.ui-dialog-title {
    margin: 0;
    line-height: 30px;
    padding: 15px 50px 0 25px;
    font-weight: bold;
    font-size: 14px;
    color: #4c5161;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    cursor: default;
}

.ui-dialog-close {
    position: absolute;
    top: 8px;
    right: 7px;
    width: 40px;
    height: 40px;
    border: 0;
    background: none;
    transition: fill 0.2s;
    fill: #b6bbc6;
    cursor: pointer;
    z-index: 1;
}

.ui-dialog-close > svg {
    width: 20px;
    height: 20px;
}

.ui-dialog-close:hover {
    background-color: #4c5161;
    background-color: rgba(0, 0, 0, 0);
    fill: #4c5161;
}

.ui-dialog-body {
    min-height: 60px;
    padding: 10px 25px 20px;
}

.ui-dialog-title:empty ~ .ui-dialog-body {
    min-height: 40px;
    padding-top: 30px;
}

.ui-dialog-body:after {
    content: "";
    display: table;
    clear: both;
}

.ui-dialog-footer {
    padding: 3px 25px 25px;
    margin-top: -3px;
    text-align: right;
    max-height: 40px;
    opacity: 1;
    transition: max-height 0.2s, opacity 0.2s 0.1s;
    overflow: hidden;
}

.ui-dialog-footer:empty {
    max-height: 0;
    opacity: 0;
}

.ui-dialog-footer .ui-button {
    margin-left: 15px;
}

.ui-dialog-footer .ui-button:first-child {
    margin-left: 0;
}

.ui-dialog-stretch {
    max-height: 2000px;
    height: calc(100% - 50px);
}

.ui-dialog-stretch .ui-dialog-footer {
    position: absolute;
    left: 0;
    bottom: 0;
    right: 0;
}

.ui-dialog-stretch .ui-dialog-body {
    position: absolute;
    left: 0;
    right: 0;
    top: 50px;
    bottom: 90px;
    padding: 0 0 0 25px;
    overflow: auto;
}

.ui-dialog-remind {
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-105 197 400 400'%3E%3Cstyle%3E.st1%7Bfill:%23fff%7D%3C/style%3E%3Cpath d='M95.5 197.5c110.5 0 200 89.5 200 200s-89.5 200-200 200-200-89.5-200-200 89.5-200 200-200z' fill='%232a80eb'/%3E%3Cpath class='st1' d='M90.5 347.5h10c2.8 0 5 2.2 5 5v150c0 2.8-2.2 5-5 5h-10c-2.8 0-5-2.2-5-5v-150c0-2.8 2.2-5 5-5zM90.5 297.5h10c2.8 0 5 2.2 5 5v20c0 2.8-2.2 5-5 5h-10c-2.8 0-5-2.2-5-5v-20c0-2.8 2.2-5 5-5z'/%3E%3C/svg%3E"), none;
}

.ui-dialog-remind {
    padding: 20px 0 20px 60px;
    background-repeat: no-repeat;
    background-position: 0 10px;
    background-size: 40px 40px;
}

.ui-dialog-alert > h6 {
    font-size: 15px;
    margin-bottom: 5px;
}

.ui-dialog-alert > :first-child:only-child {
    margin-top: 0;
}

dialog {
    backdrop-filter: blur(5px);
}

image.png

<== 下拉毛玻璃 ==>

<div class="ui-droplist-x">
    <a class="ui-droplist-li">CSS世界</a>
    <a class="ui-droplist-li">CSS世界</a>
    <a class="ui-droplist-li">CSS世界</a>
</div>
<img src="D:1.jpg" alt="" />
.ui-droplist-x {
    position: absolute;
    width: 111px;
    padding: 7px 0;
    background-color: #fff;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
    border: 1px solid #d0d0d5;
    border-radius: 4px;
    font-size: 14px;
    animation: fadeIn 0.2s;
    z-index: 9;
}

.ui-droplist-li {
    display: block;
    line-height: 20px;
    padding: 7px 12px 8px;
    color: #4c5161;
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}

.ui-droplist-x {
    /* 让下拉框整体透明度上升 */
    background: hsla(0, 0%, 100%, 0.75);
    -webkit-backdrop-filter: blur(5px);
    /* 之后使用模糊滤镜就能得到毛玻璃效果了 */
    backdrop-filter: blur(5px);
}

a.ui-droplist-li:hover {
    background: hsla(0, 0%, 100%, 0.3);
}

image.png

(3)CSS混合模式

说明: CSS与混合模式有如下相关的有三个属性,其中,mix-blend-mode属性和background-blend-mode属性支持的混合模式类型是一样的,总共有16种混合模式类型

  • background-blend-mode: 用于混合元素背景图案、渐变和颜色

  • mix-blend-mode: 用于元素与元素之间的混合

  • isolation: 用在祖先元素上,限制mix-blend-mode属性设置的混合模式的应用范围

混合模式类型作用
normal正常
multiply正片叠底
screen滤色
overlay叠加
darken变暗
lighten变亮
color-dodge颜色变淡
color-burn颜色加深
hard-light强光
soft-light柔光
difference差值
exclusion排除
hue色调
saturation饱和度
color颜色
luminosity亮度

==> 了解各种混合模式 <==

<== 正片叠底 multiply ==>

说明: 如果是A和B两种颜色(都变成RGB颜色计算吗)执行正片叠底的混合效果,则计算公式如下

image.png

计算举例: 已知颜色关键字deepskyblue的RGB色值是rgb(0,192,255),颜色关键字deeppink的RGB色值是rgb(255,20,147),则这两种颜色进行正片叠底混合后的色值是rgb(0,15,147)

R = 0 * 255 / 255 = 0
G = 192 * 20 / 25515
B = 255 * 147 / 255 = 147

image.png

结论: 任意颜色与黑色(RGB:0,0,0)正片叠底一定是黑色;与白色(RGB:255,255,255)正片叠底一定是当前颜色;除了这两种颜色外,和其他颜色混合的正片叠底效果一定是会变暗的,也就是说正片叠底可以增强两张图像中暗的部分

适用场景: 一般用来将浅色的素材进行背景合成

<== 滤色 screen ==>

说明: 如果是A和B两种颜色(都变成RGB颜色计算吗)执行滤色的混合效果,则计算公式如下

image.png

结论: screen是混合后颜色变亮。因为滤色混合模式将两个混合颜色的互补色值相乘,然后除以255

特性:

  • 任何颜色和黑色进行滤色混合后,还是呈现原来的颜色

  • 任何颜色和白色进行滤色混合后得到的仍是白色

  • 任何颜色和其他颜色进行滤色混合后,颜色会更浅,有点类似漂白的效果

image.png

适用场景: 对一些图像进行场景特效的添加

<== 叠加 overlay ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行叠加的混合效果,则计算公式如下

image.png

结论: 叠加这种混合模式的底图的高光(白色部分)和阴影(黑色部分)的颜色会被保留,其他颜色的饱和度和对比度会有一定的提高,混合后的图像看起来会更鲜亮

image.png

文字水印: 使用深色文字,将混合模式设置为叠加,再将文字旋转一定角度即可

img {
    width: 400px;
}

.water {
    width: 256px;
    height: 192px;
    position: relative;
}

.water::before {
    content: "文字水印";
    position: absolute;
    mix-blend-mode: overlay;
    text-shadow: 10ch 2em, -10ch 2em, 10ch -2em, -10ch -2em, 0 -5em, 0 5em;
    transform: rotate(-30deg);
    left: calc(50% - 5ch);
    top: 90px;
}
<div class="water">
    <img src="D:1.jpg" />
</div>

image.png

文字高亮: 直接使用色块覆盖文字,然后设置混合模式为叠加,需要注意的是文字的背景需要是白色的

p {
        background: #fff;
        color: gray;
      }

      ui-overlay {
        position: absolute;
        background: red;
        mix-blend-mode: overlay;
        z-index: 9;
      }
<h4>颜色叠加(适合浅色)</h4>

<p>
    <input type="search" placeholder="输入内容回车搜索" />
</p>

<p id="target">
    可以在上面的输入框中输入任意这段内容中出现的文字或者单词,在输入内容并按回车键确认后,就会看到文字有高亮显示效果。但是,这种高亮显示效果不是通过包裹标签元素实现的,而是将色块直接覆盖在文字上面,然后通过叠加这种混合模式实现的。
</p>
var eleSearch = document.querySelector('input[type="search"]');
var eleTraget = document.querySelector("#target");

eleSearch.addEventListener("input", function () {
    var text = eleTraget.textContent;

    [].slice
      .call(document.querySelectorAll("ui-overlay"))
      .forEach(function (overlay) {
          overlay.remove();
      });

    // 匹配处理
    var value = this.value.trim();
    var length = value.length;

    if (!length) {
        return;
    }

    var arrMatchs = text.split(value);

    if (arrMatchs.length > 1) {
        var start = 0;
        arrMatchs.forEach(function (parts, index) {
            if (index == arrMatchs.length - 1) {
                return;
            }
            
            var range = document.createRange();
            start += parts.length;
            range.setStart(eleTraget.firstChild, start);
            range.setEnd(eleTraget.firstChild, start + length);

            var bound = range.getBoundingClientRect();

            var eleOverlay = document.createElement("ui-overlay");
            document.body.appendChild(eleOverlay);

            eleOverlay.style.left = bound.left + "px";
            eleOverlay.style.top = bound.top + window.pageYOffset + "px";
            eleOverlay.style.width = bound.width + "px";
            eleOverlay.style.height = bound.height + "px";

            start += length;
        });
    }
});

image.png

<== 变暗 darken ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行变暗的混合效果,则计算公式如下

image.png

计算举例: 将deepskyblue[rgb(0,192,255)]和deeppink[rgb(255,20,147)]进行变暗混合,最后的色值是rgb(0,20,147)

R = min(0,255) = 0
G = min(192,20) = 20
B = min(255,147) = 147

image.png

<== 变亮 lighten ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行变暗的混合效果,则计算公式如下

image.png

计算举例: 将deepskyblue[rgb(0,192,255)]和deeppink[rgb(255,20,147)]进行变亮混合,最后的色值是rgb(255,192,255)

R = max(0,255) = 255
G = max(192,20) = 20
B = max(255,147) = 255

image.png

文字渐变: 设置文字为黑色,然后在文字上覆盖一层渐变颜色,同时设置混合模式为lighten,由于任何颜色和黑色进行变亮混合后都会保留当前的颜色,因此文字的轮廓就会使用渐变色进行填充

.gradient-text {
    width: 600px;
    position: relative;
    font-size: 3rem;
    /* 文字颜色设为黑色 */
    color: black;
    /* 背景颜色设为白色 */
    background: #fff;
}

.gradient-text:before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background: linear-gradient(to right, deepskyblue, deeppink);
    /* 混合模式设为lighten */
    mix-blend-mode: lighten;
}
<div class="gradient-text">混合模式实现文字渐变</div>

image.png

<== 颜色变淡 color-dodge ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行颜色变淡的混合效果,则计算公式如下

image.png

作用: 作用效果就是在保留底部图层的颜色的基础上,使其颜色变得更淡一些

image.png

适用场景: 处理高光下的人物照片,通过将照片和特定颜色混合,可以改变整个照片的色调(暖色调或是冷色调),而不会影响人物高光区域的细节

<== 颜色加深 color-burn ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行颜色加深的混合效果,则计算公式如下

image.png

作用: 作用效果就是在保留底部图层的颜色的基础上,使其颜色变得更深一些

image.png

适用场景: 保护底图的阴影,适合处理幽深秘境一类的照片,通过将照片和特定颜色混合,可以营造更加幽深的氛围

<== 强光 hard-light ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行强光的混合效果,则计算公式如下

image.png

与overlay的区别: hard-light根据上层元素的色值判断是使用正片叠底还是滤色模式,overlay则根据下层元素的色值进行判断。具体表现为:当上层元素的色值大于128的时候,运行滤色算法,底色变亮,有助于增强图像的高光;当上层元素的色值小于或等于128时,运行正片叠底算法,底色变暗,可增强图像的暗部

效果: 图像亮的地方更亮,暗的地方更暗

image.png

<== 柔光 soft-light ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行柔光的混合效果,则计算公式如下

image.png

效果: 纯黑或纯白的上层元素与底层元素混合后的效果仅仅是元素轻微变暗或变亮,而不是变成纯黑或纯白,就好像发散的光源四处弥漫,只是没有强光那样强烈

image.png

<== 差值 difference ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行差值的混合效果,则计算公式如下

image.png

效果: 最终颜色的色值是用较浅颜色的色值减去较深颜色的色值的结果,如果上层元素的颜色是白色,则最终混合的颜色是底层元素颜色的反色

image.png

<== 排除 exclusion ==>

说明: 如果是A(这里A表示底图)和B两种颜色(都变成RGB颜色计算吗)执行排除的混合效果,则计算公式如下

image.png

效果: 最终的混合效果和difference模式是类似的,区别在于exclusion的对比度要更低一些,一般来说,图像饱和度越高,两种模式的差异越小

image.png

<== 色调混合 hue ==>

作用: 将颜色混合,使用底层元素的亮度和饱和度,以及上层元素的色调

注意: 不要使用黑色进行混合,因为这样会挖掉底层元素的颜色信息,导致最终混合后的颜色是灰色

image.png

<== 饱和度混合 saturation ==>

作用: 混合后的颜色保留底图的亮度和色调,并使用顶图的饱和度

image.png

<== 颜色混合 color ==>

作用: 混合后的颜色保留底图的亮度,并使用顶图的色调和饱和度

image.png

<== 亮度混合 luminosity ==>

作用: 混合后的颜色保留底图的色调和饱和度,并使用顶图的亮度,效果和color模式正好是相反的

image.png

注意: 亮度混合模式并不适合将色彩丰富的图像作为底图,因为图像中的各种颜色的亮度是不一样的,如果替换成有规律的亮度值,效果会很奇怪

==> 滤镜 + 混合模式 = ? <==

<== 白天素材模拟夜晚场景 ==>

div {
    width: 256px;
    height: 144px;
    background: rgba(0, 40, 140, 0.6) url(D:1.jpg);
    background-size: 100%;
    background-blend-mode: darken;
    filter: brightness(80%) grayscale(20%) contrast(1.2);
}
<div />

image.png

(4)混合模式属性 background-blend-mode

说明: 主要在各个背景图像之间应用混合模式,一般用来制作丰富CSS的背景纹理

注意: 如果background-blend-mode的属性值的数量大于background-image的属性值的数量,则多出来的混合模式会被忽略,否则就重复完整的background blend-mode属性值进行补全

/* 等价的 */
.example {
    background: linear-gradient(deepskyblue, deeppink), white;
    background-blend-mode: lighten, darken;
}

.example {
    background: linear-gradient(deepskyblue, deeppink), white;
    background-blend-mode: lighten;
}
/* 等价的 */
.example {
    background: linear-gradient(deepskyblue, deeppink),
                linear-gradient(deepskyblue, deeppink),
                linear-gradient(deepskyblue, deeppink), white;
    background-blend-mode: lighten, darken;
}

.example {
    background: linear-gradient(deepskyblue, deeppink),
                linear-gradient(deepskyblue, deeppink),
                linear-gradient(deepskyblue, deeppink), white;
    background-blend-mode: lighten, darken, lighten;
}

==> 背景纹理效果 <==

说明: 使用混合模式主要能够让实现的纹理的颜色丰富度提升了一个级别,比如下面的例子

<div class="pattern"></div>
.pattern {
    width: 300px;
    height: 180px;
    margin: auto;
    background: repeating-linear-gradient(
        floralwhite 20px,
        lightcoral 0 40px,
        floralwhite 0 60px
    ),
    repeating-linear-gradient(
        90deg,
        floralwhite 20px,
        lightcoral 0 40px,
        floralwhite 0 60px
    );
    background-blend-mode: multiply;
}

image.png

==> 作用机制 <==

注意:

  • 当一个元素应用background-blend-mode背景混合模式,最终的效果只受当前元素的背景图像和背景颜色影响,不受视觉上处于当前区域的其他任意元素影响

  • 应用background-blend-mode属性后,不仅各张图像之间要进行混合,而且各张图像还要和背景色进行混合

<== 背景顺序的影响 ==>

说明: 在CSS多背景中,语法越靠后的背景图像的层级越低,从而混合效果和background属性中背景图像的顺序密切相关

/* 只是渐变的颜色顺序不一样,但是混合之后效果完全是两种颜色 */
.ball {
    text-align: center;
    line-height: 200px;
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: linear-gradient(deeppink, deeppink),
    linear-gradient(deepskyblue, deepskyblue);
    background-blend-mode: overlay;
}

.ball2 {
    text-align: center;
    line-height: 200px;
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: linear-gradient(deepskyblue, deepskyblue),
    linear-gradient(deeppink, deeppink);
    background-blend-mode: overlay;
}
<div class="ball">ball</div>
<div class="ball2">ball2</div>

image.png

<== 混合模式会作用所有背景 ==>

说明: 也就是background-blend-mode属性可以设置多个混合模式值,分别对应不同的背景图像,比如下面这个例子

/* 这两种写法是等价的 */
.ball {
    background: linear-gradient(deeppink, deeppink),
                linear-gradient(deepskyblue, deepskyblue);
    background-blend-mode: overlay;
}

.ball {
    background: linear-gradient(deeppink, deeppink),
                linear-gradient(deepskyblue, deepskyblue);
    background-blend-mode: overlay overlay;
}

作用说明: 两个渐变色层其实都使用了叠加混合模式,由于deeppink渐变层在最上层,因此deeppink渐变层实际上叠加的是deepskyblue渐变层和背景色,而deepskyblue渐变层叠加的只有背景色

举例:

.box {
    width: 200px;
    height: 200px;
    background: linear-gradient(
        to right bottom,
        deeppink 50%,
        transparent 50%
    ),
    linear-gradient(to top right, deeppink 50%, transparent 50%), darkblue;
    background-blend-mode: multiply, screen;
    position: relative;
}

/* 中间原始的deeppink色值 */
.box::before {
    content: "";
    position: absolute;
    width: 33%;
    height: 33%;
    inset: 0;
    margin: auto;
    background-color: deeppink;
}
<div class="box"></div>

image.png

理解:

  • 一号区域: 没有应用任何混合模式,颜色为deeppink

  • 二号区域: 两个斜向渐变均没有覆盖到这个区域,所以此处是设置的背景色darkblue

  • 三号区域: 颜色表现为渐变色deeppink和背景色darkblue进行滤色混合的效果

  • 四号区域: 颜色表现为渐变色deeppink和背景色darkblue进行正片叠底混合的效果

  • 五号区域: 区域⑤总共有3层,分别是:上层的deeppink,混合模式是multiply;下层 的deeppink,混合模式是screen;底层的背景色darkblue。于是,最终的色值表现是上层的deeppink使用multiply混合下层的deeppink和背景色darkblue使用screen混合后的色值。由于下层的deeppink和背景色darkblue使用screen混合后的色值就是区域③的颜色。因此,区域⑤的颜色就是deeppink和区域③的色值rgb(255,20,206)进行正片叠底混 合后的色值,结果是rgb(255,1,119)

<== 渐变图标 ==>

方法: 使用深色白底的PNG图片,然后用lighten混合模式实现渐变效果

.icon-delete {
    display: inline-block;
    width: 20px;
    height: 20px;
    background: linear-gradient(deepskyblue, deeppink),
        url(https://demo.cssworld.cn/images/8/delete@2x.png) no-repeat center / 16px 16px, white;
    /* PNG图标的混合模式单独设置成darken,这里需要注意一下 */
    background-blend-mode: lighten, darken;
    vertical-align: bottom;
}
<i class="icon-delete"></i>删除

image.png

(5)隔离混合模式 isolation: isolate

说明: 当元素应用了混合模式的时候,在默认情况下,该元素会混合轴上所有层叠顺序比其低的层叠元素。但是,有时候希望混合模式效果只应用到某一个元素或某一组元素上,此时就可以使用isolation: isolate来完成了

==> isolation 属性 <==

语法:

isolation: 全局关键字 | none | isolate
  • auto: 默认值,表示混合模式隔离与否根据具体情况而定
  • isolate: 表示对混合模式进行隔离
.box {
    background: linear-gradient(deepskyblue, deeppink);
}

.inner {
    /* 如果不创建一个混合模式作用域,两张图片会与渐变背景进行混合 */
    isolation: isolate;
    background: url(7.jpg);
}

.mode {
    mix-blend-mode: overlay;
}
<div class="box">
    <div class="inner">
        <img src="1.jpg" class="mode" />
    </div>
</div>

image.png

image.png

本质: 创建了一个新的层叠上下文

十二、更丰富的图形处理

(1)CSS遮罩

作用: 让一个元素按照某张图像的轮廓显示

==> mask-image 属性 <==

作用: 设置使用遮罩效果的图像

语法:

mask-image: none | <image> | <mask-source>
  • none: 默认值,表示默认无遮罩图片

  • <image>: 表示图像数据类型,包括CSS渐变图像、url()函数、image-set()函数、cross-fade()函数和element()函数等

  • <mask-source>:表示遮罩元素类型,主要指SVG遮罩元素

<== 半透明PNG图像的遮罩 ==>

说明: 所谓遮罩效果,就是只会显示遮罩图像非透明区域的内容,如果不是透明的PNG图片,是白色背景的JPG图片,则最终的遮罩效果就会是矩形

<img src="nature-8.jpg" class="mask-image" width="256" height="192" />
.mask-image {
    -webkit-mask: no-repeat center / contain;
    mask: no-repeat center / contain;
    -webkit-mask-image: url(../images/bird.png);
    mask-image: url(../images/bird.png);
}

image.png

<== SVG 图形遮罩 ==>

说明: SVG图像作为背景图像、遮罩图像和内容图像的时候,默认会按照当前匹配元素的尺寸进行等比例缩放

.mask-image {
    --svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M28.027 5.161l-17.017 17.017-7.007-7.007-3.003 3.003 10.010 10.010 20.020-20.020z'%3E%3C/path%3E%3C/svg%3E");

    -webkit-mask-image: var(--svg);
    mask-image: var(--svg);

    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
}
<img src="8.jpg" class="mask-image" width="256" height="174" />

image.png

<== 渐变图像遮罩 ==>

<img src="D:1.jpg" class="mask-image" width="256" height="192" />
.mask-image {
    --gradient1: radial-gradient(
        600px 80px at top,
        transparent 150px,
        black 152px 1000px,
        transparent 0
    );
    --gradient2: radial-gradient(
        600px 80px at bottom,
        transparent 150px,
        black 152px 1000px,
        transparent 0
    );
    -webkit-mask-image: var(--gradient1), var(--gradient2);
    mask-image: var(--gradient1), var(--gradient2);
}

image.png

<== SVG图形中的<mask>元素 ==>

说明: 无论是内联的SVG还是外链的SVG文件,如果其中包含<mask>遮罩元素,也可以作为mask-image属性的合法属性值,也就是正式语法中的<mask-source>数据类型,这个数据类型并不常用,由于它能让一些老版本的浏览器实现遮罩效果,因此需要学习一下

举例: 将下面的SVG形状转换为遮罩元素

<svg width="50" height="50" version="1.1">
    <ellipse cx="25" cy="25" rx="20" ry="10"></ellipse>
    <rect x="15" y="5" width="20" height="40" rx="5" ry="5"></rect>
</svg>

实现:

<svg>
    <mask id="mask" maskContentUnits="objectBoundingBox">
        <!-- 这里ellipse元素和rect元素都增加了fill="white", -->
        <!-- 因为其遮罩类型是基于亮度来进行遮罩的,由于白色 -->
        <!-- 亮度最高,因此才能有完全遮罩效果,如果是黑色, -->
        <!-- 则遮罩效果是完全透明的 -->
        <ellipse cx=".5" cy=".5" rx=".4" ry=".2" fill="white"></ellipse>
        <rect
            x=".3"
            y=".1"
            width=".4"
            height=".8"
            rx=".1"
            ry=".1"
            fill="white"
        ></rect>
    </mask>
</svg>
/* 对于隐藏内联在页面中的包含元素的SVG元素,不能使用display:none */
/* 或visibility:hidden进行隐藏,否则遮罩效果就会失效 */
svg { 
    width: 0;
    height: 0;
    position: absolute;
}

image.png

  • 场景一: 普通HTML元素与内联元素
<svg style="position: absolute; width: 0; height: 0">
    <mask id="mask" maskContentUnits="objectBoundingBox">
        <ellipse cx=".5" cy=".5" rx=".4" ry=".2" fill="white"></ellipse>
        <rect
            x=".3"
            y=".1"
            width=".4"
            height=".8"
            rx=".1"
            ry=".1"
            fill="white"
        ></rect>
    </mask>
</svg>

<img src="D:1.jpg" class="mask-image" width="256" height="192" />
.mask-image {
    -webkit-mask-image: url(#mask);
    mask-image: url(#mask);
}

image.png

  • 场景二: SVG元素与内联元素
<svg style="position: absolute; width: 0; height: 0">
    <mask id="mask" maskContentUnits="objectBoundingBox">
        <ellipse cx=".5" cy=".5" rx=".4" ry=".2" fill="white"></ellipse>
        <rect
            x=".3"
            y=".1"
            width=".4"
            height=".8"
            rx=".1"
            ry=".1"
            fill="white"
        ></rect>
    </mask>
</svg>

<svg width="256" height="192">
    <image
        xlink:href="D:1.jpg"
        class="mask-image"
        width="256"
        height="192"
    ></image>
</svg>
.mask-image {
    -webkit-mask-image: url(#mask);
    mask-image: url(#mask);
}

image.png

  • 场景三: 如果尺寸已知,可通过transform属性进行缩放

举例: 遮罩元素尺寸是256px × 192px,SVG元素尺寸是50px × 50px,因此,可以让SVG元素的水平尺寸放大5.12倍,垂直尺寸放大3.84倍,使SVG尺寸和遮罩尺寸保持一致

<!-- transform缩放实现 -->
<svg width="0" height="0" style="position: absolute">
    <mask id="mask">
        <ellipse
            transform="scale(5.12, 3.84)"
            cx="25"
            cy="25"
            rx="20"
            ry="10"
            fill="white"
        ></ellipse>
        <rect
            transform="scale(5.12, 3.84)"
            x="15"
            y="5"
            width="20"
            height="40"
            rx="5"
            ry="5"
            fill="white"
        ></rect>
    </mask>
</svg>

<svg width="256" height="192">
    <image
        xlink:href="D:1.jpg"
        class="mask-image"
        width="256"
        height="192"
    ></image>
</svg>
.mask-image {
    mask: url(#mask);
}

image.png

<== 图像函数遮罩 ==>

image-set():

.mask-image {
    -webkit-mask-image: -webkit-image-set(
        url(bird.png) 1x,
        url(ellipse-rect.svg) 2x
    );
    /* 1倍屏中使用bird.png作为遮罩图像,2倍屏中使用ellipserect.svg作为遮罩图像 */
    mask-image: image-set(url(bird.png) 1x, url(ellipse-rect.svg) 2x);
}

cross-fade():

.mask-image {
    -webkit-mask-image: -webkit-cross-fade(
        url(bird.png),
        url(ellipse-rect.svg),
        50%
    );
    /* ellipse-rect.svg保持50%的透明度进行遮罩渲染 */
    mask-image: cross-fade(url(bird.png), url(ellipse-rect.svg), 50%);
}

element():

/* 把id属性值是title的元素作为遮罩图像进行处理 */
.mask-image {
    -webkit-mask-image: -webkit-element(#title);
    mask-image: -moz-element(#title);
    mask-image: element(#title);
}

==> mask-mode 属性 <==

说明: 作用是根据资源的类型自动采用合适的遮罩模式,默认值是match-source;如果遮罩效果使用的是SVG中的元素,则此时的mask-mode属性的计算值是luminance,表示基于亮度判断是否要进行遮罩。如果是其他场景,则计算值是alpha,表示基于透明度判断是否要进行遮罩

语法:

mask-mode: match-source | luminance | alpha

注意: 由于mask-image支持多图片,因此mask-mode也支持多属性值

举例: 让白底的JPG照片存在预期的遮罩效果

.mask-image {
    mask: url(bird.jpg) no-repeat center / contain;
    mask-mode: luminance;
}
<img src="8.jpg" class="mask-image" />

image.png

==> mask-repeat 属性 <==

说明: 其作用类似于background-repeat属性,默认值是repeat

单属性值语法:

mask-repeat: repeat-x;
mask-repeat: repeat-y;
mask-repeat: repeat;
mask-repeat: no-repeat;
mask-repeat: space;
mask-repeat: round;
  • repeat-x:表示水平方向平铺

  • repeat-y:表示垂直方向平铺

  • repeat:是默认值,表示水平和垂直方向均平铺

  • no-repeat:表示不平铺,会看到只有一个遮罩图形位于左上角

  • space:与background属性中的space的含义是类似的,表示遮罩图片尽可能地平铺,同时不进行任何剪裁

  • round:表示遮罩图片尽可能靠在一起,没有任何间隙,同时不进行任何剪裁。这意味着图片可能会产生缩放效果

注意: 由于mask-image支持多遮罩图片,因此mask-repeat也支持多属性值

==> mask-position 属性 <==

说明: 它支持的属性值以及属性值的表现基本上和background-position是一样的;比如默认计算值是0% 0%,也就是相对左上角定位等

语法:

/* 单个关键字 */
mask-position: top;
mask-position: bottom;
mask-position: left;
mask-position: right;
mask-position: center;

/* 同时设置水平和垂直方向 */
mask-position: right top;

/* 可以使用长度值或者百分比值 */
mask-position: 30% 50%;
mask-position: 10px 5rem;
mask-position: right 20px top 20px;

/* 支持多属性值 */
mask-position: 0 0, center;

==> mask-clip 属性 <==

作用: 用来设置遮罩效果在哪一个盒子区域显示

语法:

mask-clip: padding-box;
/* 默认值 */
mask-clip: content-box;
mask-clip: no-clip;
mask-clip: fill-box;
mask-clip: stroke-box;
mask-clip: view-box;

<== border、padding、content-box ==>

说明: 这三个关键字简单说就是在那些地方上使用这个CSS遮罩

image.png

<== no-clip 属性值 ==>

作用: 不对元素的遮罩效果做区域上的限制,也就是主要是元素上的东西,比如轮廓、盒阴影都可以应用遮罩效果

注意: 其兼容性很差,只有少数几个浏览器支持这个属性

<== fill、stroke、view-box ==>

注意: 这几个属性值只有Firefox浏览器支持,并且要应用在SVG元素上才有效果

  • fill-box: 表示遮罩应用的区域是图形填充区域形成的边界盒子

  • stroke-box: 表示的遮罩区域把描边占据的区域也包含在内

  • view-box: 表示使用最近的SVG视口作为参考盒子。如果SVG代码中的viewBox属性有设置,则遮罩区域盒子位于viewBox属性建立的坐标系的原点,尺寸由viewBox属性中的宽高值决定

<h4>fill-box</h4>
<svg width="280" height="140" viewbox="0 0 280 140">
    <ellipse
        cx="140"
        cy="70"
        rx="120"
        ry="50"
        class="mask-svg fill-box"
    ></ellipse>
</svg>

<h4>stroke-box</h4>
<svg width="280" height="140" viewbox="0 0 280 140">
    <ellipse
        cx="140"
        cy="70"
        rx="120"
        ry="50"
        class="mask-svg stroke-box"
    ></ellipse>
</svg>

<h4>view-box</h4>
<svg width="280" height="140" viewbox="0 0 280 140">
    <ellipse
        cx="140"
        cy="70"
        rx="120"
        ry="50"
        class="mask-svg view-box"
    ></ellipse>
</svg>
.mask-svg {
    fill: deeppink;
    stroke: deepskyblue;
    stroke-width: 20px;
    -webkit-mask-image: url(border-arc.png);
    mask-image: url(border-arc.png);
}

.fill-box {
    -webkit-mask-clip: fill-box;
    mask-clip: fill-box;
}

.stroke-box {
    -webkit-mask-clip: stroke-box;
    mask-clip: stroke-box;
}

.view-box {
    -webkit-mask-clip: view-box;
    mask-clip: view-box;
}

image.png

==> mask-origin 属性 <==

说明: 表示遮罩效果起始点,与background origin有很多类似之处

语法: 这里属性值的作用和mask-clip属性的属性值的作用是一样的

mask-origin: content-box;
mask-origin: padding-box;
mask-origin: border-box;
mask-origin: fill-box;
mask-origin: stroke-box;
mask-origin: view-box;

==> mask-size 属性 <==

说明: mask-size属性的性质和background-size属性类似,支持的关键字属性值也类似,作用是控制遮罩图片尺寸。mask-size属性的默认值是auto,它支持contain和cover两个关键字属性值,也支持长度值和百分比值,同样支持多属性值

语法:

/* 关键字值 */
mask-size: cover;
mask-size: contain;

/* 长度值和百分比值 */
mask-size: 50%;
mask-size: 3em;
mask-size: 12px;
mask-size: 50% auto;
mask-size: 3em 25%;
mask-size: auto 6px;

/* 多属性值 */
mask-size: 50%, 25%, 25%;
mask-size: 6px, auto, contain;

==> mask-type 属性 <==

说明: mask-type属性的功能和mask-mode属性类似,都是设置不同的遮罩模式,但还是有一个很大的区别,就是mask-type属性只能作用在SVG元素上,因此默认值表现为 SVG元素默认遮罩模式,也就是默认值是luminance亮度遮罩模式,如果需要支持透明度遮罩模式,将其设置为alpha就可以了

mask-type: alpha;

==> mask-composite 属性 <==

说明: 表示同时使用多张图片进行遮罩时的合成方式,类似于PS中的布尔运算

语法:

mask-composite: add;
mask-composite: subtract;
mask-composite: intersect;
mask-composite: exclude;
  • add: 表示遮罩累加,是默认值

  • subtract: 表示遮罩相减,也就是遮罩图片重合的区域不显示。这就意味着,遮罩图片越多,遮罩区域越小

  • intersect: 表示遮罩相交,也就是遮罩图片重合的区域才显示遮罩

  • exclude: 表示遮罩排除,也就是遮罩图片重合的区域被当作透明的

<== 基础效果 ==>

举例: 假设有两个遮罩图像,一个是矩形,另一个是圆形

mask-image: circle.svg, rect.svg;

image.png

注意: 由于代码中顺序越往后的图像层级越低,因此,rect.svg的位置在 circle.svg的下方

add: circle.svg覆盖在rect.svg的上方,两者共同组成最终的遮罩图像

image.png

subtract: 扣除上面的circle.svg覆盖的区域,最终表现为circle.svg和rect.svg重叠的区域消失

image.png

intersect: circle.svg和rect.svg重叠的部分作为遮罩区域

image.png

exclude: 最终合成的遮罩区域是非重叠的部分

image.png

<== Chrome浏览器支持的属性值 ==>

说明:

-webkit-mask-composite: source-over;
-webkit-mask-composite: source-in;
-webkit-mask-composite: source-out;
-webkit-mask-composite: source-atop;
-webkit-mask-composite: destination-over;
-webkit-mask-composite: destination-in;
-webkit-mask-composite: destination-out;
-webkit-mask-composite: destination-atop;
-webkit-mask-composite: xor;
-webkit-mask-composite: copy;
-webkit-mask-composite: plus-lighter;
-webkit-mask-composite: clear;
  • source-over: 表示遮罩区域累加,效果和CSS规范中的add值是一样的

  • source-in: 表示遮罩区域是交叉重叠的区域,效果和CSS规范中的intersect值是一样的

  • source-out:表示遮罩区域是上层图像减去其和下层图像重叠的区域,效果和CSS规范中的subtract值是一样的

  • source-atop:表示保留底层图像区域,并在其上方累加上层和下层图像重叠的区域。destination-over表示下层遮罩图像叠加在上层遮罩图像上。不过,由于默认的遮罩模式是基于alpha通道计算的,也就是基于透明度计算的,因此source-over和destination over效果都是一样的,除非遮罩模式是基于亮度计算的,两者才会有明显的区别

  • destination-*: 这几个值和上面source-* 几个属性值的区别就是,source-*是上层图像对下层图像如何,而destination-*是下层图像对上层图像如何

  • destination-in:表示遮罩区域是交叉重叠的区域

  • destination-out:表示遮罩区域是下层图像减去其和上层图像重叠的区域

  • destination-atop:表示保留上层图像区域,并在上方累加上层和下层图像重叠的区域

  • xor:表示上层图像和下层图像重叠的区域透明,效果和CSS规范中的exclude属性值是一样的

  • copy:表示忽略下层图像,只使用上层图像区域作为遮罩区域

  • plus-lighter: 适合用在mask-sourcetype属性值为luminance的场景下

  • clear: 当遮罩图像有多张的时候,最终的遮罩效果是完全透明的

==> 使用举例 <==

<== PNG/SVG 背景图标变色 ==>

说明: 将原本作为背景图像的小图标改成遮罩图像,这样,背景色块就只会显示小图标的形状,看起来就像小图标变色了

<a href="#">
    <i class="icon-delete"></i>
    删除
</a>
.icon-delete {
    display: inline-block;
    width: 20px;
    height: 20px;
    background-color: currentColor;
    --mask: url(/images/8/delete@2x.png) no-repeat center / 1.125em 1.125em;
    -webkit-mask: var(--mask);
    mask: var(--mask);
    color: red;
    vertical-align: -4px;
}

image.png

注意: 应用遮罩的当前元素的outline效果会失去,这对于无障碍访问是有影响的, 解决方法是将图标遮罩的执行放在伪元素中,也就是在.icon delete::before{}语句中应用遮罩效果,这样,.icon-delete元素依然可以有outline轮廓效果

<== 优化大尺寸PNG图的方法 ==>

步骤1: 把PNG图片保存为JPG图片,此时图片的背景透明的特性丢失,因此文件会变小很多

image.png

步骤2: 为了保留图片的背景透明的特性,可以借助遮罩来进行优化,因为遮罩只显示遮罩图像的非透明区域,因此只需要准备一张边角透明的任意颜色的遮罩图像,就可以让JPG图片边角的白色区域变透明,所以根据PNG图片的轮廓制作纯色PNG图片,并将纯色PNG图片命名为leaf-mask.png

image.png

步骤3: 使用CSS遮罩让JPG图片边角的白色区域透明

<img src="leaf.jpg" width="300">
img {
    --mask-url: url(leaf-mask.png);
    -webkit-mask-image: var(--mask-url);
    mask-image: var(--mask-url);
    -webkit-mask-size: 300px;
    mask-size: 300px;
}

image.png

==> -webkit-mask-box-image 属性 <==

说明: 这个属性表示边框遮罩,它与border-image属性有很多相似之处,因此,只要按照理解border-image属性的方式理解,就能够很容易理解这个属性,比如它的语法就很类似

语法:

-webkit-mask-box-image: <mask-box-image> [<top> <right> <bottom> <left> <x-repeat> <y-repeat>];

举例: 实现一个包括小三角在内都有渐变效果的提示框效果

ui-tips {
    display: inline-block;
    max-width: 250px;
    padding: 12px 15px 24px;
    color: #fff;
    background: linear-gradient(45deg, deepskyblue, deeppink);
    -webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M170.667 85.333h682.666c46.934 0 85.334 38.4 85.334 85.334v512c0 46.933-38.4 85.333-85.334 85.333H682.667L512 938.667 341.333 768H170.667c-46.934 0-85.334-38.4-85.334-85.333v-512c0-46.934 38.4-85.334 85.334-85.334z'/%3E%3C/svg%3E") 10 10 20 40;
}
<ui-tips>CSS</ui-tips>

image.png

注意:

  • SVG图像的宽高设置(也就是width='60' height='60')是必须要进行的,否则SVG图像会随着容器尺寸的变化而发生变化,最终效果不能适应各种场景

  • SVG图像的宽高尺寸要小,最好小于最小内容的宽高值,在本例中SVG图像的宽高都为60px,如果再大一些,例如100px,那么可能文字内容少的时候效果就会有瑕疵

  • 只能实现小三角在一侧的效果,不能实现小三角居中定位的效果,因为在九宫格的边框剪裁分配中,中间区域的图像只能是拉伸或者平铺等方式,没有no-repeat类型

==> mask-border 属性 <==

说明: mask-border属性和border-image属性在语法上极为相似,它是很多CSS属性的缩写,它也是表示边框遮罩,与-webkit-mask-box-image属性存在以下区别

  • 语法细节不同。例如,-webkit-mask-box-image的属性值使用空格分隔,不会用到斜杠,而mask-border属性是多个CSS属性的缩写,会使用斜杠进行属性值的区分

  • -webkit-mask-box-image是非标准CSS属性,mask-border是标准CSS属性

  • -webkit-mask-box-image属性在webkit内核浏览器中兼容性极佳,因此,在移动端项目可以放心大胆使用,但是mask-border属性就少有支持的浏览器了

语法:

mask-border-modemask-border-outsetmask-border-repeatmask-border-slicemask-border-sourcemask-border-width
  • mask-border-mode: 表示边框遮罩的模式,支持alpha和luminance这两个关键字属性值,分别表示遮罩效果是基于透明度还是基于亮度

  • mask-border-outset: 表示边框遮罩效果向外偏移的大小,支持长度值和数值,如果值是数值,则表示边框宽度border-width属性值的倍数

  • mask-border-repeat: 表示遮罩图像的平铺方式,支持 stretch、repeat、round、space等关键字属性值

  • mask-border-slice: 表示对遮罩图像进行九宫格划分的方式,支持4个方位的划分,支持的参数值类型和效果与border-image-slice属性一致

  • mask-border-source: 表示使用的遮罩图像资源,支持任意的<image>数据类型的图像,常见的有url()图像和渐变图像

  • mask-border-width: 表示边框遮罩效果应用的宽度,支持长度值和百分比值

(2)裁剪属性 clip-path

说明: 这个属性可以用来对任意元素的可视区域进行剪裁,相比CSS2.1中的clip属性,clip-path属性不需要将元素设置为绝对定位就能生效,并且剪裁的形状类型要远比clip属性丰富得多。clip属性只能进行矩形剪裁,而clip-path属性不仅可以进行矩形剪裁,圆形、多边形和不规则形状都是可以剪裁的

==> 了解clip-path 属性 <==

语法:

/* 关键字属性值 */
clip-path: none;

/* <clip-source> 值类型 */
clip-path: url(resources.svg#someId);

/* <geometry-box> 值类型 */
clip-path: margin-box;
clip-path: border-box;
clip-path: padding-box;
clip-path: content-box;
clip-path: fill-box;
clip-path: stroke-box;
clip-path: view-box;

/* <basic-shape> 值类型 */
clip-path: inset(100px 50px);
clip-path: circle(50px at 0 100px);
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
clip-path: path(
    "M0.5,1 C0.5,1,0,0.7,0,0.3 
    A0.25,0.25,1,1,1,0.5,0.3  
    A0.25,0.25,1,1,1,1,0.3 C1,0.7,0.5,1,0.5,1 Z"
);

注意:

  • 剪裁效果发生的时候,元素原始的位置是保留的,不会发生布局上的变化,因此在图形动态效果领域贡献很大

  • 被剪裁的区域不能响应点击行为,也不能响应:hover伪类 和:active伪类。这一点和mask属性不同,元素应用mask属性遮罩 效果后,透明的部分依然是可以点击的

  • clip-path属性的几个基本图形函数都是支持动画效果的,但是需要关键坐标点的数量在动画前后保持一致。对于path() 函数,还需要路径的指令保持一致才会有动画效果

<== <clip-source> 类型 ==>

说明: 这里的source指的是SVG中的clipPath元素,因此这个类型的本质就是SVG裁剪,一般用来实现小图标效果

<svg width="50" height="50" version="1.1">
    <ellipse cx="25" cy="25" rx="20" ry="10"></ellipse>
    <rect x="15" y="5" width="20" height="40" rx="5" ry="5"></rect>
</svg>

举例: 将上面的图形作为小图标使用,只需要嵌套一层clipPath标签,然后把尺寸设置成剪裁元素需要的尺寸即可,一般有两种方法,一种是transform,第二种方法是设置 clipPathUnits=objectBoundingBox

  • transform: 这种是进行相应的缩放操作,假设剪裁元素的尺寸是20px × 20px,SVG的尺寸是50px × 50px,因此,SVG尺寸需要缩小到原来的40%,这种方法优点是计算方便,不足是只能用到尺寸固定的元素上

  • clipPathUnits=objectBoundingBox: 之后需要把所有坐标值以数值1为基准进行重计算,这种方法优点是可以自动适配任意尺寸元素的剪裁,不足是数值的重计算有些复杂,需要借助工具

.icon-example-1,
.icon-example-2 {
    display: inline-block;
    width: 20px;
    height: 20px;
    background-color: deepskyblue;
    vertical-align: middle;
}

.icon-example-1 {
    /* 将url()函数中的参数指向<clipPath>元素的id即可 */
    -webkit-clip-path: url(#clipDefault);
    clip-path: url(#clipDefault);
}

.icon-example-2 {
    /* 将url()函数中的参数指向<clipPath>元素的id即可 */
    -webkit-clip-path: url(#clipBound);
    clip-path: url(#clipBound);
}
<h4 class="fill">transform缩放</h4>
<svg width="0" height="0" style="position: absolute">
    <clipPath id="clipDefault">
    <ellipse
        transform="scale(0.4, 0.4)"
        cx="25"
        cy="25"
        rx="20"
        ry="10"
    ></ellipse>
    <rect
        transform="scale(0.4, 0.4)"
        x="15"
        y="5"
        width="20"
        height="40"
        rx="5"
        ry="5"
    ></rect>
    </clipPath>
</svg>

<p>图标效果:<i class="icon-example-1"></i></p>

<h4 class="fill">clipPathUnits属性</h4>
<svg width="0" height="0" style="position: absolute">
    <clipPath id="clipBound" clipPathUnits="objectBoundingBox">
    <ellipse cx=".5" cy=".5" rx=".4" ry=".2"></ellipse>
    <rect x=".3" y=".1" width=".4" height=".8" rx=".1" ry=".1"></rect>
    </clipPath>
</svg>
<p>图标效果:<i class="icon-example-2"></i></p>

<h4 class="fill">使用SVG元素(兼容到IE9)</h4>
图标效果:<svg width="20" height="20">
    <rect
        fill="deepskyblue"
        class="icon-example-1"
        width="20"
        height="20"
    ></rect>
</svg>

image.png

<== <geometry-box> 类型 ==>

说明: 这种类型是跟盒子相关的,一般只需要关注margin-box、border-box、padding box和content-box这几个盒子就可以,通常这些盒子类型需要配合其他类型的值一起使用

举例:

.clip-path {
    width: 200px;
    height: 150px;
    margin: 20px;
    padding: 20px;
    border: 20px solid deepskyblue;
    background-color: deeppink;
    --basic-shape: polygon(0% 0%, 100% 0%, 50% 100%);
    clip-path: var(--geometry-box) var(--basic-shape);
}

.margin-box {
    --geometry-box: margin-box;
}

.border-box {
    --geometry-box: border-box;
}

.padding-box {
    --geometry-box: padding-box;
}

.content-box {
    --geometry-box: content-box;
}
<img src="D:1.jpg" class="clip-path margin-box" />
<img src="D:1.jpg" class="clip-path border-box" />
<img src="D:1.jpg" class="clip-path padding-box" />
<img src="D:1.jpg" class="clip-path content-box" />

image.png

<== <basic-shape> 类型 ==>

说明: 这是这个属性使用的裁剪方式最频繁的一个,需要结合基本形状函数进行使用,常见的有inset()、circle()、 ellipse()、polygon()和path()

  • inset(): 可以剪裁出矩形和圆角矩形形状

说明: 这个函数类似于clip属性中的rect()函数,但是二者的语法的解释是不一样的;rect()函数的4个值只对应2个方位,分别是元素的上、左、 上、左;而inset()函数的4个值对应4个方位,分别是元素的上、左、下、右。

.clip-me {
    /* rect()函数 */
    /* position:fixed也可以 */
    position: absolute;
    clip: rect(30px 150px 150px 20px);

    /* inset()函数,无须定位属性 */
    clip-path: inset(30px 150px 150px 20px);
}

image.png

注意: 相比rect()函数,inset()函数支持百分比值,这个在clip属性中是不支持的

/* 合法 */ 
clip-path: inset(10% 20% 30% 40%)

/* 非法 */
clip: rect(10% 20% 30% 40%);

圆角语法:

/* 注意round关键字的位置 */
inset(<length-percentage>{1,4} round <'border-radius'>);

举例:

/* 偏移大小15%,圆角大小10% 50% 10% 50%*/
clip-path: inset(15% round 10% 50% 10% 50%);

image.png

img {
    /* 圆角的位置是可以移动指定的 */
    clip-path: inset(15% 0% 15% 30% round 10% 50% 10% 50%);
}

image.png

img {
    /* border-radius属性支持的圆角值,inset()函数也都支持,比如内圆角和外圆角 */
    clip-path: inset(15% 0% 15% 30% round 50% 50% 0% 0% / 100% 100% 0% 0%);
}

image.png

  • circle(): 可以用来剪裁圆形形状

语法:

circle( [ <shape-radius> ]? [ at <position> ]? )

说明: 从语法中可以得到无论是圆角大小尺寸,还是位置信息,都是可以省略的,也就是可以不设置任何参数,直接使用circle()函数,此时选取最短边作为剪裁半径,默认中心位置

img {
    width: 256px;
    clip-path: circle();
}

image.png

注意: 也可以指定圆心的位置,从而能够轻松实现任意大小的1/2圆形或者1/4圆形剪裁效果;同时半径值支持百分比值

/* 以右下角为圆心进行圆形剪裁 */
img {
    clip-path: circle(180px at right bottom);
}

image.png

  • ellipse(): 可以用来剪裁椭圆形状

语法:

ellipse( [ <shape-radius>{2} ]? [ at <position> ]? )

说明: ellipse()函数和circle()函数的区别在于前者多了一个半径(半轴)值。与circle()函数一样,ellipse()函数中的参数值都是可以省略的,同样会默认选取元素的长边作为长半轴,短边作为短半轴,元素中心作为裁剪中心

/* 以右下角为圆心进行圆形剪裁 */
img {
    width: 256px;
    clip-path: ellipse();
}

image.png

注意: 半径值同样支持百分比值

img {
    clip-path: ellipse(30% 50% at 75% 50%);
}

image.png

  • polygon(): 可以用来剪裁多边形

语法:

/* 这里的fill-rule表示填充规则,值可以是nonzero或evenodd */
/* 剩下的参数是坐标值 */
polygon( <fill-rule>? , [ x, y ]# )

说明: 最终的效果就是坐标点一个一个连起来形成的多边形图形,其中,最后1个点和第1个点会自动连在一起,理论上,polygon()函数可以用来剪裁任意的多边形和任意的非曲线图形,甚至在线条足够多的情况下,可以一定程度上模拟曲线效果,虽然操作起来简单,但是麻烦在坐标的设置

image.png

举例: 由于重复的连线的剪裁效果是透明的,因此polygon()函数还可以实现不规则的复合多边形效果

img {
    clip-path: polygon(
        5px 10px,
        16px 3px,
        16px 10px,
        26px 10px,
        26px 3px,
        37px 10px,
        26px 17px,
        26px 10px,
        16px 10px,
        16px 17px
    );
}

image.png

  • path(): 可以剪裁出任意图形效果

语法:

path( [ <fill-rule>, ]? <string> )

说明: 其中的string表示路径字符内容

.icon-arrow {
    width: 32px;
    height: 32px;
    background: linear-gradient(45deg, deepskyblue, deeppink);
    clip-path: path(
        "M16.016 1.157l-15.015 15.015h9.009v16.016h12.012v-16.016h9.009z"
    );
    transition: 0.2s;
}

.icon-arrow:active {
    clip-path: path(
        "M16.016 31.187l15.015-15.015h-9.009v-16.016h-12.012v16.016h-9.009z"
    );
}
<button class="icon-arrow"></button>

image.png

==> 了解填充规则 <==

说明: 也就是上面语法中的fill-rule数据类型,表示填充规则,只要涉及路径填充,都离不开填充规则,并且用来表示填充规则的参数名也都是完全一致的,即nonzero和evenodd

<== 规则差异 ==>

说明: 如果填充对象是一个三角形,则这两种填充规则没什么区别,如果填充的时候存在重叠部分,比如两个三角形,此时差别就出现了

image.png

image.png

<== 理解规则差异 ==>

关键: 是确定复杂路径构成的图形的内部和外部。内部则填充,外部则透明

规则:

  • nonzero: 非零规则,也就是在这个区域里面随便找一点发射一条射线,这条射线会穿过图形的边界,此时面向射线的方向,规定其左边是左侧,右边是右侧,如果图形边界是从左到右穿过这条射线则+1,如果是从右侧到左侧,则-1,默认是从0开始加减,计算完毕后,如果数值是0,则点在图形外部,如果值不为0,则在图形的内部

  • evenodd: 奇偶规则,射线会和路径相交,每交叉一条路径,值就加1,最后看总计算数值,如果是奇数,则认为是路径内部;如果是偶数,则认为是路径外部

非零规则举例:

image.png

image.png

奇偶规则举例:

image.png

image.png

image.png

(3)倒影效果 -webkit-box-reflect 属性

==> 了解-webkit-box-reflect 属性 <==

语法:

/* 分别表示:方位、偏移大小、遮罩图像 */
-webkit-box-reflect: [ above | below | right | left]? <length>? <image>?;
  • 方位:可以是above、below、left和right这4个值中的任意一个,分别表示在上、下、左、右进行倒影

  • 偏移大小:表示倒影和原始元素的偏移距离,可以是数值,也可以是百分比值。如果是百分比值,则百分比大小是相对于元素自身尺寸计算的,与transform属性中translate()函数的百分 比计算规则是一致的

  • 遮罩图像:可以实现对元素倒影的遮罩控制,支持url()函数图像、渐变图像等

举例:

img {
    width: 200px;
    -webkit-box-reflect: below;
}

image.png

注意:

  • 倒影和outline、box-shadow属性一样,其不占据尺寸空间,同时倒影也无法响应点击事件

  • 如果倒影的偏移值是使用百分比值设置的,那么这个百分比 值对应的尺寸计算方位是根据倒影方向自动识别的。例如倒影方向是below或above,则偏移百分比值是根据原始元素的高度计算的;如果倒影方向是left或right,则偏移百分比值是根据元素的宽度来计算的

  • 遮罩图像可以使用任意的CSS渐变语法,包括锥形渐变,不过这里的遮罩图像一次最多只能设置一张图像

  • 使用遮罩图像的时候,倒影的偏移值是不能缺省的。如果没有偏移,请使用0占位

  • 倒影效果具有实时渲染特性,也就是说,如果我们对原始图像进行剪裁,倒影也会被剪裁

  • 遮罩图像是没有跨域限制的,而mask-image属性使用的遮罩图像是有跨域限制的

(4)不规则运动 offset 属性

说明: 这个属性是offset-anchor、offset-distance、offset-path、offset-position和offset-rotate这几个属性的缩写

==> 了解offset-anchor 属性 <==

作用: 也就是规定元素在运动的时候是哪一个点在轨迹上运动的

语法:

offset-anchor: auto | <position>
  • auto: 初始值。在通常情况下,auto关键字属性值表示使用和transform-origin属性一样的值;需要注意,如果offset-path属性值是none,则使用和offset-position一样的属性值

  • <position>: 具体的锚点位置

举例:

img {
    width: 50px;
}

.horse-run {
    position: absolute;
    offset-path: path("M10,80 q100,120 120,20 q140,-50 160,0");
    offset-anchor: var(--anchor);
    animation: motion 3s linear infinite paused;
    outline: solid deepskyblue;
    mix-blend-mode: overlay;
}

.playing .horse-run {
    animation-play-state: running;
}

@keyframes motion {
    100% {
        offset-distance: 100%;
    }
}

/* 辅助识别的黑色小圆圈 */
circle {
    offset-path: path("M10,80 q100,120 120,20 q140,-50 160,0");
    animation: motion 3s linear infinite paused;
}
.playing circle {
    animation-play-state: running;
}
<button onclick="this.parentElement.classList.toggle('playing');">
    运动状态变化
</button>

<h4>默认</h4>
<p>
    <img src="D:1.jpg" class="horse-run" />
    <svg width="280" height="100" viewBox="0 50 280 100">
        <path
            d="M10,80 q100,120 120,20 q140,-50 160,0"
            stroke="#cd0000"
            stroke-width="2"
            fill="none"
        />
        <circle cx="0" cy="0" r="5" fill="#000;"></circle>
    </svg>
</p>

<h4>offset-anchor: 0 0</h4>
<p style="--anchor: 0 0; margin-top: -1em">
    <img src="D:1.jpg" class="horse-run" />
    <svg width="280" height="100" viewBox="0 50 280 100">
        <path
            d="M10,80 q100,120 120,20 q140,-50 160,0"
            stroke="#cd0000"
            stroke-width="2"
            fill="none"
        />
        <circle cx="0" cy="0" r="5" fill="#000;"></circle>
    </svg>
</p>

<h4>offset-anchor: right bottom</h4>
<p style="--anchor: right bottom; margin-top: 3em">
    <img src="D:1.jpg" class="horse-run" />
    <svg width="280" height="100" viewBox="0 50 280 100">
        <path
            d="M10,80 q100,120 120,20 q140,-50 160,0"
            stroke="#cd0000"
            stroke-width="2"
            fill="none"
        />
        <circle cx="0" cy="0" r="5" fill="#000;"></circle>
    </svg>
</p>

image.png

==> 了解offset-distance 属性 <==

说明: 表示偏移的距离大小,也就是元素沿着路径移动的距离,支持百分比值和长度值,如果移动的路径是一个封闭的路径,则可以实现一个不断循 环、无限运动的动画效果

offset-distance: 0;
/* 偏移50%距离 */
offset-distance: 50%;
/* 固定的长度值 */
offset-distance: 50px;

举例: 利用等边三角形路径实现加载效果

<i class="loading"><i></i></i>
.loading {
    display: inline-block;
    width: 40px;
    height: 40px;
    color: deepskyblue;
    background: radial-gradient(currentColor 2px, transparent 3px);
    animation: spin 6s linear infinite reverse;
}

.loading::before,
.loading::after,
.loading > i {
    content: "";
    position: absolute;
    width: 5px;
    height: 5px;
    background-color: currentColor;
    border-radius: 50%;
    /* 盒阴影模拟龙尾效果 */
    box-shadow: 0 6px 0 -0.5px, 0 12px 0 -1px, 0 18px 0 -1.5px,
    0 24px 0 -2px;
    /* 沿着等边三角形路径运动 */
    offset-path: path("M20 0 L5.858 30L34.142 30Z");
    offset-rotate: auto 90deg;
    /* 无限循环运动 */
    animation: motion 1.5s linear infinite,
    shadow 0.5s linear infinite alternate;
}

/* 通过延时让元素分别在3条边上运动 */
.loading::before {
    animation-delay: -0.5s;
}

.loading::after {
    animation-delay: -1s;
}

@keyframes motion {
    100% {
        offset-distance: 100%;
    }
}

@keyframes shadow {
    50% {
        box-shadow: 0 6px 0 -0.5px, 0 12px 0 -1px, 0 18px 0 -1.5px, 0 24px 0 -2px;
    }
    0%,
    100% {
        box-shadow: 0 0 0 -0.5px, 0 0 0 -1px, 0 0 0 -1.5px, 0 0 0 -2px;
    }
}

@keyframes spin {
    100% {
        transform: rotate(360deg);
    }
}

image.png

注意: 属性值可以是负值,也可以大于100%;如果路径是封闭的,则无论offset-distance有多大的值,都可以看到位置的变化;如果路径是开放的,则负值的位置和0%的位置是一样的,大于100%的值的位置和100%的位置是一样的

==> 了解offset-path 属性 <==

说明: 指的是运动的路径,支持多种路径类型,它和clip-path属性有众多相似之处

语法: 在兼容性上,只有path()兼容性良好

offset-path: none;
offset-path: ray([ <angle> && <size> && contain?]);
offset-path: <path() >;
/* <url>:数据类型可以直接使用页面内联SVG元素中任意图形元素的路径 */
offset-path: <url>;
/* <basic-shape>:包括inset、circle、 ellipse、polygon等基本图形函数 */
offset-path: [ <basic-shape> || <geometry-box>];

<== ray() ==>

作用: 表示射线状的偏移,其尺寸与当前元素的包含块元素密切相关

语法:

offset-path: ray( [ <angle> && <size> && contain? ] )
  • 偏移路径: 一条射线,射线的起始位置默认是元素的中心点,由offset- anchor属性决定

  • 射线角度: 由<angle>参数决定,角度的方向和位置与CSS渐变中的<angle>角度是一样的,0deg表示方向朝上,正角度值表示沿顺时针方向旋转

  • 射线距离: 由<size>参数决定,支持closest-side、closest-corner、 farthest-side、farthest-corner和sides这几个值,表示射线终止的位置是包含块元素的短边、长边、近角或远角;最后的sides表示射线和包含块元素交点的距离,如果射线的初始位置不在包含块元素内,则sides表示的距离是0

  • 射线区域: 参数contain表示当前元素是否在射线覆盖的圆形区域之内

<div class="container">
    <div class="box box-red"></div>
    <div class="box box-blue"></div>
</div>
.container {
    border-radius: 50%;
    border: 1px dashed black;
    width: 200px;
    height: 200px;
    transform-style: preserve-3d;
}

.box {
    width: 50px;
    height: 50px;
    offset-distance: 100%;
    offset-position: 50% 50%;
    offset-rotate: 0deg;
    position: relative;
}

.box-red {
    background-color: red;
    offset-path: ray(45deg closest-side);
}

.box-blue {
    background-color: blue;
    offset-path: ray(180deg closest-side);
}

image.png

.box-red {
    offset-path: ray(45deg closest-side contain);
}

.box-blue {
    offset-path: ray(180deg closest-side contain);
}

image.png

<== url() ==>

说明: 函数的参数指向SVG图形元素的ID,这样,就可以使用SVG图形元素对应的路径作为偏移路径。这些SVG图形元素包括<circle>、<ellipse>、<line>、<path>、<polygon>和<rect>等

<svg width="280" height="150" viewBox="0 0 280 150">
    <path id="road" d="M10,80 q100,120 120,20 q140,-50 160,0" />
</svg>
/* 理论上下面的CSS代码可以让元素按照#road对应的<path>元素的路径偏移 */
/* 实际上兼容性很差 */
offset-path: url('#road');

<== 基本图形函数 ==>

说明: offset-path属性支持的基本图形函数和clip-path属性支持的基本图形函数一模一样,语法也是一模一样的

常见使用:

offset-path: inset(50% 50% 50% 50%);
offset-path: circle(50% at 25% 25%);
offset-path: ellipse(30% 50% at 75% 50%);
offset-path: polygon(30% 0%, 70% 0%, 100% 50%, 30% 100%, 0% 70%, 0% 30%);

==> 了解offset-position 属性 <==

作用: 定义路径的起始点

语法:

offset-position: auto | <position>
  • auto: 初始值,表示偏移路径的起始点是元素正常的位置
  • <position>: 用来指定偏移路径起始点的位置

注意:

  • 如果元素的position属性的计算值是static,则offset-position属性是无效的

  • 如果offset-path的属性值包含数据类型(margin-box、padding- box、border-box),或者使用了基本形状函数[circle()和ellipse()除外],则offset-position属性也是无效的

  • offset-position的属性值不是auto的时候会创建新的层叠上下文和包含块,类似于transform属性

  • offset-position设置具体的定位值的表现和绝对定位是极度相似的,区别就在于绝对定位是脱离文档流的,位置的变化不会影响兄弟元素,但是offset-position属性产生的位移依然在当前布局中

  • offset-position产生的位置偏移是相对于包含块区域计算的,而不是相对于自身,这一点和offset-anchor属性不同

  • 当路径的起始点发生了变化(即offset-position值发生了变化)时,那么很自然的,元素在应用offset偏移的时候也会发生位置的变化,因此,offset-position本质上是一个定位属性

定位属性对比:

属性定位计算方式脱离文档流影响后面的兄弟元素创建层叠上下文
absolute/fixed定 位包含块
relative位移自身是,需要z-index不是auto
translate位移自身
offset-position包含块是,需要值非auto

==> 了解offset-rotate 属性 <==

作用: 用来定义元素沿着offset-path路径运动时的方向和角度

语法:

offset-rotate: [ auto | reverse] || <angle>;
  • auto: 初始值,表示元素沿着垂直于路径切线的方向运动

  • <angle>: 表示元素按照指定的角度运动,例如设置offset rotate: 30deg后,则元素会一直保持相对于元素正常状态旋转30°的角度进行运动

  • auto <angle>: 表示元素沿着垂直于路径切线的方向运动,但是元素自身需要增加一点旋转角度

  • reverse: 和auto类似,区别在于方向是反的,等同于设置了auto 180deg