第二十八讲:帖子子元素布局技巧(一)
多数音画帖不能只是一张背景图,得有其他诸如文本、粒子、媒体等内容的加入。子元素在帖子中如何布局是一门技巧,在这里我们不以美学的视角去审视这个问题,而是从前端技术角度出发讲讲怎样在帖子定位子元素。
之前的讲义有提到过父子元素的 position 属性在帖子布局子元素的作用,这里再次强调:父元素的相对定位 position: relative; 配套子元素的绝对定位 position: absolute 可以令子元素严格受到父元素的约束,如此设置之后,子元素的 left: 0; 和 top: 0; 或缺省 left/top 设置恰好能让子元素定位在父元素的左上角,top和left所设定的子元素的具体定位坐标 {0,0} 以父元素的左上角坐标 {0,0} 为参照,即二者重合。试看下面的演示代码和效果:
<style>
.papa {
width: 600px;
height: 200px;
border: 1px solid;
position: relative; /* 父元素位置属性 : 相对定位 */
}
.son {
position: absolute; /* 子元素位置属性 :绝对定位 */
padding: 10px;
width: 100px;
height: 50px;
background-color: #ffcc66;
}
</style>
<!-- HTML代码 :父元素要将子元素包裹其中 -->
<div class="papa">
<div class="son">子元素</div>
</div>
点击查看效果
演示效果中,灰色边框的大元素是父元素,其下有一个子元素,二者都是div标签。元素的父子关系是一种包含和被包含的关系,上例HTML代码中,class="papa" 的div标签包含有一个 class="son" 的div标签,papa 为父、son 为子;子元素没有设置 left 和 top 具体定位属性,使用了缺省值 {0,0}。一个父元素可以拥有一个或多个子标签,音画帖就是利用这一点在帖子容器即父元素中安排若干个子元素,这会涉及到子元素的具体定位问题,不处理这个问题,所有的子元素都会重叠在帖子容器的左上角或像文本流一样按自左而右、自上而下的次序布排各种元素,一切取决于position的设置。看以下例子,刚打开时子元素堆叠在父元素的左上角,点击其下按钮后它们会各就各位:
<style>
#papa1 {
width: 600px;
height: 200px;
border: 1px solid;
position: relative; /* 父元素位置属性 : 相对定位 */
}
.sons {
position: absolute; /* 子元素位置属性 :绝对定位 */
padding: 10px;
width: 100px;
height: 50px;
background-color: #ffcc66;
}
</style>
<div id="papa1">
<div class="sons">子元素一</div>
<div class="sons">子元素二</div>
<div class="sons">子元素三</div>
<div class="sons">子元素四</div>
</div>
<p><button id="btnSet" type="button" value="set">戳我调整子元素位置</button>
<script>
setPos = (elms) => {
let datas = [
{ x: 20, y: 20 },
{ x: 400, y: 30 },
{ x: 90, y: 100 },
{ x: 360, y: 120 },
];
elms.forEach((elm, idx) => {
elm.style.cssText += `
left: ${datas[idx].x}px;
top: ${datas[idx].y}px;
`;
});
};
var elements = papa1.querySelectorAll('.sons');
btnSet.onclick = () => setPos(elements);
</script>
点击查看效果
这个示例父子元素的position设定是 relative+absolute,在CSS代码中子元素没有使用left和top给子元素进行具体定位,它们按缺省left和top值即0定位自己,只有设置了每一个子元素的 left 和 top 属性值,或 right 和 bottom 属性值,它们才会各就各位,例中的JS代码就是做这个工作,不过只是演示,实际上,绝大多数情况下,我们应该在CSS代码中具体定位好所有子元素的具体位置。具体位置就是通过 left 或 right 定位横向位置、top 或 bottom 定位纵向位置,需要注意的是,特定元素若使用了 left 和 top 属性进行定位,则,如果将来会动态改变其位置,就一直得使用 left 和 top,不能改用 right 和 bottom;同时,一次性的定位不能四个属性都用,只能一横一纵配套使用,否则可能会出现意想不到的结果。以下例子可以拿去 pencil code 或存为本地文档进行测试以查看上述描述的不可预知效果:
<style>
.papa {
width: 600px;
height: 300px;
border: 1px solid;
position: relative;
}
.son {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* 以上四个属性等同于 inset: 0;
若设置了 width 和 height属性,则
后两个即right和bottom设置自动失效
width: 100px;
height: 50px;
*/
background-color: #ffcc66;
padding: 10px;
}
</style>
<div class="papa">
<div class="son">子元素</div>
</div>
当子元素不太多,建议每一个子元素建立自己的ID选择器或class选择器;若较多,可以考虑归类管理,即,同一个类属标签的子元素建立统一的class选择器,然后再使用伪类选择器来具体定义每一个子元素的其他如left、top等具体属性。举例说明如下:
<style>
#mama {
width: 600px;
height: 300px;
border: 1px solid;
position: relative;
}
#mama > img { position: absolute; width: 160px; }
.girl {
position: absolute;
width: 100px;
height: 50px;
background-color: #445566;
color: yellow;
padding: 10px;
opacity: .75;
}
.girl:nth-of-type(1) { left: 130px; top: 20px; }
.girl:nth-of-type(2) { right: 20px; top: 20px; }
.girl:nth-of-type(3) { left: calc(50% - 50px); bottom: 20px; }
</style>
<div id="mama">
<div class="girl">子元素1</div>
<img src="https://638183.freep.cn/638183/2-0.gif" alt="" />
<div class="girl">子元素2</div>
<div class="girl">子元素3</div>
</div>
点击查看效果
仔细阅读上述代码并特别注意,mama下的所有子元素,包括img和三个div元素,都用了 position: absolute; 定位类型设置(position),这样除了能够更好地用方向属性具体定位,还存在一个层级问题。请观察演示效果图片和子元素的位置关系,它们部分重叠,图片在上层,子元素1在下层,这是为什么?原因是,两个或更多元素有位置重叠时,依据HTML代码流中谁先出现谁后出现,先出现的在下层、后出现的在上层。如果想让先出现的元素不被覆盖,可以给该元素的CSS选择器加入 z-index 属性并赋予一个合适的值,它就会在较为顶层的层级,例如可以这样:z-index: 9; 。此外,注意一下代码中的伪类选择器 *:nth-of-type(n) ,它可以用来具体设定特定类属的选择器(如例中的 .girl 第n个元素的其他具体属性,上例就是这样设置了 class="girl" 三个div子元素的具体位置。
如果子元素更多,例如做粒子特效是会用到几十甚至上百个子元素,这种情况一个一个粒子去写CSS样式就不现实了,这得用CSS+JS的方式动态生成粒子:先制定通用的粒子CSS样式,再在JS代码中动态创建元素并给出各异的样式,这个前面的讲义有提及到,这里再给一个动态生成粒子的示例,该粒子代码来源于 《帖子在线制作》 程序。
<style>
#papa2 {
margin: auto;
width: 600px;
height: 300px;
background: linear-gradient(skyblue, black);
overflow: hidden;
position: relative;
}
li-zi {
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
background: snow;
}
/* 关键帧动画使用 --x0 和 --y0 两个变量存储translate平移的{x,y}坐标 */
@keyframes moving {
from { opacity: 0; transform: translate(0,0); }
to { opacity: 1; transform: translate(var(--x0),var(--y0)); }
}
</style>
<div id="papa2"></div>
<script>
//用for循环语句创建80个粒子
for(let i = 0, all = 80; i < all; i++) {
let lz = document.createElement('li-zi'); //创建粒子 li-zi 为自定义元素
let hudu = Math.PI / 180 * 360 / all * i; //计算弧度
let xx = 600 * Math.cos(hudu), yy = 300 * Math.sin(hudu); //计算平移目的地xy坐标值
//粒子其他各异的CSS属性
lz.style.cssText += `
--x0: ${xx}px; /* 平移目的地X坐标 */
--y0: ${yy}px; /* 平移目的地Y坐标 */
left: calc(50% - 10px); /* 粒子元素初始位置 :水平居中 */
top: calc(50% - 10px); /* 粒子元素初始位置 :垂直居中 */
/* 运行动画 :动画时长随机、延时运行加负号随机提前 */
animation: moving ${Math.random() * 20 + 20}s -${Math.random() * 20}s infinite;
`;
papa2.prepend(lz); //父元素追加粒子子元素
}
</script>
点击查看效果
本讲是帖子子元素布局的第一讲,讲义中的示例代码量偏多,需要细细领会、尝试。本讲的作业 :参照最后的示例做一个帖子,帖子背景和音频以及音频控制元素(img或div都行)需要额外加入,并在JS中实现能随音频的播放、暂停进行联动控制粒子的运动。
返回目录