从今天起开始使用 CSS 属性 calc()

原文地址:Getting Started With CSS calc()

四年前,看了CSS3 Click Chart这篇文章之后,我第一次发现了calc() ,我当然非常高兴能够看到,基本的数学运算-加法,减法,乘法和除法-能够在CSS中应用。

大部分人可能会觉着预处理就可以实现逻辑运算。但是预处理器只能同单位的运算,如角度单位,时间单位,频率单位,分辨率单元和固定长度单位。而calc()可以实现混合单位的逻辑运算。

1turn总是360deg100grad始终是90deg,而3.14rad总是180deg1S始终是1000毫秒,和1kHz时总是1000Hz的,1英寸总是2.54厘米25.4毫米96px1dppx始终等于96DPI。这就是为什么预处理器能够在它们之间转换,进行混合运算。但是,因为缺少上下文关系,预处理器是不能处理1em1%1vmin1ch是多少像素的。

我们先看一个简单的例子:

div {
   font-size: calc(3em + 5px);
   padding: calc(1vmax + -1vmin);
   transform: rotate(calc(1turn - 32deg));
   background: hsl(180, calc(2*25%), 65%);
   line-height: calc(8/3);
   width: calc(23vmin - 2*3rem);
}

在一些情况下,我们可能需要在calc()函数里传变量。这个在主流的预处理器可以实现。

首先,用 Sass,我们就像任何其他原生的CSS功能一样,插入变量:

$a: 4em
height: calc(#{$a} + 7px)

LESS写法如下:

@a: 4em;
height: ~"calc(@{a} + 7px)";

还有Stylus

a = 4em
height: "calc(%s + 7px)" % a

我们也可以使用原生的CSS变量,但要注意的是,只有在Firefox 31+支持,其他的浏览器暂时还不支持CSS的变量呢。

--a: 4em;
height: calc(var(--a) + 7px);

为了能够使得calc()函数正常的起作用,有以下几点要注意的事情。首先,除以零显然是行不通的。函数名和括号之间不可有空格。加号和减号运算符之间必须用空格分隔。

下面是几种错误的写法:

calc(50% / 0)
calc (1em + 7px)
calc(2rem+2vmin)
calc(2vw-2vh)

calc()函数的参数必须是数字,有或无指定单位,都是可以的。虽然基本的支持是非常好的,但是我们可能还是会遇到一些麻烦。接下来让我们看几个例子,看下有哪些兼容性问题,以及是否是最佳的解决方案。

进一步了解运算属性

实现一个彩虹渐变。例子链接

background: linear-gradient(#f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00);

但是,这些十六进制值看着不易理解。如果使用HSL()calc(),就会更清晰些:

background: linear-gradient(hsl(calc(0*60), 100%, 50%),
                            hsl(calc(1*60), 100%, 50%),
                            hsl(calc(2*60), 100%, 50%),
                            hsl(calc(3*60), 100%, 50%),
                            hsl(calc(4*60), 100%, 50%),
                            hsl(calc(5*60), 100%, 50%),
                            hsl(calc(6*60), 100%, 50%));

可悲的是,在Firefox或Internet Explorer(IE)下,HSL()RGB()HSLA()RGBA()里使用calc()不起作用。也就是说demo只在WebKit浏览器下运行。因此,在实践中,在这一点上,最好让预处理来执行运算。而且使用预处理还有一个优点是,循环生成列表

$n: 6;
$l: ();

@for $i from 0 through $n {
   $l: append($l, hsl($i*360/$n, 100%, 50%), comma);
}
background: linear-gradient($l);

为弹性元素添加渐变背景色

比方说,我们希望背景上顶部和底部有固定的1em的条纹。唯一的问题是我们不知道元素的高度。一种解决办法是使用两个渐变

background:
   linear-gradient(#e53b2c 1em, transparent 1em),
   linear-gradient(0deg, #e53b2c 1em, #f9f9f9 1em);

但是,如果我们使用calc()就只需要一个渐变 就能搞定:

background:
   linear-gradient(#e53b2c 1em, #f9f9f9 1em,
                   #f9f9f9 calc(100% - 1em),
                   #e53b2c calc(100% - 1em));

这种写法在所有支持calc()和渐变的浏览器里都可以工作正常,因为涉及的混合单位,它和预处理器的逻辑运算是不能等效的。另外,我们可以通过定义变量使其更易于维护:

$s: 1em;
$c: #e53b2c;
$bg: #f9f9f9;

background:
   linear-gradient($c $s,
                   $bg $s,
                   $bg calc(100% - #{$s}),
                   $c calc(100% - #{$s}));

注:出于一些原因,在chromeopera里,有一个条纹比另一条略微的模糊、细。

斜条纹渐变

比方说,我们希望它的实际对角线的两侧延伸粗斜条纹。我们使用百分比来实现

background:
   linear-gradient(to right bottom,
                   transparent 42%, #000 0, #000 58%,
                   transparent 0);

在这种情况下,条纹的宽度将取决于元素本身的大小。有时候,我们正好需要用这个。比如,如果我们想用CSS实现一个旗帜。在旗帜上添加一点绿色,黄色和蓝色渐变,标志爱好者可能认识-这是一面坦桑尼亚国旗

background:
   linear-gradient(to right bottom,
                   #1eb53a 38%, #fcd116 0,
                   #fcd116 42%, #000 0,
                   #000 58%, #fcd116 0,
                   #fcd116 62%, #00a3dd 0);

坦桑尼亚国旗(查看大图

但是,如果我们想要我们的对角线条纹不依赖于元素的大小,而是固定的宽度呢?那么,我们就需要用到calc() ,在起点的时候,在50%的固定条的宽度加上的一半,在终点的时候,50%减去一半固定条纹的宽度,。如果我们想条纹的宽度为4em,这样来写:

background:
   linear-gradient(to right bottom,
                   transparent calc(50% - 2em),
                   #000 0,
                   #000 calc(50% + 2em),
                   transparent 0);

您可以调整窗口大小来测试一下这个demo。元素的大小是通过视口的大小来设定的,但是,视口改变,对角条纹始终保持相同的宽度。

已知大小中定位子元素

你可能已经看到了在父元素中间固定元素的技巧

position: absolute;
top: 50%;
left: 50%;
margin: -2em -2.5em;
width: 5em;
height: 4em;

有了calc() ,我们就可以摆脱掉margin

position: absolute;
top: calc(50% - 2em);
left: calc(50% - 2.5em);
width: 5em;
height: 4em;

我们还可以使用变量来定义宽度和高度

$w: 5em;
$h: 4em;

position: absolute;
top: calc(50% - #{.5*$h});
left: calc(50% - #{.5*$w});
width: $w;
height: $h;

需要注意的是,使用偏移量(距顶部,距左侧)的初始定位是可以的,但如果以后你打算让元素的位置移动,那么你应该使用变换。这是因为更改转换只需要合成,但更改偏移是要重新布局,触发重绘的,因此,会影响性能。

系统的坐标在网格中的起点

由于position四个属性值,我一直没热衷使用过calc()来定位相对于元素的右侧或底部的背景。但是,calc()是背景相对的某个定位的元素中间的绝佳解决方案。

几年前,我发现自己想在一个起点固定的网格上创建一个表示坐标的背景。

网格坐标系统(查看大版

该网格部分很容易实现:

background-image:
   linear-gradient(#e53b2c .5em, transparent .5em) /* horizontal axis */,
   linear-gradient(90deg, #e53b2c .5em, transparent .5em) /* vertical axis */,
   linear-gradient(#333 .25em, transparent .25em) /* major horizontal gridline */,
   linear-gradient(90deg, #333 .25em, transparent .25em) /* major vertical gridline */,
   linear-gradient(#777 .125em, transparent .125em) /* minor horizontal gridline */,
   linear-gradient(90deg, #777 .125em, transparent .125em) /* minor vertical gridline */;

background-size:
   100vw 100vh, 100vw 100vh,
   10em 10em, 10em 10em,
   1em 1em, 1em 1em;

但是,但是我们怎样把起点放到中间,而不是在左上角?

首先,background-position: 50% 50%这个写法是不正确的,它会在相对于元素50% 50%这个点的基础上再定位出 50% 50%的渐变点,而且topleft两个方向的渐变线是分开计算的。这个问题的解决方法是,使用 calc() 来重新定位这些渐变,目的是使这些渐变尽量的相对于在整个viewport的中心位置,之后只要再向上和左两个方向位移坐标轴的一半或者栅格线的一半宽度即可。

background-position:
    0 calc(50vh - .25em), calc(50vw - .25em),
    0 calc(50vh - .125em), calc(50vw - .125em),
    0 calc(50vh - .0625em), calc(50vw - .0625em);

同样,我们可以通过使用变量使其更易于维护:

演示链接 system of coordinates + grid #2 by Ana Tudor (@thebabydino) on CodePen.

保持长宽比和视窗尺寸

在构建HTML幻灯片的时候,我总是希望在一个窗口下,沿着某一个轴的中心拖拽,图片能有固定的比例。

比例箱动画

我们假设幻灯片的大小比例为 4:3,我是一个宽屏显示器上演示。幻灯片要想覆盖视口,左,右肯定会有一些留白。

比例盒:情况1(查看大版

视觉高度为100vh。已知的高度,宽高比,可以得到宽度,公式为4/3 * 100vh。我们想要让他在中间的话,需要从偏移一半视口的宽度(100vw / 2),再减去幻灯片的宽度的一半(4/3 * 100vh / 2)。这时我们就需要的calc(),因为我们有混合单位。

.slide {
   position: absolute;
   left: calc(100vw/2 - 4/3*100vh/2);
   width: calc(4/3*100vh);
   height: 100vh;
}

然而,当我们的窗口小于4:3的时候。在这种情况下,幻灯片盖住了水平方向的窗口,在顶部和底部留有一定的空间。

比例盒:情况2(查看大版

覆盖了视水平意味着宽度为100vw。根据宽高比,我们可以得出高度为 3/4*100vw。最后,顶部偏移量和刚刚的算法一样,也就是 100vh/2 - 3/4*100vw/2

@media (max-aspect-ratio: 4/3) {
   .slide {
      top: calc(100vh/2 - 3/4*100vw/2);
      left: auto; /* Undo style set outside media query  */
      width: 100vw;
      height: calc(3/4*100vh);
   }
}

当然,我们还可以用变量来定义宽高比例。这里有一个在线的demo,可以通过调整窗口大小来测试。

$a: 4;
$b: 3;

.slide {
   position: absolute;
   top: 0;
   left: calc(50vw - #{$a/$b/2*100vh});
   width: $a/$b*100vh;
   height: 100vh;

   @media (max-aspect-ratio: #{$a}/#{$b}) {
      top: calc(50vh - #{$b/$a/2*100vw});
      left: 0;
      width: 100vw;
      height: $b/$a*100vw;
   }
}

另外,我们还可以使用比全局变量更好的方式,使用mixin实现:

@mixin proportional-box($a: 1, $b: $a) {
   position: absolute;
   top: 0;
   left: calc(50vw - #{$a/$b/2*100vh});
   width: $a/$b*100vh;
   height: 100vh;

   @media (max-aspect-ratio: #{$a}/#{$b}) {
      top: calc(50vh - #{$b/$a/2*100vw}); left: 0;
      width: 100vw; height: $b/$a*100vw;
   }
}

.slide {
   @include proportional-box(4, 3);
}

注意,变量 $a$b 必须是整数,才能正常的实现媒体查询。

以上在主流浏览器目前所有版本的支持。然而,直到最近, WebKit浏览器都不支持在calc()函数中使用视口单位,不过,这个问题已被分别在Safari 8Chrome 34Opera浏览器中修复。

在幻灯片中间添加文字

关于幻灯片演示,我有两件事要说。

首先是为幻灯片没有真正覆盖整个视口的边缘,因为可能会被切断。这是一个容易解决。我简单地设置他们箱尺寸为边界盒上他们还设置了边界。

第一件事是,幻灯片并没有真正的覆盖窗口边缘,因为可能会被切断掉边框。这个很容易解决。只需要设置 box-sizingborder-box就可以了。

第二件事是给空白的幻灯片上添加一行文字,来标识。

期望的结果(查看大版

我不想使用绝对定位,所以我想我会用设置了合适的line-height

如果幻灯片的高度包含边框,覆盖了窗口的整个高度,那么行高就是 100vh减去两个边框宽度:

$slide-border-width: 5vmin;

.slide {
   /* The other styles */
   box-sizing: border-box;
   border: solid $slide-border-width dimgrey;

   h1 {
      line-height: calc(100vh - #{2*$slide-border-width});
   }
}

在这种情况下,包括边界,相对于窗口水平垂直居中,那么它的高度就是$b/$a*100vw。那么,标题的line-height 将是减去幻灯片的边框宽度的两倍:

line-height: calc(#{$b/$a*100vw} - #{2*$slide-border-width});

这是我最初的想法,理论上,应该是可行的。它确实在WebKit浏览器和IE可以。但事实证明,在Firefox里,,calc()值下,行高和其他一些属性不起作用。这个问题已解决。这样的话, calc() 就不是最佳的解决方案。幸运的是,还有很多其他的方法来解决这个问题(Flexbox的,绝对定位等等)。

在窗口上的固定点

有件事情,我特别热衷,那就是CSS 3D -尤其是使用CSS创建几何3D形状。如果我创建一个形状,通常会放到窗口的中间位置。

我会设置元素的 perspective, 还有这个图形的父级元素。这个是透视图,这里我们不做详细介绍。 如果您想了解更多关于他们是如何定位的,可以看我的CSS-技巧博客文章

设置一个 perspective,是为了确保我们看到的一切,越近物体越大,越远物体越小。这个属性perspective接受长度值,值越小,对比度越大,离我们更近,反之,渐远同理。

现在,我们来说一个非常简单的3D形状 - 一个立方体,例如 - 就在我们屏幕的中央。它看起来并不十分3D:这是太对称,正面是完全不透明的,我们只能看到最前面的面。

立方体(查看大版

我们可以将其旋转一点,旋转30度吧,围绕其y轴线(即穿过立方体中间的垂直轴)或围绕其点x轴线。这样看起来更好,但我们只能看到两个面。此外,我们看到物体旋转了,但这不是我们的目的。

旋转立方体(查看大版

还有件事,我们可以做调整的就是视觉点。这个属性叫perspective-origin。它的默认值是50% 50% 。这是相对于场景,我们知道,50% 50%的视觉点会将形状定位到中心点。现在,我们希望这个向上和向右。那么只需要设置perspective-origin: 100% 0就可以了。但这里有一个问题:我们看到的立方体展示效果将取决于窗口的尺寸(你可以测试这个通过调整视口)。

改变的窗口大小,我们看到的立方体效果。

perspective-origin定义为 100% 0,是从右上角看过去,但立方体是总是都在场景的中部。因为这个原因,改变窗口的尺寸将改变为 50% 50%(立方体被定位的位置),和 100% 0(我们设置的视角起点)之间。

可以使用calc()来设置perspective-origin,从默认值的50%加上或者减去一个固定值。

perspective-origin: calc(50% + 15em) calc(50% - 10em);

解决方案

您可以通过调整视口测试

你会用calc()吗

你已经使用calc() 了?如果是的话,那么你用在了什么情况下?

时间: 2024-11-06 10:08:53

从今天起开始使用 CSS 属性 calc()的相关文章

CSS属性选择器

属性选择器 1 <!DOCTYPE html> 2 <html lang="zh-cn"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 <link rel="stylesheet" type="text/css" href="./css/属性选择器.css&qu

CSS属性

CSS样式属性 一.字体 1.font-family:Tahoma,Arial,"Hiragino Sans GB";字体,第一种字体不能显示时,用第二种字体 2.font-size:xx-small or 10px字体大小:绝对大小:xx-small.x-small.medium.large.x-large.xx-large.x-large.xx-l相对大小:large smaller 使用数字和度量单位绝对单位:px:显示器像素个数mm.cm.in:毫米 厘米 英寸,使用这类单位,

Day49:CSS属性操作(文本、背景、边框、列表、display、边距)

一.CSS属性操作 1.CSS text 文本颜色:color 颜色属性被用来设置文字的颜色. 颜色是通过CSS最经常的指定: 十六进制值 - 如: #FF0000 一个RGB值 - 如: RGB(255,0,0) 颜色的名称 - 如:  red p { color: rebeccapurple; } 水平对齐方式 text-align 属性规定元素中的文本的水平对齐方式. left       把文本排列到左边.默认值:由浏览器决定. right     把文本排列到右边. center 把文

可用于jquery animate()方法的css属性

* backgroundPosition * borderWidth * borderBottomWidth * borderLeftWidth * borderRightWidth * borderTopWidth * borderSpacing * margin * marginBottom * marginLeft * marginRight * marginTop * outlineWidth * padding * paddingBottom * paddingLeft * paddi

与换行相关的css属性简单介绍

与换行相关的css属性简单介绍:在css布局中可能需要人为的进行操作是否换行,如何换行,本章节就就做一下简单介绍.一.word-break属性:此属性用来设定文本如何进行换行.语法结构: word-break:normal | break-all | keep-all 参数解析:1.normal:默认值,原则上规定在断字点换行,通俗的说就是在单词与语单词之间可以进行换行,但是如果单词特别的长,超出了行的长度,可以从单词内部断开进行换行.2.break-all:此属性值能够实现强制将单词从内部截断

JS里引用CSS属性时候的命名

????如果JS代码中设置<p>元素的另一个CSS属性font-family.这个属性的获取方式与color属性略有不同,因为 font和family之间的连字符与JS中减法操作符相同,JS会把它解释为减号.如果你像下边这样访问名为 font-family 的属性,会收到一条出错信息: ????Element.style.font-family ????JS将减号前边的内容解释为"元素的style属性的font属性",把减号后的内容解释为一个名为family的变量,将整个表

前端学习 -- Css -- 属性选择器

属性选择器:根据元素的属性选择指定元素 语法:[属性名] 选取含有指定属性的元素 [属性名="属性值"]:选取属性值等于指定值的元素 [属性名^="属性值"]:选取属性值以指定内容开头的元素 [属性名$="属性值"]:选取属性值以指定内容结尾的元素 [属性名*="属性值"]:选取属性值中包含指定内容的元素 demo:(注:这么尴尬的诗句肯定不是我写的) <!DOCTYPE html> <html> &l

jQuery修改操作css属性实现方法

在jquery中我们要动态的修改css属性我们只要使用css()方法就可以实现了,下面我来给各位同学详细介绍介绍. css()方法在使用上具有多样性,我们先来了解css()方法基本知识. css() 方法设置或返回被选元素的一个或多个样式属性. 返回 CSS 属性如需返回指定的 CSS 属性的值,请使用如下语法: css("propertyname");下面的例子将返回首个匹配元素的 background-color 值: 实例 $("p").css("b

CSS属性之字体(Font)

CSS属性之字体(Font) font:[ [ <font-style> || <font-variant> || <font-weight> ]? <font-size> [ / <line-height> ]? <font-family> ]  复合属性,各属性见下文. 必须同时包含font-size和font-family,且这两个值顺序不能变,其他关键字的值放到这两个值的前面,顺序可以随意打乱. 可以在字号值(font-siz