利用CSS属性环绕布局Shapes绘制各个形状图形形状文本文字环绕特效
发布时间:2017-10-28, 16:31:55 分类:HTML | 编辑 off 网址 | 辅助
图集1/10
正文 5525字数 435,177阅读
奇妙的 CSS shapes(CSS图形)shape-outside url图片列表环绕iPhone X
ShapesOfCSS 绘制图形
CSS SHAPES 入门基础教程
<style>.box {
max-width: 414px;
height: 480px;
border: solid #000;
margin: auto;
overflow: auto;
}
.shape {
float: left;
width: 30px; height: 180px;
margin-top: 150px;
-webkit-shape-outside: url(https://out.img.pan.lizhenqiu.com/FldCdtDjo7aQ--E0kYPltIa3l0XO);
shape-outside: url(https://out.img.pan.lizhenqiu.com/FldCdtDjo7aQ--E0kYPltIa3l0XO);
transition: margin-top .15s;
}
.liuhai {
width: 24px; height: 180px;
background: url(https://out.img.pan.lizhenqiu.com/FnQOGc_MAscj1S9urYkhDi858qi7) no-repeat left center;
position: absolute;
margin-top: 150px;
}
.content ul {
list-style: none;
padding: 0;
margin: 0;
}
.content li {
border-bottom: 1px solid #eee;
padding: .5em;
}
</style>
<div id="box" class="box">
<i id="shape" class="shape"></i>
<i class="liuhai"></i>
<div class="content">
<ul>
<li>为了防止看花眼</li>
<li>我就手动敲下面的文字</li>
<li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li><li>我就手动敲下面的文字</li>
<li>理论上还可以使用CSS region实现</li>
<li>但没有这个方法容易理解</li>
</ul>
</div>
</div>
<script>
var eleShape = document.getElementById('shape');
var eleBox = document.getElementById('box');
var funShape = function () {
var scrollTop = eleBox.scrollTop;
// 滚动偏移应用在margin-top上
eleShape.style.marginTop = (150 + scrollTop) + 'px';
};
// 滚动时候实时改变shape形状
eleBox.addEventListener('scroll', funShape);
funShape();</script>
Run code
Cut to clipboard
环绕齐刘海滚动实现原理
CSS Shapes中有个CSS属性名为shape-outside,可以让内联元素以不规则的形状进行外部排列,其语法如下(参考自MDN):
/* 关键字值 */
shape-outside: none;
shape-outside: margin-box;
shape-outside: content-box;
shape-outside: border-box;
shape-outside: padding-box;
/* 函数值 */
shape-outside: circle();
shape-outside: ellipse();
shape-outside: inset(10px 10px 10px 10px);
shape-outside: polygon(10px 10px, 20px 20px, 30px 30px);
/* <url>值 */
shape-outside: url(image.png);
/* 渐变值 */
shape-outside: linear-gradient(45deg, rgba(255, 255, 255, 0) 150px, red 150px);
Run code
Cut to clipboard
shape-outside属性要想生效,本身需要是浮动float元素。
上面demo效果实现使用的是shape-outside:polygon(),通过点坐标勾勒出和齐刘海形状相似的多边形形状,CSS代码为:
.shape {
float: left;
shape-outside: polygon(0 0, 0 150px, 16px 154px, 30px 166px, 30px 314px, 16px 326px, 0 330px, 0 0);
}
Run code
Cut to clipboard
如下图紫色区域示意:
此时,后面没有设置BFC(块状格式化上下文)的列表元素就会自动环绕这个形状排列,也就是自动避开了齐刘海区域。
然后,只要搞个假的iPhone X的齐刘海图片覆盖在区域上就可以了。
至此,一个静态的列表环绕齐刘海的效果就完成了。
下面关键的问题是如何让滚动的时候,列表元素动态的跟着环绕呢?
由于shape-outside所在的元素是浮动元素,因此,必定会跟着容器一起滚动,我们需要的效果是我们所绘制的这个刘海区域需要是固定的,怎么办?此时,我是借助JavaScript处理的。
原理很简单,监听容器的滚动事件,让我们的shape-outside绘制的区域实时偏移滚动的大小。此时肉眼看上去的效果就是shape-outside区域永远固定在了滚动容器clientHeight的中间。
整个效果就这么实现了,相关JS如下:
box.addEventListener('scroll', function () {
var scrollTop = box.scrollTop;
// 滚动偏移应用在shape-outside上
shape.style.shapeOutside = 'polygon(0 0, 0 '+ (150 + scrollTop) +'px, 16px '+ (154 + scrollTop) +'px, 30px '+ (166 + scrollTop) +'px, 30px '+ (314 + scrollTop) +'px, 16px '+ (326 + scrollTop) +'px, 0 '+ (330 + scrollTop) +'px, 0 0)';
});
Run code
Cut to clipboard
CSS Shapes环绕iPhone X刘海的其它更简易方法
如果我们的技术选型是更看重简单易懂,而不是资源消耗与占用,还可以使用shape-outside:url(image.png)语法实现类似的效果,其中'image.png'就是用来被环绕的图片,环绕与否是基于计算alpha通道决定,用句简单的话描述,就是沿着图片非透明区域环绕。
由于使用url()的形状计算是基于图片元素,和inset(), circle(), ellipse()或者polygon()这些基础形状方法的计算性质不一样,因此,可以直接使用垂直方向的margin进行偏移。这要比polygon()这样实时计算坐标位置要好理解的多。
我们不妨看下CSS和JS代码,如下:
.shape {
float: left;
shape-outside: url(liu-outside.png);
margin-top: 150px;
}
box.addEventListener('scroll', function () {
var scrollTop = box.scrollTop;
// 滚动偏移应用在margin-top上
shape.style.marginTop = (150 + scrollTop) + 'px';
});
可以看到,当我们滚动容器的时候,改变的就一个marginTop值就好了;而上面的 shape-outside:polygon()实现需要同时改变多个坐标值。
Run code
Cut to clipboard
有个细节说明
这里有个细节需要说明下,那就是作为环绕区域的图片和前面显示的那个刘海图片不是一张图片,因为我们的刘海区域需要和后面的文字有一段的间隙,因此,url(liu-outside.png)中的这张'liu-outside.png'图片是有特别的实色填充处理的(扩展右侧环绕区域尺寸):
CSS Shapes的兼容性以及结束语
CSS Shapes的兼容性为Chrome浏览器和Safari浏览器(包括iOS)都是支持的,也就意味着我们是可以在iPhone上使用的,完美。只是需要注意的是在iOS10.2及其之前的版本,CSS Shapes的使用还是需要加webkit私有前缀的,但据说iPhone X至少默认iOS 11,而刘海头交互效果就是针对iPhone X处理的,因此webkit私有前缀不加也没关系。
其实就是CSS3 Shapes和CSS3 Regions这些与环绕布局相关的CSS属性。
(支付宝)给作者钱财以资鼓励 (微信)→
有过 2 条评论 »
就是兼容不好,只有webkit内核支持。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> ::-webkit-scrollbar{ width: 0; height: 0; } *{ margin: 0; padding: 0; } html,body{ width: 100%; height: 100%; background: #000; } body{ display: flex; justify-content: center; align-items: center; } ul{ list-style: none; } li{ padding: .5em; box-shadow: inset 0 0 10px #eee; } .wrap{ position: relative; margin: 0 auto; width: 800px; height: 500px; overflow: auto; background: #fff; box-shadow: 0 0 15px #fff; } .po{ position: absolute; left: 0; top: 155px; width: 30px; height: 190px; background: #000; border-radius: 0 100% 100% 0/0 100% 100% 0; } .fl{ float: left; width: 40px; shape-outside: polygon(0 150px, 20px 152px, 30px 155px, 40px 170px, 40px 330px, 30px 345px, 20px 348px, 0 350px); } </style> </head> <body> <div class="wrap"> <div class="po"></div> <div class="fl"></div> <div> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> </ul> </div> </div> <script type="text/javascript"> var wrap = document.querySelector('.wrap'); var ul = document.querySelector('.wrap ul'); var fl = document.querySelector('.wrap .fl'); var po = document.querySelector('.wrap .po'); fl.style.height = ul.scrollHeight + 'px'; wrap.onscroll = function(e){ var s = wrap.scrollTop; fl.style.shapeOutside = 'polygon(0 '+(150+s)+'px, 20px '+(152+s)+'px, 30px '+(155+s)+'px, 40px '+(170+s)+'px, 40px '+(330+s)+'px, 30px '+(345+s)+'px, 20px '+(348+s)+'px, 0 '+(350+s)+'px)'; po.style.top = 155 + s +'px'; } </script> </body> </html>