手记

【CSS 好文系列】实战大操作-变量控件

2021-04-08 20:43:031645浏览

小小小

1实战 · 21手记 · 14推荐

忍不住开头打个广告😎:
2021 火爆全网的 CSS 架构实战课上线,好评如潮!!!
【课程链接:https://coding.imooc.com/class/501.html

前言

变量作为CSS体系中最高逼格的特性,没有之一。随着浏览器日益完善,变量可大范围在项目中使用,无需关注其兼容性。

虽说变量可在纯CSS中起到领头羊的作用,但是变量的设计初衷是为了更便利CSS与JS间的联系。CSS使用变量有如下好处。

  • 减少样式代码的重复性
  • 增加样式代码的扩展性
  • 提高样式代码的灵活性
  • 增多一种CSS与JS的通讯方式
  • 不用深层遍历DOM改变某个样式

本章的主题是变量控件,主要是基于变量与JS通讯简化基于JS逻辑的效果。也是本小册唯二两章结合JS完成效果的章节,常见控件有放大镜滚动渐变背景

放大镜

传统的放大镜效果需依赖大部分JS逻辑,移动和显示的效果均依赖JS,通过JS计算偏移量再渲染样式。

本次使用变量简化这些JS逻辑,将计算偏移量的逻辑整合到变量中,还记得第7章函数计算calc()吗?calc()用于动态计算单位,是本次改造的核心用法。

基于上述需求,实时获取鼠标的左偏移量上偏移量即可,而这两个偏移量是相对父节点的。通过左偏移量上偏移量结合calc()即可计算放大镜显示内容相对父节点的显示位置。

event提供以下八个偏移量,若不了解其概念很容易发生混淆。

  • screenX/screenY:相对屏幕区域左上角定位,若发生滚动行为,则相对该区域定位
  • pageX/pageY:相对网页区域左上角定位
  • clientX/clientY:相对浏览器可视区域左上角定位
  • offsetX/offsetY:相对父节点区域左上角定位,若无父节点则相对<html><body>定位

罗列出这些偏移量概念,发现offsetX/offsetY是最符合需求的,所以使用其作为放大镜显示内容相对父节点的显示位置。

<div class="magnifier" @mousemove="move"></div>

export default {
    methods: {
        move(e) {
            e.target.style.setProperty("--x", `${e.offsetX}px`);
            e.target.style.setProperty("--y", `${e.offsetY}px`);
        }
    }
};

接下来使用sass构建放大镜效果。放大镜显示内容其实就是将原图像放大N倍,通过上述偏移量按照比例截取一定区域显示内容。

先定义相关的Sass变量。设定放大倍率为2倍,那么被放大图像的宽高也是原来宽高的2倍。声明两个变量,分为为--x--y

$ratio: 2;
$box-w: 600px;
$box-h: 400px;
$box-bg: "https://static.yangzw.vip/codepen/gz.jpg";
$outbox-w: $box-w * $ratio;
$outbox-h: $box-h * $ratio;
.magnifier {
    --x: 0;
    --y: 0;
    overflow: hidden;
    position: relative;
    width: $box-w;
    height: $box-h;
    background: url($box-bg) no-repeat center/100% 100%;
    cursor: pointer;
}

还记得第9章选择器的伪元素使用场景吗?在这个场景下很明显无需插入子节点作为放大镜的容器了,使用::before即可。

放大镜在使用时宽高为100px,不使用时宽高为0px。通过绝对定位布局放大镜随鼠标移动的位置,即声明lefttop,再通过声明transform:translate(-50%,-50%)将放大镜补位,使放大镜中心与鼠标光标位置一致。由于声明lefttop定位放大镜的位置,那么还需声明will-change改善lefttop因改变而引发的性能问题。

.magnifier {
    &::before {
        --size: 0;
        position: absolute;
        left: var(--x);
        top: var(--y);
        border-radius: 100%;
        width: var(--size);
        height: var(--size);
        box-shadow: 1px 1px 3px rgba(#000, .5);
        content: "";
        will-change: left, top;
        transform: translate(-50%, -50%);
    }
    &:hover::before {
        --size: 100px;
    }
}

接下来使用background实现放大镜显示内容。依据放大倍率为2倍,那么声明size:$outbox-w $outbox-h,通过声明position-xposition-y移动背景即可,最终连写成background:#333 url($box-bg) no-repeat $scale-x $scale-y/$outbox-w $outbox-h,而$scale-x$scale-y对应position-xposition-y,用于随着鼠标移动而改变背景位置。

水平方向偏移量 = offsetX * 倍率 - 放大镜宽度 / 倍率
垂直方向偏移量 = offsetY * 倍率 - 放大镜高度 / 倍率

基于第10章背景与遮罩background-position正负值问题,上述两条公式还需乘以-1,则变成以下公式。

水平方向偏移量 = 放大镜宽度 / 倍率 - offsetX * 倍率
垂直方向偏移量 = 放大镜高度 / 倍率 - offsetY * 倍率

此时将两条公式代入到$scale-x$scale-y两个Sass变量中,若在calc()中使用Sass变量,需使用#{}的方式包含Sass变量,否则会按照字符串的方式解析。

$scale-x: calc(var(--size) / #{$ratio} - #{$ratio} * var(--x));
$scale-y: calc(var(--size) / #{$ratio} - #{$ratio} * var(--y));

最终的scss文件如下。

$ratio: 2;
$box-w: 600px;
$box-h: 400px;
$box-bg: "https://static.yangzw.vip/codepen/gz.jpg";
$outbox-w: $box-w * $ratio;
$outbox-h: $box-h * $ratio;
.magnifier {
    --x: 0;
    --y: 0;
    overflow: hidden;
    position: relative;
    width: $box-w;
    height: $box-h;
    background: url($box-bg) no-repeat center/100% 100%;
    cursor: pointer;
    &::before {
        --size: 0;
        $scale-x: calc(var(--size) / #{$ratio} - #{$ratio} * var(--x));
        $scale-y: calc(var(--size) / #{$ratio} - #{$ratio} * var(--y));
        position: absolute;
        left: var(--x);
        top: var(--y);
        border-radius: 100%;
        width: var(--size);
        height: var(--size);
        background: #333 url($box-bg) no-repeat $scale-x $scale-y/$outbox-w $outbox-h;
        box-shadow: 1px 1px 3px rgba(#000, .5);
        content: "";
        will-change: left, top;
        transform: translate(-50%, -50%);
    }
    &:hover::before {
        --size: 100px;
    }
}


  • 在线演示:[Here] codepen.io/JowayYoung/pen/oNbGzPy
  • 在线源码:[Here] github.com/JowayYoung/idea-css/blob/master/icss/src/components/component/%E6%94%BE%E5%A4%A7%E9%95%9C.vue

滚动渐变背景

各位同学使用移动端APP应该会发现某些页面在滚动时,顶部背景颜色会发生细微的变化,该变化随着滚动距离的增大而导致背景颜色变淡。

有了上述示例作为铺垫,可清楚知道变量结合鼠标事件能完成更多的酷炫效果,而关键点是把鼠标的某些参数(例如偏移量)赋值到变量上,让变量随着鼠标参数的变化而变化。主要了解该技巧,就能开发出很多变量与JS通讯的动画关联事件响应

由于本示例与滚动有关,那么毫不犹豫地想起了event.target.scrollTop,监听滚动事件并将event.target.scrollTop赋值到变量上即可。另外,当滚动距离超过一定时需做一些限制,例如背景颜色不再发生变化。

<div ref="bg" class="dynamic-bg">
    <header></header>
    <main @scroll="scroll">
        <div>
            <p>网易公司(NASDAQ: NTES),1997年由创始人兼CEO丁磊先生在广州创办,2000年在美国NASDAQ股票交易所挂牌上市,是中国领先的互联网技术公司。在开发互联网应用、服务及其它技术方面,始终保持中国业界领先地位。本着对中国互联网发展强烈的使命感,缔造美好生活的愿景,网易利用最先进的互联网技术,加强人与人之间信息的交流和共享。</p>
            <p>网易公司推出了门户网站、在线游戏、电子邮箱、在线教育、电子商务、在线音乐、网易bobo等多种服务。网易在广州天河智慧城的总部项目计划2019年1月建成,网易游戏总部将入驻。2016年,游戏业务营业收入在网易总营收中占比73.3%。2011年,网易杭州研究院启用。网易传媒等业务在北京。网易在杭州上线了网易考拉海购、网易云音乐等项目。</p>
            <p>网易2019全年财报显示,网易公司2019年全年净收入为592.4亿元;基于非美国通用会计准则,归属于网易公司股东的持续经营净利润为156.6亿元。</p>
            <p>2019年,网易深入推进战略聚焦,坚守内容消费领域,积极布局游戏、教育、音乐、电商等核心赛道,取得重大突破。在保持稳健增长的同时,网易有道、创新及其他等业务板块爆发强大潜力,为未来的长期发展提供源源不断的动能。</p>
        </div>
    </main>
</div>

.dynamic-bg {
    --scrolly: 250;
    overflow: hidden;
    position: relative;
    border: 1px solid #66f;
    width: 400px;
    height: 400px;
    header {
        --Θ: calc(var(--scrolly) * 2deg);
        --size: calc(1500px - var(--scrolly) * 2px);
        --x: calc(var(--size) / 2 * -1);
        --y: calc(var(--scrolly) * -1px);
        --ratio: calc(50% - var(--scrolly) / 20 * 1%);
        position: absolute;
        left: 50%;
        bottom: 100%;
        margin: 0 0 var(--y) var(--x);
        border-radius: var(--ratio);
        width: var(--size);
        height: var(--size);
        background-color: #3c9;
        filter: hue-rotate(var(--Θ));
        animation: rotate 5s linear infinite;
    }
    main {
        overflow: auto;
        position: relative;
        width: 100%;
        height: 100%;
        div {
            padding: 300px 20px 50px;
        }
        p {
            line-height: 1.2;
            text-align: justify;
            text-indent: 2em;
        }
    }
}
@keyframes rotate {
    to {
        transform: rotate(1turn);
    }
}

export default {
    mounted() {
        this.bgStyle = this.$refs.bg.style;
    },
    methods: {
        scroll(e) {
            const top = e.target.scrollTop;
            if (top <= 250) {
                this.bgStyle.setProperty("--scrolly", 250 - top);
            } else {
                this.bgStyle.setProperty("--scrolly", 0);
            }
        }
    }
};


  • 在线演示:[Here] codepen.io/JowayYoung/pen/wvMxdNm
  • 在线源码:[Here] github.com/JowayYoung/idea-css/blob/master/icss/src/components/component/%E6%BB%9A%E5%8A%A8%E6%B8%90%E5%8F%98%E8%83%8C%E6%99%AF.vue

作者:JowayYoung

打个小广告

2021 火爆全网的 CSS 架构实战课上线,好评如潮!!!
【课程链接:https://coding.imooc.com/class/501.html

2人推荐
随时随地看视频
慕课网APP