【CSS】1104- 惊!妙用CSS变量和calc()实现放大镜效果

前端自习课

共 5698字,需浏览 12分钟

 ·

2021-10-09 11:49

「链接和长图失效,请大家点击阅读原文查看详情」

前言

曾发表《妙用CSS变量,让你的CSS变得更心动》[1]一文,使用全部篇幅介绍「CSS变量」。变量作为CSS体系里最厉害的特性无之一,随着浏览器日益完善,变量可大范围在项目里使用,无需关注其兼容性。

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

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

本文主题是结合另一个特性calc()玩出更多花样,主要是基于变量与JS通讯简化基于JS逻辑的效果。因此废话不多,想了解更多CSS变量细节可回看《妙用CSS变量,让你的CSS变得更心动》[2]

为了让大家感受变量与calc()的完美契合,笔者设计了一个「放大镜」效果展现其搭配使用的魅力。

动态计算

calc()用于动态计算单位,数值长度角度时间百分比都能作为参数。由于执行数学表达式后返回运算后的计算值,所以可减少大量人工计算甚至无需人工计算。

calc()饥不择食,所有计量单位都能作为参数参加整个动态计算。

  • 「数值」整数浮点数
  • 「长度」pxemremvwvh
  • 「角度」degturn
  • 「时间」sms
  • 「百分比」%

calc()虽然好用,但新手难免会遇到一些坑,谨记以下特点,相信就能玩转calc()了。

  • 四则运算:只能使用+-*/作为运算符号
  • 运算顺序:遵循加减乘除运算顺序,可用()提升运算等级
  • 符号连接:每个运算符号必须使用空格间隔起来
  • 混合计算:可混合不同计量单位动态计算

第三点尤为重要,若未能遵守,浏览器直接忽略该属性。

在SPA里有遇过因为有滚动条或无滚动条而导致页面路由在跳转过程里发生向左或向右的抖动吗?这让强迫症患者很难受,此时可用calc()巧妙解决该问题。

.elem {
    padding-rightcalc(100vw - 100%);
}

不直接声明padding-right为滚动条宽度是因为每个浏览器的默认滚动条宽度都可能不一致。100vw是视窗宽度,100%内容宽度,那么100vw - 100%就是滚动条宽度,声明padding-right用于保留滚动条出现的位置,这样滚动条出不出现都不会让页面抖动了。

另外在移动端开发里,若不考虑低版本浏览器兼容性,完全可用一行CSS代码秒杀所有移动端的伸缩方案。

/* 基于UI width=750px DPR=2的网页 */
html {
    font-sizecalc(100vw / 7.5);
}

上述font-size:calc(100vw / 7.5)其实就是根据设计图与浏览器视窗的比例动态计算font-size100/750 = x/100vw

放大镜

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

本次使用变量简化这些JS逻辑,将计算偏移量的逻辑整合到变量里,calc()用于动态计算单位,是本次改造的核心用法。

放大镜

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

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

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

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

<div class="magnifier" id="magnifier">div>
document.getElementById("magnifier").addEventListener("mousemove", e => {
    e.target.style.setProperty("--x"`${e.offsetX}px`);
    e.target.style.setProperty("--y"`${e.offsetY}px`);
});

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

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

背景选用了笔者最爱的广州珠江新城CBD全家福,不喜欢可自行替换背景哈!

$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 {
    --x0;
    --y0;
    overflow: hidden;
    position: relative;
    width: $box-w;
    height: $box-h;
    backgroundurl($box-bg) no-repeat center/100% 100%;
    cursor: pointer;
}

在该场景下很明显无需插入子节点作为放大镜的容器,可使用::before代替。

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

.magnifier {
    &::before {
        --size0;
        position: absolute;
        leftvar(--x);
        topvar(--y);
        border-radius100%;
        widthvar(--size);
        heightvar(--size);
        box-shadow1px 1px 3px rgba(#000, .5);
        content"";
        will-change: left, top;
        transformtranslate(-50%, -50%);
    }
    &:hover::before {
        --size100px;
    }
}

接下来使用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 * 倍率 - 放大镜高度 / 倍率

基于background-position正负值问题,上述两条公式还需乘以-1,则变成以下公式。

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

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

$scale-xcalc(var(--size) / #{$ratio} - #{$ratio} * var(--x));
$scale-ycalc(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 {
    --x0;
    --y0;
    overflow: hidden;
    position: relative;
    width: $box-w;
    height: $box-h;
    backgroundurl($box-bg) no-repeat center/100% 100%;
    cursor: pointer;
    &::before {
        --size0;
        $scale-xcalc(var(--size) / #{$ratio} - #{$ratio} * var(--x));
        $scale-ycalc(var(--size) / #{$ratio} - #{$ratio} * var(--y));
        positionabsolute;
        leftvar(--x);
        topvar(--y);
        border-radius: 100%;
        widthvar(--size);
        heightvar(--size);
        background#333 url($box-bgno-repeat $scale-x $scale-y/$outbox-w $outbox-h;
        box-shadow: 1px 1px 3px rgba(#000.5);
        content: "";
        will-changelefttop;
        transformtranslate(-50%, -50%);
    }
    &:hover::before {
        --size100px;
    }
}

  • 在线演示:Here[3]
  • 在线源码:Here[4]

总结

小小的变量与calc()也能如此玩耍,相信还会有更多CSS技巧可发掘。

很多同学可能觉得CSS很简单,但真正玩起来也能与JS有得一比。笔者从事前端领域多年,一直致力于CSS技术的研究与应用,当然真的不是为了玩,而是在玩的过程里将实践到的知识充分应用于工作上。

JS重要但CSS同样重要,希望喜欢CSS的同学多多关注笔者,相信你一定会有更多CSS方面的收获。在你不太愿意学习CSS时,请浏览以下网站,相信你会有不同的体验。

  • 个人官网[5]:暂时支持PC端浏览,拒绝支持IExplorer
  • 特效专辑[6]:暂时支持PC端浏览,拒绝支持IExplorer,查看源码请戳这里[7]

笔者更多的CSS开发经验已撰写成掘金小册《玩转CSS的艺术之美》[8],作为一本小众的小册同时也是掘金社区里唯一一本关于CSS的小册,相信关注CSS的你一定会喜欢。笔者已向小册姐姐申请了100份小册「6折」优惠码「OGecoefC」,喜欢CSS的同学可了解下喔。

参考资料

[1]

《妙用CSS变量,让你的CSS变得更心动》: https://juejin.cn/post/6844904084936327182

[2]

《妙用CSS变量,让你的CSS变得更心动》: https://juejin.cn/post/6844904084936327182

[3]

Here: https://codepen.io/JowayYoung/pen/oNbGzPy

[4]

Here: https://github.com/JowayYoung/idea-css/blob/master/icss/src/components/component/放大镜.vue

[5]

个人官网: https://yangzw.vip

[6]

特效专辑: https://yangzw.vip

[7]

这里: https://github.com/JowayYoung/idea-css

[8]

《玩转CSS的艺术之美》: https://juejin.cn/book/6850413616484040711

1. JavaScript 重温系列(22篇全)
2. ECMAScript 重温系列(10篇全)
3. JavaScript设计模式 重温系列(9篇全)
4. 正则 / 框架 / 算法等 重温系列(16篇全)
5. Webpack4 入门(上)|| Webpack4 入门(下)
6. MobX 入门(上) ||  MobX 入门(下)
7. 120+篇原创系列汇总

回复“加群”与大佬们一起交流学习~

点击“阅读原文”查看 120+ 篇原创文章

浏览 45
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报