> 原文出处:http://www.w3cplus.com/css3/css-secrets/flexible-background-positioning.html
## 问题
我们经常遇见的一个问题就是,需要将背景图片定位到不同的位置,往往不仅是左上角,比如右下角。在 CSS 2.1 中,只能指定图片相对左上角的偏移量,或者使用关键字定位到其他的角落。不过,通常希望在背景图片和边角之间保留一些空间,避免类下图的效果。
![灵活的背景定位](https://box.kancloud.cn/2015-10-01_560cdba331325.png "灵活的背景定位")
*因为图像和边缘之间没有间隔,所以 `background-position: bottom right;` 实现的效果通常并不美观*
对于具有固定尺寸的容器,在 CSS 2.1 中是可以解决这个问题的,但是使用的技术比较杂乱:我们需要计算出背景图片相对左上角的偏移量,模拟出背景图片和左下角之间的间隙。但是,对于尺寸不固定的元素(由于内容不固定导致的尺寸不固定),这种方法就无能为力了。开发者通常喜欢使用百分比设置背景位置,通常是略小于 `100%` 的值,比如 `95%`。当然,使用最新的 CSS,我们对这个问题有更优雅的解决方法。
## 解决方案:增强型 `background-position`
在 [CSS Backgrounds & Borders Level 3](http://w3.org/TR/css3-background) 中,增强型的 `background-position` 属性已经开始支持针对每个边角的偏移量了,开发者需要做的就是在偏移量之前加上边角的信息。比如,如果我们想要背景图片距离右侧边界 `20px`,距离底部边界 `10px`,那么就可以这么做:
~~~
background: url(code-pirate.svg) no-repeat #58a;
background-position: right 20px bottom 10px;
~~~
完成后的效果如下图所示:
![灵活的背景定位](https://box.kancloud.cn/2015-10-01_560cdba3ab793.png "灵活的背景定位")
*不同的边上指定偏移量,如图所示的虚线轮廓*
最后需要做的就是设置降级措施。目前,大多数的浏览器尚不支持新式的 `background-position` 语法,虽然编写了上述代码,在某些浏览器背景图片仍然会被定位到左上角,看起来非常难看,并且遮挡住部分文字,如下图所示效果:
![灵活的背景定位](https://box.kancloud.cn/2015-10-01_560cdba3e9079.png "灵活的背景定位")
*如果我们不希望低版本浏览器的用户看到这样的效果,就需要提供一种降级处理*
一个简单降级方式是在 `background` 缩写属性中添加定位属性 **bottom right** :
~~~
background: url(code-pirate.svg)
no-repeat bottom right #58a;
background-position: right 20px bottom 10px;
~~~
## 解决方案:`background-origin`
通常,我们只需要给容器添加内边距就可以模拟背景图片和边界之间的偏移。如果使用上一节讲述的 `background-positon` 来做这件事,那么代码就会像这样:
~~~
padding: 10px;
background: url(code-pirate.svg) no-repeat #58a;
background-position: right 10px bottom 10px;
~~~
最终效果如下图所示:
![灵活的背景定位](https://box.kancloud.cn/2015-10-01_560cdba95bdb6.png "灵活的背景定位")
*为背景图添加的偏移量正好等于内边距的值*
如你所见,效果还不错,但是这些代码并不够简洁:如果需求发生变化需要修改代码,那么我们需要修改三处代码才能完成。所幸还有一种简单的方式可以代替它,无需重复声明内边距即可让背景图片根据内边距调整偏移量。
在你的web开发生涯中,你可能会写许多次类似 `background-position: top left;` 的属性。那么你是否思考过:哪里是左上角(`top-left corner`)?或许你已经知道了,每一个元素都有四个盒模型(如下图所示):外边距盒模型(margin box)、边框盒模型(border box)、内边距盒模型(padding box)以及内容盒模型(content box)。那么 background-position 中所指的 top left 指的是哪一个盒模型呢?
![灵活的背景定位](https://box.kancloud.cn/2015-10-01_560cdba99c518.png "灵活的背景定位")
实际上,`background-position`默认上值得就是内边距盒模型,所以边框最终遮盖了背景图片。因此,`top left` 默认上指的就是内边距盒模型的左上角。不过在 [Backgrounds & Borders Level 3](http://www.w3.org/TR/css3-background/) 中,我们拥有了新的属性用来改变这种默认行为:`background-origin`。你可能已经猜测到了 ,它的默认值也是 `padding-box`。如果我们就像下面的代码一样=将其更改为 `content-box`,那么 `background-pisition` 就会将`content-box` 视为背景图片的存在范围(实际上,这意味着所有的背景图片将会根据内边距实现和边框的偏移):
~~~
padding: 10px;
background: url("code-pirate.svg") no-repeat #58a
bottom right; /* or 100% 100% */
background-origin: content-box;
~~~
现在视觉效果和前面示例相同了,不过更胜一筹的是现在的代码更简洁。一定要牢记,如有需要你可以将上述的两种方法组合起来使用。如果你想使用不同的内边距,同时又想让其向外突出或向内嵌入,那么就可以组合使用 `background-origin: content-box` 和 `background-position`。
## 解决方案:`calc()`
让我们重新审视一下我们遇到的挑战:我们需要将背景图片定位到离右边框 `20px`、离底部边框 `10px`的位置。不过,使用这样解决方案,我们需要将其视为从左上角的偏移量,所以可以视为在水平方向偏移 `100% - 20px`、在垂直方向偏移 `100% - 10px`。所幸的是,`calc()` 函数就可以帮我们完成这种计算,并且可以完美地融入 `background-position` 属性:
~~~
background: url("code-pirate.svg") no-repeat;
background-position: calc(100% - 20px) calc(100% - 10px);
~~~
> 在 `calc()` 中,不要忘记在加减号操作符左右添加空格,否则就会发生解析错误!这种奇怪的规则是为了兼容性:因为在未来,`calc()` 中可能会允许使用关键字,而这些关键字中就有可能会包含连字符。
## 总结
在 CSS 2.1 中,只能指定图片相对左上角的偏移量,或者使用关键字定位到其他的角落。如果需要让背景图距离元素四边距有一定的偏移量,相对而言是较为麻烦。在这篇文章中,作者通过`background-position`新特性、修改`background-origin`以及`calc()`函数等三种方法,实现了一种灵活型的背景图定位。希望这篇文章对大家有所帮助。